]> git.nihav.org Git - nihav.git/blame - nihav-commonfmt/src/muxers/avi.rs
avimux: do not record palette change chunks in OpenDML index
[nihav.git] / nihav-commonfmt / src / muxers / avi.rs
CommitLineData
5cd2f537
KS
1use nihav_core::muxers::*;
2use nihav_registry::register::*;
3
e90482b6
KS
4const MAX_RIFF_BLOCKS: usize = 16; // ~16GB file should be enough
5const 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)]
8struct 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)]
16struct 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
26struct 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
40impl<'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
136fn 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
144fn 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
155impl<'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
567impl<'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
573pub struct AVIMuxerCreator {}
574
575impl 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)]
585mod 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}