rename "str" variable to avoid confusion with the primitive type
[nihav.git] / nihav-commonfmt / src / muxers / avi.rs
index 25831098d318d3e513bd9c55fa647362e13cbb1b..fad358cf2356604b0cdff092185d17647a3e3562 100644 (file)
@@ -25,6 +25,7 @@ struct AVIMuxer<'a> {
     video_id:       u32,
     data_pos:       u64,
     stream_info:    Vec<AVIStream>,
+    pal_pos:        Vec<u32>,
 }
 
 impl<'a> AVIMuxer<'a> {
@@ -36,6 +37,7 @@ impl<'a> AVIMuxer<'a> {
             video_id:       0,
             data_pos:       0,
             stream_info:    Vec::with_capacity(2),
+            pal_pos:        Vec::with_capacity(2),
         }
     }
 }
@@ -61,6 +63,8 @@ fn write_chunk_hdr(bw: &mut ByteWriter, stype: StreamType, str_no: u32) -> Muxer
 }
 
 impl<'a> MuxCore<'a> for AVIMuxer<'a> {
+    #[allow(clippy::unreadable_literal)]
+    #[allow(clippy::cast_lossless)]
     fn create(&mut self, strmgr: &StreamManager) -> MuxerResult<()> {
         if strmgr.get_num_streams() == 0 {
             return Err(MuxerError::InvalidArgument);
@@ -68,10 +72,10 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
         if strmgr.get_num_streams() > 99 {
             return Err(MuxerError::UnsupportedFormat);
         }
-        for (str_no, str) in strmgr.iter().enumerate() {
-            if str.get_media_type() == StreamType::Video {
+        for (str_no, strm) in strmgr.iter().enumerate() {
+            if strm.get_media_type() == StreamType::Video {
                 self.video_str = Some(str_no);
-                self.video_id  = str.id;
+                self.video_id  = strm.id;
                 break;
             }
         }
@@ -105,20 +109,22 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
         self.bw.write_u32le(0)?; // reserved
         self.bw.write_u32le(0)?; // reserved
 
-        for str in strmgr.iter() {
+        self.pal_pos.clear();
+        self.pal_pos.resize(strmgr.get_num_streams(), 0);
+        for (strno, strm) in strmgr.iter().enumerate() {
             let strl_pos = self.bw.tell() + 8;
             self.bw.write_buf(b"LIST\0\0\0\0strlstrh")?;
             self.bw.write_u32le(56)?; // strh size
 
-            match str.get_media_type() {
+            match strm.get_media_type() {
                 StreamType::Video => {
                     self.bw.write_buf(b"vids")?;
-                    let fcc = find_avi_fourcc(str.get_info().get_name());
+                    let fcc = find_avi_fourcc(strm.get_info().get_name());
                     if fcc.is_none() {
                         return Err(MuxerError::UnsupportedFormat);
                     }
                     self.bw.write_buf(&fcc.unwrap_or([0; 4]))?;
-                    let vinfo = str.get_info().get_properties().get_video_info().unwrap();
+                    let vinfo = strm.get_info().get_properties().get_video_info().unwrap();
                     if vinfo.width >= (1 << 16) || vinfo.height >= (1 << 16) {
                         return Err(MuxerError::UnsupportedFormat);
                     }
@@ -135,7 +141,7 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
             };
             self.stream_info.push(AVIStream {
                     strh_pos:   self.bw.tell(),
-                    is_video:   str.get_media_type() == StreamType::Video,
+                    is_video:   strm.get_media_type() == StreamType::Video,
                     nframes:    0,
                     max_size:   0,
                 });
@@ -144,8 +150,8 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
             self.bw.write_u16le(0)?; // priority
             self.bw.write_u16le(0)?; // language
             self.bw.write_u32le(0)?; // initial frames
-            self.bw.write_u32le(str.tb_num)?;
-            self.bw.write_u32le(str.tb_den)?;
+            self.bw.write_u32le(strm.tb_num)?;
+            self.bw.write_u32le(strm.tb_den)?;
             self.bw.write_u32le(0)?; // start
             self.bw.write_u32le(0)?; // length
             self.bw.write_u32le(0)?; // suggested buffer size
@@ -159,20 +165,21 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
             self.bw.write_buf(b"strf")?;
             self.bw.write_u32le(0)?;
             let strf_pos = self.bw.tell();
-            match str.get_media_type() {
+            match strm.get_media_type() {
                 StreamType::Video => {
-                    let vinfo = str.get_info().get_properties().get_video_info().unwrap();
+                    let vinfo = strm.get_info().get_properties().get_video_info().unwrap();
                     let hdr_pos = self.bw.tell();
                     self.bw.write_u32le(0)?;
                     self.bw.write_u32le(vinfo.width as u32)?;
-                    if vinfo.flipped {
-                        self.bw.write_u32le((-(vinfo.height as i32)) as u32)?;
+                    self.bw.write_u32le(vinfo.height as u32)?;
+                    if !vinfo.format.palette {
+                        self.bw.write_u16le(vinfo.format.components as u16)?;
+                        self.bw.write_u16le(vinfo.format.get_total_depth() as u16)?;
                     } else {
-                        self.bw.write_u32le(vinfo.height as u32)?;
+                        self.bw.write_u16le(1)?;
+                        self.bw.write_u16le(8)?;
                     }
-                    self.bw.write_u16le(vinfo.format.components as u16)?;
-                    self.bw.write_u16le(vinfo.format.get_total_depth() as u16)?;
-                    let fcc = find_avi_fourcc(str.get_info().get_name());
+                    let fcc = find_avi_fourcc(strm.get_info().get_name());
                     if fcc.is_none() {
                         return Err(MuxerError::UnsupportedFormat);
                     }
@@ -181,14 +188,17 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
                     self.bw.write_u32le(0)?; // x dpi
                     self.bw.write_u32le(0)?; // y dpi
                     if vinfo.format.palette {
-//                        unimplemented!();
-                        self.bw.write_u32le(0)?; // total colors
+                        self.bw.write_u32le(256)?; // total colors
                         self.bw.write_u32le(0)?; // important colors
+                        self.pal_pos[strno] = self.bw.tell() as u32;
+                        for _ in 0..256 {
+                            self.bw.write_u32le(0)?;
+                        }
                     } else {
                         self.bw.write_u32le(0)?; // total colors
                         self.bw.write_u32le(0)?; // important colors
                     }
-                    if let Some(ref edata) = str.get_info().get_extradata() {
+                    if let Some(ref edata) = strm.get_info().get_extradata() {
                         self.bw.write_buf(edata.as_slice())?;
                     }
                     let bisize = self.bw.tell() - hdr_pos;
@@ -197,8 +207,8 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
                     self.bw.seek(SeekFrom::End(0))?;
                 },
                 StreamType::Audio => {
-                    let ainfo = str.get_info().get_properties().get_audio_info().unwrap();
-                    let twocc = find_wav_twocc(str.get_info().get_name());
+                    let ainfo = strm.get_info().get_properties().get_audio_info().unwrap();
+                    let twocc = find_wav_twocc(strm.get_info().get_name());
                     if twocc.is_none() {
                         return Err(MuxerError::UnsupportedFormat);
                     }
@@ -208,21 +218,21 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
                     self.bw.write_u32le(0)?; // avg bytes per second
                     self.bw.write_u16le(ainfo.block_len as u16)?;
                     self.bw.write_u16le(ainfo.format.bits as u16)?;
-                    if let Some(ref edata) = str.get_info().get_extradata() {
+                    if let Some(ref edata) = strm.get_info().get_extradata() {
                         self.bw.write_buf(edata.as_slice())?;
                     }
                 },
                 StreamType::Subtitles => {
-                    if let Some(ref edata) = str.get_info().get_extradata() {
+                    if let Some(ref edata) = strm.get_info().get_extradata() {
                         self.bw.write_buf(edata.as_slice())?;
                     }
                 },
                 _ => unreachable!(),
             };
-            patch_size(&mut self.bw, strf_pos)?;
-            patch_size(&mut self.bw, strl_pos)?;
+            patch_size(self.bw, strf_pos)?;
+            patch_size(self.bw, strl_pos)?;
         }
-        patch_size(&mut self.bw, hdrl_pos)?;
+        patch_size(self.bw, hdrl_pos)?;
 
         self.data_pos = self.bw.tell() + 8;
         self.bw.write_buf(b"LIST\0\0\0\0movi")?;
@@ -233,44 +243,78 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> {
         if self.data_pos == 0 {
             return Err(MuxerError::NotCreated);
         }
-        let str = pkt.get_stream();
-        let str_num = str.get_num();
+        let stream = pkt.get_stream();
+        let str_num = stream.get_num();
         if str_num > 99 || str_num >= self.stream_info.len() {
             return Err(MuxerError::UnsupportedFormat);
         }
 
         let chunk_len = pkt.get_buffer().len() as u32;
 
+        if self.pal_pos[str_num] != 0 {
+            for sdata in pkt.side_data.iter() {
+                if let NASideData::Palette(_, ref pal) = sdata {
+                    let cur_pos = self.bw.tell();
+                    self.bw.seek(SeekFrom::Start(u64::from(self.pal_pos[str_num])))?;
+                    self.bw.write_buf(pal.as_ref())?;
+                    self.bw.seek(SeekFrom::Start(cur_pos))?;
+                    self.pal_pos[str_num] = 0;
+                    break;
+                }
+            }
+        } else {
+            for sdata in pkt.side_data.iter() {
+                if let NASideData::Palette(true, ref pal) = sdata {
+                    //todo search for changed region
+                    let start_clr = 0usize;
+                    let end_clr = 256usize;
+                    if start_clr < end_clr {
+                        let chunk_len = ((end_clr - start_clr) as u32) * 4 + 4;
+                        self.bw.write_byte(b'0' + ((str_num / 10) as u8))?;
+                        self.bw.write_byte(b'0' + ((str_num % 10) as u8))?;
+                        self.bw.write_buf(b"pc")?;
+                        self.bw.write_u32le(chunk_len)?;
+                        self.bw.write_byte(start_clr as u8)?;
+                        self.bw.write_byte((end_clr - start_clr) as u8)?;
+                        self.bw.write_u16le(0)?; //flags
+                        self.bw.write_buf(&pal[start_clr * 4..end_clr * 4])?;
+                    }
+                }
+            }
+        }
+
         self.stream_info[str_num].nframes += 1;
         self.stream_info[str_num].max_size = self.stream_info[str_num].max_size.max(chunk_len);
-// todo palchange
         self.index.push(IdxEntry {
                 stream: str_num as u32,
-                stype:  str.get_media_type(),
+                stype:  stream.get_media_type(),
                 key:    pkt.keyframe,
                 pos:    self.bw.tell() as u32,
                 len:    chunk_len });
-        write_chunk_hdr(&mut self.bw, str.get_media_type(), str_num as u32)?;
+        write_chunk_hdr(self.bw, stream.get_media_type(), str_num as u32)?;
         self.bw.write_u32le(chunk_len)?;
         self.bw.write_buf(pkt.get_buffer().as_slice())?;
+        if (self.bw.tell() & 1) != 0 {
+            self.bw.write_byte(0)?;
+        }
         Ok(())
     }
     fn flush(&mut self) -> MuxerResult<()> {
         Ok(())
     }
     fn end(&mut self) -> MuxerResult<()> {
-        patch_size(&mut self.bw, self.data_pos)?;
-        if self.index.len() > 0 {
+        patch_size(self.bw, self.data_pos)?;
+        if !self.index.is_empty() {
             self.bw.write_buf(b"idx1")?;
             self.bw.write_u32le((self.index.len() * 16) as u32)?;
             for item in self.index.iter() {
-                write_chunk_hdr(&mut self.bw, item.stype, item.stream)?;
+                write_chunk_hdr(self.bw, item.stype, item.stream)?;
                 self.bw.write_u32le(if item.key { 0x10 } else { 0 })?;
                 self.bw.write_u32le(item.pos)?;
                 self.bw.write_u32le(item.len)?;
             }
         }
-        patch_size(&mut self.bw, 8)?;
+        patch_size(self.bw, 8)?;
         let mut max_frames = 0;
         let mut max_size = 0;
         for stri in self.stream_info.iter() {
@@ -316,6 +360,7 @@ mod test {
     fn test_avi_muxer() {
         let mut dmx_reg = RegisteredDemuxers::new();
         generic_register_all_demuxers(&mut dmx_reg);
+        //test sample: https://samples.mplayerhq.hu/V-codecs/RT21/320x240/laser05.avi
         let dec_config = DecoderTestParams {
                 demuxer:        "avi",
                 in_name:        "assets/Indeo/laser05.avi",