Skip to content

Commit 337e09c

Browse files
committed
Refactor video rendering code
1 parent 59751fc commit 337e09c

File tree

4 files changed

+89
-72
lines changed

4 files changed

+89
-72
lines changed

src/data/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
pub mod cli;
22
pub mod song;
33
pub mod track;
4+
pub mod video;

src/data/song.rs

Lines changed: 4 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
1-
use super::track::load_track_into_memory;
1+
use super::{track::load_track_into_memory, video::Encoding};
22
use crate::{
33
display::{draw, RGB},
4-
SCREEN_FRAME_RATE, SCREEN_HEIGHT, SCREEN_SCALE, SCREEN_WIDTH,
4+
SCREEN_FRAME_RATE, SCREEN_HEIGHT, SCREEN_WIDTH,
55
};
6-
use fast_image_resize as fr;
76
use image::RgbImage;
8-
use ndarray::Array3;
97
use rayon::prelude::*;
108
use serde::Deserialize;
11-
use std::{collections::VecDeque, fs::File, io::BufReader, num::NonZeroU32};
9+
use std::{collections::VecDeque, fs::File, io::BufReader};
1210
use symphonia::core::{
1311
audio::{AudioBufferRef, Signal},
1412
codecs::Decoder,
1513
errors::Error,
1614
formats::{FormatReader, Track},
1715
};
18-
use video_rs::{Encoder, Time};
1916

2017
#[derive(Deserialize)]
2118
pub struct Channel {
@@ -162,14 +159,6 @@ impl Window {
162159
}
163160
}
164161

