]>
Commit | Line | Data |
---|---|---|
5cd2f537 KS |
1 | use nihav_core::muxers::*; |
2 | use nihav_registry::register::*; | |
3 | ||
e90482b6 KS |
4 | const MAX_RIFF_BLOCKS: usize = 16; // ~16GB file should be enough |
5 | const MAX_RIFF_SIZE: u64 = 900 << 20; // 900MB of payload plus index data will hopefully fit in 1GB chunk limit | |
6 | ||
5cd2f537 KS |
7 | #[derive(Clone,Copy)] |
8 | struct IdxEntry { | |
34bd4557 | 9 | tag: [u8; 4], |
5cd2f537 | 10 | key: bool, |
34bd4557 | 11 | pos: u64, |
5cd2f537 KS |
12 | len: u32, |
13 | } | |
14 | ||
15 | #[derive(Clone,Copy)] | |
16 | struct AVIStream { | |
17 | strh_pos: u64, | |
e90482b6 | 18 | indx_pos: u64, |
5cd2f537 | 19 | nframes: u32, |
e90482b6 | 20 | lframes: u32, |
5cd2f537 | 21 | is_video: bool, |
c8db9313 | 22 | max_size: u32, |
04fafc56 | 23 | pal_change: bool, |
5cd2f537 KS |
24 | } |
25 | ||
26 | struct AVIMuxer<'a> { | |
27 | bw: &'a mut ByteWriter<'a>, | |
28 | index: Vec<IdxEntry>, | |
29 | video_str: Option<usize>, | |
30 | video_id: u32, | |
31 | data_pos: u64, | |
32 | stream_info: Vec<AVIStream>, | |
a1613eee | 33 | pal_pos: Vec<u32>, |
e90482b6 KS |
34 | odml_pos: u64, |
35 | riff_blk: usize, | |
36 | blk_start: u64, | |
37 | wrote_hdr: bool, | |
5cd2f537 KS |
38 | } |
39 | ||
40 | impl<'a> AVIMuxer<'a> { | |
41 | fn new(bw: &'a mut ByteWriter<'a>) -> Self { | |
42 | Self { | |
43 | bw, | |
44 | index: Vec::new(), | |
45 | video_str: None, | |
46 | video_id: 0, | |
47 | data_pos: 0, | |
48 | stream_info: Vec::with_capacity(2), | |
a1613eee | 49 | pal_pos: Vec::with_capacity(2), |
e90482b6 KS |
50 | odml_pos: 0, |
51 | riff_blk: 0, | |
52 | blk_start: 0, | |
53 | wrote_hdr: true, | |
5cd2f537 KS |
54 | } |
55 | } | |
e90482b6 KS |
56 | fn patch_stream_stats(&mut self) -> MuxerResult<()> { |
57 | let mut max_frames = 0; | |
58 | let mut max_size = 0; | |
59 | for sstat in self.stream_info.iter() { | |
60 | max_frames = max_frames.max(sstat.nframes); | |
61 | max_size = max_size.max(sstat.max_size); | |
62 | if sstat.pal_change { | |
63 | self.bw.seek(SeekFrom::Start(sstat.strh_pos))?; | |
64 | self.bw.write_u32le(0x00010000)?; | |
65 | } | |
66 | self.bw.seek(SeekFrom::Start(sstat.strh_pos + 0x18))?; | |
67 | self.bw.write_u32le(if sstat.is_video { sstat.nframes } else { 0 })?; | |
68 | self.bw.write_u32le(sstat.max_size)?; | |
69 | } | |
70 | self.bw.seek(SeekFrom::Start(0x30))?; | |
71 | self.bw.write_u32le(max_frames)?; | |
72 | self.bw.seek(SeekFrom::Current(8))?; | |
73 | self.bw.write_u32le(max_size)?; | |
74 | Ok(()) | |
75 | } | |
76 | fn flush_riff_block(&mut self) -> MuxerResult<()> { | |
77 | for (sidx, stream) in self.stream_info.iter_mut().enumerate() { | |
78 | let idx_pos = self.bw.tell(); | |
79 | ||
80 | let tag0 = b'0' + ((sidx / 10) as u8); | |
81 | let tag1 = b'0' + ((sidx % 10) as u8); | |
82 | ||
6db909c5 | 83 | let nentries = self.index.iter().filter(|idx| idx.tag[0] == tag0 && idx.tag[1] == tag1 && &idx.tag[2..] != b"pc").count(); |
e90482b6 KS |
84 | |
85 | let tag = [b'i', b'x', tag0, tag1]; | |
86 | self.bw.write_buf(&tag)?; | |
87 | self.bw.write_u32le((6 * 4 + nentries * 8) as u32)?; | |
88 | self.bw.write_u16le(2)?; | |
89 | self.bw.write_byte(0)?; | |
90 | self.bw.write_byte(1)?; // AVI_INDEX_OF_CHUNKS | |
91 | let mut chunk_id = [tag0, tag1, 0, 0]; | |
92 | if stream.is_video { | |
93 | chunk_id[2] = b'd'; | |
94 | chunk_id[3] = b'c'; | |
95 | } else { | |
96 | chunk_id[2] = b'w'; | |
97 | chunk_id[3] = b'b'; | |
98 | } | |
99 | self.bw.write_u32le(nentries as u32)?; | |
100 | self.bw.write_buf(&chunk_id)?; | |
101 | self.bw.write_u64le(self.blk_start)?; | |
102 | self.bw.write_u32le(0)?; | |
103 | ||
6db909c5 | 104 | for entry in self.index.iter().filter(|idx| idx.tag[0] == tag0 && idx.tag[1] == tag1 && &idx.tag[2..] != b"pc") { |
e90482b6 KS |
105 | self.bw.write_u32le((entry.pos - self.blk_start + 8) as u32)?; |
106 | self.bw.write_u32le(entry.len)?; | |
107 | } | |
108 | ||
109 | let idx_end = self.bw.tell(); | |
110 | ||
111 | self.bw.seek(SeekFrom::Start(stream.indx_pos + 8 * 4 + (self.riff_blk as u64) * 16))?; | |
112 | self.bw.write_u64le(idx_pos)?; | |
113 | self.bw.write_u32le((idx_end - idx_pos) as u32)?; | |
114 | self.bw.write_u32le(stream.lframes)?; | |
115 | stream.lframes = 0; | |
116 | ||
117 | self.bw.seek(SeekFrom::Start(idx_end))?; | |
118 | } | |
119 | ||
120 | patch_size(self.bw, self.blk_start + 8)?; | |
121 | patch_size(self.bw, self.data_pos)?; | |
122 | ||
123 | self.index.clear(); | |
124 | ||
125 | self.riff_blk += 1; | |
126 | self.blk_start = self.bw.tell(); | |
127 | self.wrote_hdr = false; | |
128 | ||
129 | // keep updating actual stream statistics and flags | |
130 | self.patch_stream_stats()?; | |
131 | ||
132 | Ok(()) | |
133 | } | |
5cd2f537 KS |
134 | } |
135 | ||
136 | fn patch_size(bw: &mut ByteWriter, pos: u64) -> MuxerResult<()> { | |
137 | let size = bw.tell() - pos; | |
138 | bw.seek(SeekFrom::Current(-((size + 4) as i64)))?; | |
139 | bw.write_u32le(size as u32)?; | |
140 | bw.seek(SeekFrom::End(0))?; | |
141 | Ok(()) | |
142 | } | |
143 | ||
34bd4557 KS |
144 | fn gen_chunk_tag(stype: StreamType, str_no: u32) -> [u8; 4] { |
145 | let c0 = b'0' + ((str_no / 10) as u8); | |
146 | let c1 = b'0' + ((str_no % 10) as u8); | |
5cd2f537 | 147 | match stype { |
34bd4557 KS |
148 | StreamType::Video => [c0, c1, b'd', b'c'], |
149 | StreamType::Audio => [c0, c1, b'w', b'b'], | |
150 | StreamType::Subtitles => [c0, c1, b't', b'x'], | |
151 | _ => unimplemented!() | |
152 | } | |
5cd2f537 KS |
153 | } |
154 | ||
155 | impl<'a> MuxCore<'a> for AVIMuxer<'a> { | |
156 | fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> { | |
157 | if strmgr.get_num_streams() == 0 { | |
158 | return Err(MuxerError::InvalidArgument); | |
159 | } | |
160 | if strmgr.get_num_streams() > 99 { | |
161 | return Err(MuxerError::UnsupportedFormat); | |
162 | } | |
405cec9e KS |
163 | for (str_no, strm) in strmgr.iter().enumerate() { |
164 | if strm.get_media_type() == StreamType::Video { | |
5cd2f537 | 165 | self.video_str = Some(str_no); |
405cec9e | 166 | self.video_id = strm.id; |
5cd2f537 KS |
167 | break; |
168 | } | |
169 | } | |
170 | let (vinfo, tb_num, tb_den) = if let Some(str_id) = self.video_str { | |
171 | let vstr = strmgr.get_stream(str_id).unwrap(); | |
172 | (vstr.get_info(), vstr.tb_num, vstr.tb_den) | |
173 | } else { | |
174 | (NACodecInfo::new_dummy(), 0, 1) | |
175 | }; | |
176 | let hdrl_pos = self.bw.tell() + 20; | |
177 | self.bw.write_buf(b"RIFF\0\0\0\0AVI LIST\0\0\0\0hdrlavih")?; | |
178 | self.bw.write_u32le(56)?; // avih size | |
179 | let ms_per_frame = NATimeInfo::ts_to_time(1, 1000000, tb_num, tb_den); | |
180 | self.bw.write_u32le(ms_per_frame as u32)?; | |
181 | self.bw.write_u32le(0)?; // max transfer rate | |
182 | self.bw.write_u32le(0)?; // padding granularity | |
183 | self.bw.write_u32le(0)?; // flags | |
184 | self.bw.write_u32le(0)?; // total frames | |
185 | self.bw.write_u32le(0)?; // initial frames | |
186 | self.bw.write_u32le(strmgr.get_num_streams() as u32)?; | |
187 | self.bw.write_u32le(0)?; // suggested buffer size | |
188 | if let NACodecTypeInfo::Video(ref vinfo) = vinfo.get_properties() { | |
189 | self.bw.write_u32le(vinfo.width as u32)?; | |
190 | self.bw.write_u32le(vinfo.height as u32)?; | |
191 | } else { | |
192 | self.bw.write_u32le(0)?; | |
193 | self.bw.write_u32le(0)?; | |
194 | } | |
195 | self.bw.write_u32le(0)?; // reserved | |
196 | self.bw.write_u32le(0)?; // reserved | |
197 | self.bw.write_u32le(0)?; // reserved | |
198 | self.bw.write_u32le(0)?; // reserved | |
199 | ||
37952415 | 200 | self.pal_pos.clear(); |
a1613eee | 201 | self.pal_pos.resize(strmgr.get_num_streams(), 0); |
405cec9e | 202 | for (strno, strm) in strmgr.iter().enumerate() { |
5cd2f537 KS |
203 | let strl_pos = self.bw.tell() + 8; |
204 | self.bw.write_buf(b"LIST\0\0\0\0strlstrh")?; | |
205 | self.bw.write_u32le(56)?; // strh size | |
206 | ||
405cec9e | 207 | match strm.get_media_type() { |
5cd2f537 KS |
208 | StreamType::Video => { |
209 | self.bw.write_buf(b"vids")?; | |
f22b2973 KS |
210 | let fcc = if strm.get_info().get_name() == "rawvideo-ms" { |
211 | Some(*b"DIB ") | |
212 | } else { find_avi_fourcc(strm.get_info().get_name()) }; | |
5cd2f537 KS |
213 | if fcc.is_none() { |
214 | return Err(MuxerError::UnsupportedFormat); | |
215 | } | |
216 | self.bw.write_buf(&fcc.unwrap_or([0; 4]))?; | |
405cec9e | 217 | let vinfo = strm.get_info().get_properties().get_video_info().unwrap(); |
5cd2f537 KS |
218 | if vinfo.width >= (1 << 16) || vinfo.height >= (1 << 16) { |
219 | return Err(MuxerError::UnsupportedFormat); | |
220 | } | |
221 | }, | |
222 | StreamType::Audio => { | |
223 | self.bw.write_buf(b"auds")?; | |
224 | self.bw.write_u32le(0)?; | |
225 | }, | |
226 | StreamType::Subtitles => { | |
227 | self.bw.write_buf(b"txts")?; | |
228 | self.bw.write_u32le(0)?; | |
229 | }, | |
230 | _ => return Err(MuxerError::UnsupportedFormat), | |
231 | }; | |
232 | self.stream_info.push(AVIStream { | |
233 | strh_pos: self.bw.tell(), | |
e90482b6 | 234 | indx_pos: 0, |
405cec9e | 235 | is_video: strm.get_media_type() == StreamType::Video, |
5cd2f537 | 236 | nframes: 0, |
e90482b6 | 237 | lframes: 0, |
5cd2f537 | 238 | max_size: 0, |
04fafc56 | 239 | pal_change: false, |
5cd2f537 KS |
240 | }); |
241 | ||
242 | self.bw.write_u32le(0)?; // flags | |
243 | self.bw.write_u16le(0)?; // priority | |
244 | self.bw.write_u16le(0)?; // language | |
245 | self.bw.write_u32le(0)?; // initial frames | |
0ed22721 KS |
246 | if strm.get_info().get_name() != "pcm" { |
247 | self.bw.write_u32le(strm.tb_num)?; | |
248 | self.bw.write_u32le(strm.tb_den)?; | |
249 | } else { | |
250 | let ainfo = strm.get_info().get_properties().get_audio_info().unwrap(); | |
251 | self.bw.write_u32le(1)?; | |
252 | self.bw.write_u32le(ainfo.sample_rate)?; | |
253 | } | |
5cd2f537 KS |
254 | self.bw.write_u32le(0)?; // start |
255 | self.bw.write_u32le(0)?; // length | |
256 | self.bw.write_u32le(0)?; // suggested buffer size | |
257 | self.bw.write_u32le(0)?; // quality | |
258 | self.bw.write_u32le(0)?; // sample_size | |
259 | self.bw.write_u16le(0)?; // x | |
260 | self.bw.write_u16le(0)?; // y | |
261 | self.bw.write_u16le(0)?; // w | |
262 | self.bw.write_u16le(0)?; // h | |
263 | ||
264 | self.bw.write_buf(b"strf")?; | |
265 | self.bw.write_u32le(0)?; | |
266 | let strf_pos = self.bw.tell(); | |
405cec9e | 267 | match strm.get_media_type() { |
5cd2f537 | 268 | StreamType::Video => { |
405cec9e | 269 | let vinfo = strm.get_info().get_properties().get_video_info().unwrap(); |
5cd2f537 KS |
270 | let hdr_pos = self.bw.tell(); |
271 | self.bw.write_u32le(0)?; | |
272 | self.bw.write_u32le(vinfo.width as u32)?; | |
e5b5248d | 273 | self.bw.write_u32le(vinfo.height as u32)?; |
c905da8d | 274 | if !vinfo.format.palette { |
fff11a37 KS |
275 | if strm.get_info().get_name() != "rawvideo-ms" { |
276 | self.bw.write_u16le(vinfo.format.components as u16)?; | |
277 | } else { | |
278 | self.bw.write_u16le(1)?; | |
279 | } | |
c905da8d KS |
280 | self.bw.write_u16le(vinfo.format.get_total_depth() as u16)?; |
281 | } else { | |
282 | self.bw.write_u16le(1)?; | |
283 | self.bw.write_u16le(8)?; | |
284 | } | |
f22b2973 KS |
285 | let fcc = if strm.get_info().get_name() == "rawvideo-ms" { |
286 | Some([0; 4]) | |
287 | } else { find_avi_fourcc(strm.get_info().get_name()) }; | |
5cd2f537 KS |
288 | if fcc.is_none() { |
289 | return Err(MuxerError::UnsupportedFormat); | |
290 | } | |
291 | self.bw.write_buf(&fcc.unwrap_or([0; 4]))?; | |
292 | self.bw.write_u32le(0)?; // image size | |
293 | self.bw.write_u32le(0)?; // x dpi | |
294 | self.bw.write_u32le(0)?; // y dpi | |
295 | if vinfo.format.palette { | |
a1613eee | 296 | self.bw.write_u32le(256)?; // total colors |
5cd2f537 | 297 | self.bw.write_u32le(0)?; // important colors |
a1613eee KS |
298 | self.pal_pos[strno] = self.bw.tell() as u32; |
299 | for _ in 0..256 { | |
300 | self.bw.write_u32le(0)?; | |
301 | } | |
5cd2f537 KS |
302 | } else { |
303 | self.bw.write_u32le(0)?; // total colors | |
304 | self.bw.write_u32le(0)?; // important colors | |
305 | } | |
405cec9e | 306 | if let Some(ref edata) = strm.get_info().get_extradata() { |
5cd2f537 KS |
307 | self.bw.write_buf(edata.as_slice())?; |
308 | } | |
309 | let bisize = self.bw.tell() - hdr_pos; | |
310 | self.bw.seek(SeekFrom::Current(-(bisize as i64)))?; | |
311 | self.bw.write_u32le(bisize as u32)?; | |
312 | self.bw.seek(SeekFrom::End(0))?; | |
313 | }, | |
314 | StreamType::Audio => { | |
405cec9e KS |
315 | let ainfo = strm.get_info().get_properties().get_audio_info().unwrap(); |
316 | let twocc = find_wav_twocc(strm.get_info().get_name()); | |
5cd2f537 KS |
317 | if twocc.is_none() { |
318 | return Err(MuxerError::UnsupportedFormat); | |
319 | } | |
586ea187 | 320 | self.bw.write_u16le(twocc.unwrap_or(0))?; |
5cd2f537 KS |
321 | self.bw.write_u16le(ainfo.channels as u16)?; |
322 | self.bw.write_u32le(ainfo.sample_rate)?; | |
0ed22721 KS |
323 | if strm.get_info().get_name() != "pcm" { |
324 | self.bw.write_u32le(0)?; // avg bytes per second | |
325 | self.bw.write_u16le(ainfo.block_len as u16)?; | |
326 | } else { | |
327 | let blk_size = u32::from(ainfo.channels) * u32::from(ainfo.format.bits) / 8; | |
328 | let avg_bytes = ainfo.sample_rate * blk_size; | |
329 | self.bw.write_u32le(avg_bytes)?; | |
330 | self.bw.write_u16le(blk_size as u16)?; | |
331 | } | |
5cd2f537 | 332 | self.bw.write_u16le(ainfo.format.bits as u16)?; |
405cec9e | 333 | if let Some(ref edata) = strm.get_info().get_extradata() { |
5cd2f537 KS |
334 | self.bw.write_buf(edata.as_slice())?; |
335 | } | |
336 | }, | |
337 | StreamType::Subtitles => { | |
405cec9e | 338 | if let Some(ref edata) = strm.get_info().get_extradata() { |
5cd2f537 KS |
339 | self.bw.write_buf(edata.as_slice())?; |
340 | } | |
341 | }, | |
342 | _ => unreachable!(), | |
343 | }; | |
6f263099 | 344 | patch_size(self.bw, strf_pos)?; |
e90482b6 KS |
345 | /* OpenDML */ { |
346 | if let Some(sstat) = self.stream_info.last_mut() { | |
347 | sstat.indx_pos = self.bw.tell(); | |
348 | } | |
349 | self.bw.write_buf(b"indx")?; | |
350 | self.bw.write_u32le((6 * 4 + MAX_RIFF_BLOCKS * 16) as u32)?; | |
351 | self.bw.write_u16le(4)?; | |
352 | self.bw.write_byte(0)?; | |
353 | self.bw.write_byte(0)?; // AVI_INDEX_OF_INDEXES | |
354 | self.bw.write_u32le(0)?; // num entries | |
355 | let mut ix_tag = [b'0' + ((strno / 10) as u8), b'0' + ((strno % 10) as u8), 0, 0]; | |
356 | match strm.get_media_type() { | |
357 | StreamType::Video => { | |
358 | ix_tag[2] = b'd'; | |
359 | ix_tag[3] = b'b'; | |
360 | }, | |
361 | StreamType::Audio => { | |
362 | ix_tag[2] = b'w'; | |
363 | ix_tag[3] = b'b'; | |
364 | }, | |
365 | _ => return Err(MuxerError::NotImplemented), | |
366 | } | |
367 | self.bw.write_buf(&ix_tag)?; | |
368 | for _ in 0..3 { | |
369 | self.bw.write_u32le(0)?; | |
370 | } | |
371 | for _ in 0..MAX_RIFF_BLOCKS { | |
372 | self.bw.write_u64le(0)?; | |
373 | self.bw.write_u32le(0)?; | |
374 | self.bw.write_u32le(0)?; | |
375 | } | |
376 | ||
377 | /*if let StreamInfo::Video(ref vinfo) = stream { | |
378 | self.bw.write_buf(b"vprp")?; | |
379 | self.bw.write_u32le(0x44)?; | |
380 | self.bw.write_u32le(0)?; // video format token | |
381 | self.bw.write_u32le(0)?; // video standard | |
382 | self.bw.write_u32le((vinfo.tb_den + vinfo.tb_num / 2) / vinfo.tb_num)?; // refresh rate | |
383 | self.bw.write_u32le(vinfo.width as u32)?; | |
384 | self.bw.write_u32le(vinfo.height as u32)?; | |
385 | self.bw.write_u32le(0x00010001)?; // aspect ratio | |
386 | self.bw.write_u32le(vinfo.width as u32)?; | |
387 | self.bw.write_u32le(vinfo.height as u32)?; | |
388 | self.bw.write_u32le(1)?; // single field | |
389 | self.bw.write_u32le(vinfo.height as u32)?; | |
390 | self.bw.write_u32le(vinfo.width as u32)?; | |
391 | self.bw.write_u32le(vinfo.height as u32)?; | |
392 | self.bw.write_u32le(vinfo.width as u32)?; | |
393 | self.bw.write_u32le(0)?; | |
394 | self.bw.write_u32le(0)?; | |
395 | self.bw.write_u32le(0)?; | |
396 | self.bw.write_u32le(0)?; | |
397 | }*/ | |
398 | } | |
6f263099 | 399 | patch_size(self.bw, strl_pos)?; |
5cd2f537 | 400 | } |
e90482b6 KS |
401 | /* OpenDML */ { |
402 | self.odml_pos = self.bw.tell(); | |
403 | self.bw.write_buf(b"LIST")?; | |
404 | self.bw.write_u32le(16)?; | |
405 | self.bw.write_buf(b"odmldmlh")?; | |
406 | self.bw.write_u32le(4)?; | |
407 | self.bw.write_u32le(0)?; | |
408 | } | |
6f263099 | 409 | patch_size(self.bw, hdrl_pos)?; |
5cd2f537 KS |
410 | |
411 | self.data_pos = self.bw.tell() + 8; | |
412 | self.bw.write_buf(b"LIST\0\0\0\0movi")?; | |
413 | ||
414 | Ok(()) | |
415 | } | |
416 | fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> { | |
417 | if self.data_pos == 0 { | |
418 | return Err(MuxerError::NotCreated); | |
419 | } | |
817e4872 KS |
420 | let stream = pkt.get_stream(); |
421 | let str_num = stream.get_num(); | |
5cd2f537 KS |
422 | if str_num > 99 || str_num >= self.stream_info.len() { |
423 | return Err(MuxerError::UnsupportedFormat); | |
424 | } | |
425 | ||
e90482b6 KS |
426 | if self.bw.tell() > self.blk_start + MAX_RIFF_SIZE { |
427 | self.flush_riff_block()?; | |
428 | } | |
429 | ||
430 | if !self.wrote_hdr { | |
431 | if self.riff_blk >= MAX_RIFF_BLOCKS { | |
432 | return Err(MuxerError::NotPossible); | |
433 | } | |
434 | self.bw.seek(SeekFrom::Start(self.blk_start))?; | |
435 | ||
436 | self.bw.write_buf(b"RIFF")?; | |
437 | self.bw.write_u32le(0)?; | |
438 | self.bw.write_buf(b"AVIX")?; | |
439 | self.bw.write_buf(b"LIST")?; | |
440 | self.bw.write_u32le(0)?; | |
441 | self.data_pos = self.bw.tell(); | |
442 | self.bw.write_buf(b"movi")?; | |
443 | ||
444 | self.wrote_hdr = true; | |
445 | } | |
446 | ||
5cd2f537 KS |
447 | let chunk_len = pkt.get_buffer().len() as u32; |
448 | ||
a1613eee KS |
449 | if self.pal_pos[str_num] != 0 { |
450 | for sdata in pkt.side_data.iter() { | |
451 | if let NASideData::Palette(_, ref pal) = sdata { | |
452 | let cur_pos = self.bw.tell(); | |
453 | self.bw.seek(SeekFrom::Start(u64::from(self.pal_pos[str_num])))?; | |
bfe6df94 KS |
454 | for quad in pal.chunks(4) { |
455 | self.bw.write_byte(quad[2])?; | |
456 | self.bw.write_byte(quad[1])?; | |
457 | self.bw.write_byte(quad[0])?; | |
458 | self.bw.write_byte(0)?; | |
459 | } | |
a1613eee KS |
460 | self.bw.seek(SeekFrom::Start(cur_pos))?; |
461 | self.pal_pos[str_num] = 0; | |
462 | break; | |
463 | } | |
464 | } | |
465 | } else { | |
466 | for sdata in pkt.side_data.iter() { | |
467 | if let NASideData::Palette(true, ref pal) = sdata { | |
468 | //todo search for changed region | |
469 | let start_clr = 0usize; | |
470 | let end_clr = 256usize; | |
471 | if start_clr < end_clr { | |
472 | let chunk_len = ((end_clr - start_clr) as u32) * 4 + 4; | |
fb37b471 KS |
473 | let mut tag = gen_chunk_tag(StreamType::Video, str_num as u32); |
474 | tag[2] = b'p'; | |
475 | tag[3] = b'c'; | |
476 | self.index.push(IdxEntry { | |
477 | tag, | |
478 | key: false, | |
479 | pos: self.bw.tell(), | |
480 | len: chunk_len }); | |
481 | self.bw.write_buf(&tag)?; | |
a1613eee KS |
482 | self.bw.write_u32le(chunk_len)?; |
483 | self.bw.write_byte(start_clr as u8)?; | |
484 | self.bw.write_byte((end_clr - start_clr) as u8)?; | |
485 | self.bw.write_u16le(0)?; //flags | |
486 | self.bw.write_buf(&pal[start_clr * 4..end_clr * 4])?; | |
04fafc56 | 487 | self.stream_info[str_num].pal_change = true; |
a1613eee KS |
488 | } |
489 | } | |
490 | } | |
491 | } | |
492 | ||
5cd2f537 | 493 | self.stream_info[str_num].nframes += 1; |
e90482b6 | 494 | self.stream_info[str_num].lframes += 1; |
5cd2f537 | 495 | self.stream_info[str_num].max_size = self.stream_info[str_num].max_size.max(chunk_len); |
34bd4557 | 496 | let tag = gen_chunk_tag(stream.get_media_type(), str_num as u32); |
5cd2f537 | 497 | self.index.push(IdxEntry { |
34bd4557 | 498 | tag, |
5cd2f537 | 499 | key: pkt.keyframe, |
34bd4557 | 500 | pos: self.bw.tell(), |
5cd2f537 | 501 | len: chunk_len }); |
34bd4557 | 502 | self.bw.write_buf(&tag)?; |
5cd2f537 KS |
503 | self.bw.write_u32le(chunk_len)?; |
504 | self.bw.write_buf(pkt.get_buffer().as_slice())?; | |
77d52ff6 KS |
505 | if (self.bw.tell() & 1) != 0 { |
506 | self.bw.write_byte(0)?; | |
507 | } | |
5cd2f537 KS |
508 | Ok(()) |
509 | } | |
510 | fn flush(&mut self) -> MuxerResult<()> { | |
511 | Ok(()) | |
512 | } | |
513 | fn end(&mut self) -> MuxerResult<()> { | |
e90482b6 KS |
514 | if self.riff_blk == 0 && self.index.is_empty() { |
515 | return Err(MuxerError::NotCreated); | |
5cd2f537 | 516 | } |
e90482b6 KS |
517 | if self.riff_blk > 0 { |
518 | self.flush_riff_block()?; | |
519 | let mut max_frames = 0; | |
520 | for sstat in self.stream_info.iter() { | |
521 | max_frames = max_frames.max(sstat.nframes); | |
522 | ||
523 | self.bw.seek(SeekFrom::Start(sstat.indx_pos + 12))?; | |
524 | self.bw.write_u32le(self.riff_blk as u32)?; | |
04fafc56 | 525 | } |
e90482b6 KS |
526 | self.bw.seek(SeekFrom::Start(self.odml_pos + 20))?; |
527 | self.bw.write_u32le(max_frames)?; | |
528 | ||
529 | self.bw.flush()?; | |
530 | Ok(()) | |
531 | } else if !self.index.is_empty() { | |
532 | patch_size(self.bw, self.data_pos)?; | |
533 | if !self.index.is_empty() { | |
534 | self.bw.write_buf(b"idx1")?; | |
535 | self.bw.write_u32le((self.index.len() * 16) as u32)?; | |
536 | for item in self.index.iter() { | |
537 | self.bw.write_buf(&item.tag)?; | |
538 | let flags = if item.key { 0x10 } | |
539 | else if &item.tag[2..] == b"pc" { 0x100 } | |
540 | else { 0 }; | |
541 | self.bw.write_u32le(flags)?; | |
542 | self.bw.write_u32le(item.pos as u32)?; | |
543 | self.bw.write_u32le(item.len)?; | |
544 | } | |
545 | } | |
546 | patch_size(self.bw, 8)?; | |
547 | ||
548 | self.patch_stream_stats()?; | |
549 | ||
550 | // patch out useless OpenDML entries | |
551 | self.bw.seek(SeekFrom::Start(self.odml_pos))?; | |
552 | self.bw.write_buf(b"JUNK")?; | |
553 | for sstat in self.stream_info.iter() { | |
554 | self.bw.seek(SeekFrom::Start(sstat.indx_pos))?; | |
555 | self.bw.write_buf(b"JUNK")?; | |
556 | } | |
557 | ||
558 | self.bw.flush()?; | |
559 | ||
560 | Ok(()) | |
561 | } else { | |
562 | Err(MuxerError::NotCreated) | |
5cd2f537 | 563 | } |
5cd2f537 KS |
564 | } |
565 | } | |
566 | ||
dc80f48e KS |
567 | impl<'a> NAOptionHandler for AVIMuxer<'a> { |
568 | fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } | |
569 | fn set_options(&mut self, _options: &[NAOption]) { } | |
570 | fn query_option_value(&self, _name: &str) -> Option<NAValue> { None } | |
571 | } | |
572 | ||
5cd2f537 KS |
573 | pub struct AVIMuxerCreator {} |
574 | ||
575 | impl MuxerCreator for AVIMuxerCreator { | |
576 | fn new_muxer<'a>(&self, bw: &'a mut ByteWriter<'a>) -> Box<dyn MuxCore<'a> + 'a> { | |
577 | Box::new(AVIMuxer::new(bw)) | |
578 | } | |
579 | fn get_name(&self) -> &'static str { "avi" } | |
f0081142 | 580 | fn get_capabilities(&self) -> MuxerCapabilities { MuxerCapabilities::Universal } |
46008a8f | 581 | fn get_quirks(&self) -> MuxerQuirks { MuxerQuirks(MUX_QUIRK_FIXED_RATE) } |
5cd2f537 KS |
582 | } |
583 | ||
584 | #[cfg(test)] | |
585 | mod test { | |
68e5a4ca | 586 | use nihav_core::codecs::*; |
5cd2f537 | 587 | use nihav_core::demuxers::*; |
68e5a4ca KS |
588 | use nihav_core::muxers::*; |
589 | use nihav_codec_support::test::enc_video::*; | |
590 | use crate::*; | |
5cd2f537 KS |
591 | |
592 | #[test] | |
593 | fn test_avi_muxer() { | |
594 | let mut dmx_reg = RegisteredDemuxers::new(); | |
595 | generic_register_all_demuxers(&mut dmx_reg); | |
886cde48 | 596 | //test sample: https://samples.mplayerhq.hu/V-codecs/RT21/320x240/laser05.avi |
68e5a4ca KS |
597 | let dec_config = DecoderTestParams { |
598 | demuxer: "avi", | |
599 | in_name: "assets/Indeo/laser05.avi", | |
600 | limit: None, | |
601 | stream_type: StreamType::None, | |
602 | dmx_reg, dec_reg: RegisteredDecoders::new(), | |
603 | }; | |
604 | let mut mux_reg = RegisteredMuxers::new(); | |
605 | generic_register_all_muxers(&mut mux_reg); | |
606 | /*let enc_config = EncoderTestParams { | |
607 | muxer: "avi", | |
608 | enc_name: "", | |
609 | out_name: "muxed.avi", | |
610 | mux_reg, enc_reg: RegisteredEncoders::new(), | |
611 | }; | |
612 | test_remuxing(&dec_config, &enc_config);*/ | |
613 | test_remuxing_md5(&dec_config, "avi", &mux_reg, | |
e90482b6 | 614 | [0xc5306965, 0xfe9ae8b8, 0x91c38644, 0x21ab366d]); |
5cd2f537 KS |
615 | } |
616 | } |