1pub mod av1;
7pub mod mkv;
8
9#[derive(Clone, Copy, Debug)]
13pub struct Clip {
14 pub width: u32,
15 pub height: u32,
16 pub frames: u32,
17 pub fps: u32,
18 pub audio_rate: Option<u32>,
19}
20
21fn le16(v: &mut Vec<u8>, x: u16) {
22 v.extend_from_slice(&x.to_le_bytes());
23}
24
25fn le32(v: &mut Vec<u8>, x: u32) {
26 v.extend_from_slice(&x.to_le_bytes());
27}
28
29fn chunk(id: &[u8; 4], data: &[u8]) -> Vec<u8> {
30 let mut v = id.to_vec();
31 le32(&mut v, data.len() as u32);
32 v.extend_from_slice(data);
33 if data.len() % 2 == 1 {
34 v.push(0);
35 }
36 v
37}
38
39fn list(kind: &[u8; 4], content: &[u8]) -> Vec<u8> {
40 let mut data = kind.to_vec();
41 data.extend_from_slice(content);
42 chunk(b"LIST", &data)
43}
44
45impl Clip {
46 pub fn avi(&self, pixel: impl Fn(u32, u32) -> [u8; 3]) -> Vec<u8> {
49 let Clip { width: w, height: h, frames, fps, audio_rate } = *self;
50 let frame_bytes = w * h * 3;
51 let streams = if audio_rate.is_some() { 2 } else { 1 };
52
53 let mut avih = Vec::new();
54 for x in [1_000_000 / fps, 0, 0, 0x10, frames, 0, streams, frame_bytes, w, h, 0, 0, 0, 0] {
55 le32(&mut avih, x);
56 }
57 let mut strh = b"vids".to_vec();
58 strh.extend_from_slice(&[0; 4]);
59 for x in [0u32, 0, 0, 1, fps, 0, frames, frame_bytes, u32::MAX, 0] {
60 le32(&mut strh, x);
61 }
62 for x in [0u16, 0, w as u16, h as u16] {
63 le16(&mut strh, x);
64 }
65 let mut strf = Vec::new();
66 for x in [40, w, h] {
67 le32(&mut strf, x);
68 }
69 le16(&mut strf, 1);
70 le16(&mut strf, 24);
71 for x in [0, frame_bytes, 0, 0, 0, 0] {
72 le32(&mut strf, x);
73 }
74 let mut hdrl = chunk(b"avih", &avih);
75 hdrl.extend(list(b"strl", &[chunk(b"strh", &strh), chunk(b"strf", &strf)].concat()));
76 if let Some(rate) = audio_rate {
77 let per_frame = rate / fps;
78 let mut ah = b"auds".to_vec();
79 ah.extend_from_slice(&[0; 4]);
80 for x in [0u32, 0, 0, 4, rate * 4, 0, per_frame * frames, per_frame * 4, u32::MAX, 4] {
81 le32(&mut ah, x);
82 }
83 ah.extend_from_slice(&[0; 8]);
84 let mut af = Vec::new();
85 le16(&mut af, 1);
86 le16(&mut af, 2);
87 le32(&mut af, rate);
88 le32(&mut af, rate * 4);
89 le16(&mut af, 4);
90 le16(&mut af, 16);
91 le16(&mut af, 0);
92 hdrl.extend(list(b"strl", &[chunk(b"strh", &ah), chunk(b"strf", &af)].concat()));
93 }
94
95 let mut movi = Vec::new();
96 let mut idx = Vec::new();
97 let mut add = |movi: &mut Vec<u8>, id: &[u8; 4], data: &[u8]| {
98 idx.extend_from_slice(id);
99 le32(&mut idx, 0x10);
100 le32(&mut idx, movi.len() as u32 + 4);
101 le32(&mut idx, data.len() as u32);
102 movi.extend(chunk(id, data));
103 };
104 for i in 0..frames {
105 let mut px = Vec::with_capacity(frame_bytes as usize);
107 for row in (0..h).rev() {
108 let [r, g, b] = pixel(i, row);
109 for _ in 0..w {
110 px.extend_from_slice(&[b, g, r]);
111 }
112 }
113 add(&mut movi, b"00db", &px);
114 if let Some(rate) = audio_rate {
115 let per_frame = rate / fps;
116 let mut pcm = Vec::new();
117 for n in 0..per_frame {
118 let t = f64::from(i * per_frame + n) / f64::from(rate);
119 let s = ((t * 440.0 * std::f64::consts::TAU).sin() * 8000.0) as i16;
120 pcm.extend_from_slice(&s.to_le_bytes());
121 pcm.extend_from_slice(&s.to_le_bytes());
122 }
123 add(&mut movi, b"01wb", &pcm);
124 }
125 }
126 let mut body = b"AVI ".to_vec();
127 body.extend(list(b"hdrl", &hdrl));
128 body.extend(list(b"movi", &movi));
129 body.extend(chunk(b"idx1", &idx));
130 chunk(b"RIFF", &body)
131 }
132}
133
134#[cfg(test)]
135mod tests {
136 use super::*;
137
138 #[test]
139 fn avi_files_are_riff_with_an_index() {
140 let clip = Clip { width: 4, height: 2, frames: 3, fps: 10, audio_rate: Some(1000) };
141 let bytes = clip.avi(|_, _| [1, 2, 3]);
142 assert_eq!(&bytes[0..4], b"RIFF");
143 assert_eq!(u32::from_le_bytes(bytes[4..8].try_into().unwrap()) as usize, bytes.len() - 8);
144 assert_eq!(&bytes[8..12], b"AVI ");
145 assert!(bytes.windows(4).any(|w| w == b"idx1"));
146 assert_eq!(bytes.windows(4).filter(|w| *w == b"00db").count(), 3 + 3);
148 assert!(bytes.windows(3).any(|w| w == [3, 2, 1]), "pixels are stored BGR");
149 }
150}