165-
pub struct Encoding {
166-
pub encoder: Encoder,
167-
pub position: Time,
168-
pub resizer: fr::Resizer,
169-
pub size_src: (NonZeroU32, NonZeroU32),
170-
pub size_dst: (NonZeroU32, NonZeroU32),
171-
}
172-
173162
#[derive(Deserialize)]
174163
pub struct Song {
175164
pub channels: Vec<Channel>,
@@ -320,41 +309,7 @@ impl Song {
320309
}
321310

322311
// Render frame to video
323-
324-
let width = *SCREEN_WIDTH * *SCREEN_SCALE;
325-
let height = *SCREEN_HEIGHT * *SCREEN_SCALE;
326-
327-
let rs: Vec<u8> = if *SCREEN_SCALE == 1 {
328-
frame.as_raw().to_vec()
329-
} else {
330-
let src_image = fr::Image::from_slice_u8(
331-
encoding.size_src.0,
332-
encoding.size_src.1,
333-
frame,
334-
fr::PixelType::U8x3,
335-
)
336-
.unwrap();
337-
338-
let mut dst_image = fr::Image::new(
339-
encoding.size_dst.0,
340-
encoding.size_dst.1,
341-
fr::PixelType::U8x3,
342-
);
343-
344-
// Get mutable view of destination image data
345-
let mut dst_view = dst_image.view_mut();
346-
encoding
347-
.resizer
348-
.resize(&src_image.view(), &mut dst_view)
349-
.unwrap();
350-
351-
dst_image.buffer().to_vec()
352-
};
353-
354-
let ef: Array3<u8> =
355-
ndarray::Array3::from_shape_vec((height as usize, width as usize, 3), rs).unwrap();
356-
357-
encoding.encoder.encode(&ef, &encoding.position).unwrap();
312+
encoding.render_frame(frame);
358313

359314
self.frame += 1;
360315
if self.frame % 100 == 0 {

src/data/video.rs

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
use crate::{SCREEN_FRAME_RATE, SCREEN_HEIGHT, SCREEN_SCALE, SCREEN_WIDTH};
2+
use fast_image_resize as fr;
3+
use image::RgbImage;
4+
use ndarray::Array3;
5+
use std::num::NonZeroU32;
6+
use video_rs::{Encoder, Time};
7+
8+
pub struct Encoding {
9+
pub encoder: Encoder,
10+
pub position: Time,
11+
pub frame_duration: Time,
12+
pub resizer: fr::Resizer,
13+
pub size_src: (NonZeroU32, NonZeroU32),
14+
pub size_dst: (NonZeroU32, NonZeroU32),
15+
}
16+
17+
impl Encoding {
18+
pub fn new(encoder: Encoder) -> Self {
19+
Encoding {
20+
encoder,
21+
position: Time::zero(),
22+
frame_duration: Time::from_nth_of_a_second(*SCREEN_FRAME_RATE),
23+
size_src: (
24+
NonZeroU32::new(*SCREEN_WIDTH).unwrap(),
25+
NonZeroU32::new(*SCREEN_HEIGHT).unwrap(),
26+
),
27+
size_dst: (
28+
NonZeroU32::new(*SCREEN_WIDTH * *SCREEN_SCALE).unwrap(),
29+
NonZeroU32::new(*SCREEN_HEIGHT * *SCREEN_SCALE).unwrap(),
30+
),
31+
resizer: fr::Resizer::new(fr::ResizeAlg::Nearest),
32+
}
33+
}
34+
35+
pub fn resize_frame(&mut self, frame: &mut RgbImage) -> Vec<u8> {
36+
if *SCREEN_SCALE == 1 {
37+
return frame.as_raw().to_vec();
38+
}
39+
40+
let src_image =
41+
fr::Image::from_slice_u8(self.size_src.0, self.size_src.1, frame, fr::PixelType::U8x3)
42+
.unwrap();
43+
44+
let mut dst_image = fr::Image::new(self.size_dst.0, self.size_dst.1, fr::PixelType::U8x3);
45+
46+
// Get mutable view of destination image data
47+
let mut dst_view = dst_image.view_mut();
48+
self.resizer
49+
.resize(&src_image.view(), &mut dst_view)
50+
.unwrap();
51+
52+
dst_image.buffer().to_vec()
53+
}
54+
55+
pub fn render_frame(&mut self, frame: &mut RgbImage) {
56+
let pixels = self.resize_frame(frame);
57+
let ef: Array3<u8> = ndarray::Array3::from_shape_vec(
58+
(
59+
self.size_dst.1.get() as usize,
60+
self.size_dst.0.get() as usize,
61+
3,
62+
),
63+
pixels,
64+
)
65+
.unwrap();
66+
67+
self.encoder.encode(&ef, &self.position).unwrap();
68+
self.update_position();
69+
}
70+
71+
pub fn update_position(&mut self) {
72+
self.position = self.position.aligned_with(&self.frame_duration).add();
73+
}
74+
75+
pub fn flush(&mut self) {
76+
self.encoder.finish().unwrap();
77+
}
78+
}

src/main.rs

Lines changed: 6 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
1-
use crate::data::{cli::Args, song::Encoding};
1+
use crate::data::{cli::Args, video::Encoding};
22
use clap::Parser;
33
use data::song::{Song, Window};
4-
use fast_image_resize as fr;
54
use image::RgbImage;
65
use lazy_static::lazy_static;
7-
use std::{num::NonZeroU32, path::PathBuf};
8-
use video_rs::{Encoder, EncoderSettings, Locator, Time};
6+
use std::path::PathBuf;
7+
use video_rs::{Encoder, EncoderSettings, Locator};
98

109
mod data;
1110
mod display;
@@ -32,30 +31,16 @@ fn main() {
3231

3332
let destination: Locator = PathBuf::from(&song.video_file_out).into();
3433
let settings = EncoderSettings::for_h264_yuv420p(width, height, false);
35-
let duration: Time = Time::from_nth_of_a_second(*SCREEN_FRAME_RATE);
36-
3734
let mut frame = RgbImage::new(*SCREEN_WIDTH, *SCREEN_HEIGHT);
35+
let mut encoding =
36+
Encoding::new(Encoder::new(&destination, settings).expect("failed to create encoder"));
3837

3938
println!(
4039
"\nGenerated frame of size {}x{}",
4140
frame.width(),
4241
frame.height()
4342
);
4443

45-
let mut encoding = Encoding {
46-
encoder: Encoder::new(&destination, settings).expect("failed to create encoder"),
47-
position: Time::zero(),
48-
size_src: (
49-
NonZeroU32::new(*SCREEN_WIDTH).unwrap(),
50-
NonZeroU32::new(*SCREEN_HEIGHT).unwrap(),
51-
),
52-
size_dst: (
53-
NonZeroU32::new(width as u32).unwrap(),
54-
NonZeroU32::new(height as u32).unwrap(),
55-
),
56-
resizer: fr::Resizer::new(fr::ResizeAlg::Nearest),
57-
};
58-
5944
// Step 2: Render waveforms
6045
loop {
6146
let result = song.draw(&mut frame, &mut encoding);
@@ -64,10 +49,8 @@ fn main() {
6449
println!("{:?}", result.err().unwrap());
6550
break;
6651
}
67-
68-
encoding.position = encoding.position.aligned_with(&duration).add();
6952
}
7053

7154
// Step 3: Flush MP4 to file
72-
encoding.encoder.finish().unwrap();
55+
encoding.flush();
7356
}

0 commit comments

Comments
 (0)