avi: support raw YUV formats
[nihav.git] / nihav-commonfmt / src / demuxers / avi.rs
index f108e3ccc62adb49cdd9635974a6484956de8051..73bd02f9731139c93f08fa270dec22c7bceb6a3c 100644 (file)
@@ -1,6 +1,7 @@
 use nihav_core::demuxers::*;
 use nihav_registry::register;
 use nihav_core::demuxers::DemuxerError::*;
+use std::str::FromStr;
 
 macro_rules! mktag {
     ($a:expr, $b:expr, $c:expr, $d:expr) => ({
@@ -52,6 +53,7 @@ struct AVIDemuxer<'a> {
     sstate:         StreamState,
     tb_num:         u32,
     tb_den:         u32,
+    strm_duration:  u32,
     key_offs:       Vec<u64>,
     pal:            Vec<PalInfo>,
 }
@@ -119,7 +121,10 @@ impl<'a> DemuxCore<'a> for AVIDemuxer<'a> {
                 continue;
             }
             let (tb_num, tb_den) = stream.get_timebase();
-            let ts = NATimeInfo::new(Some(self.cur_frame[stream_no as usize]), None, None, tb_num, tb_den);
+            let mut ts = NATimeInfo::new(Some(self.cur_frame[stream_no as usize]), None, None, tb_num, tb_den);
+            if stream.get_media_type() == StreamType::Audio && tb_num == 1 && stream.get_info().get_name() == "pcm" {
+                ts.pts = None;
+            }
             let mut pkt = self.src.read_packet(stream, ts, is_keyframe, size)?;
             for pe in self.pal.iter_mut() {
                 if pe.stream_no == (stream_no as usize) {
@@ -135,7 +140,7 @@ impl<'a> DemuxCore<'a> for AVIDemuxer<'a> {
         }
     }
 
-    fn seek(&mut self, time: u64, seek_index: &SeekIndex) -> DemuxerResult<()> {
+    fn seek(&mut self, time: NATimePoint, seek_index: &SeekIndex) -> DemuxerResult<()> {
         let ret = seek_index.find_pos(time);
         if ret.is_none() {
             return Err(DemuxerError::SeekError);
@@ -152,6 +157,13 @@ impl<'a> DemuxCore<'a> for AVIDemuxer<'a> {
 
         Ok(())
     }
+    fn get_duration(&self) -> u64 { 0 }
+}
+
+impl<'a> NAOptionHandler for AVIDemuxer<'a> {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
 }
 
 impl<'a> AVIDemuxer<'a> {
@@ -167,6 +179,7 @@ impl<'a> AVIDemuxer<'a> {
             sstate: StreamState::new(),
             tb_num: 0,
             tb_den: 0,
+            strm_duration: 0,
             key_offs: Vec::new(),
             pal: Vec::new(),
         }
@@ -354,7 +367,7 @@ fn parse_strh(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize) ->
     dmx.tb_num = dmx.src.read_u32le()?; //scale
     dmx.tb_den = dmx.src.read_u32le()?; //rate
     dmx.src.read_skip(4)?; //start
-    dmx.src.read_skip(4)?; //length
+    dmx.strm_duration = dmx.src.read_u32le()?;
     dmx.src.read_skip(4)?; //buf size
     dmx.src.read_skip(4)?; //quality
     dmx.src.read_skip(4)?; //sample size
@@ -409,7 +422,18 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize
 
     let flip = height < 0;
     let format = if bitcount > 8 { RGB24_FORMAT } else { PAL8_FORMAT };
-    let vhdr = NAVideoInfo::new(width as usize, if flip { -height as usize } else { height as usize}, flip, format);
+    let mut vhdr = NAVideoInfo::new(width as usize, if flip { -height as usize } else { height as usize}, flip, format);
+    vhdr.bits = (planes as u8) * (bitcount as u8);
+    let cname = if find_raw_rgb_fmt(&compression, planes, bitcount, flip, &mut vhdr) {
+            "rawvideo-ms"
+        } else if find_raw_yuv_fmt(&compression, &mut vhdr) {
+            "rawvideo"
+        } else {
+            match register::find_codec_from_avi_fourcc(&compression) {
+                None => "unknown",
+                Some(name) => name,
+            }
+        };
     let vci = NACodecTypeInfo::Video(vhdr);
     let edata = dmx.read_extradata(size - 40)?;
     if colors > 0 {
@@ -425,17 +449,57 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize
             dmx.pal.push(pal);
         }
     }
-    let cname = match register::find_codec_from_avi_fourcc(&compression) {
-                    None => "unknown",
-                    Some(name) => name,
-                };
     let vinfo = NACodecInfo::new(cname, vci, edata);
-    let res = strmgr.add_stream(NAStream::new(StreamType::Video, u32::from(dmx.sstate.strm_no), vinfo, dmx.tb_num, dmx.tb_den));
+    let res = strmgr.add_stream(NAStream::new(StreamType::Video, u32::from(dmx.sstate.strm_no), vinfo, dmx.tb_num, dmx.tb_den, u64::from(dmx.strm_duration)));
     if res.is_none() { return Err(MemoryError); }
     dmx.sstate.reset();
     Ok(size)
 }
 
+fn find_raw_rgb_fmt(compr: &[u8; 4], planes: u16, bitcount: u16, flip: bool, vhdr: &mut NAVideoInfo) -> bool {
+    match compr {
+        &[0, 0, 0, 0] | b"DIB " => {
+            if planes != 1 {
+                return false;
+            }
+            let fmt_name = match bitcount {
+                     8 => "pal8",
+                    16 => "bgr555",
+                    24 => "bgr24",
+                    32 => "bgra24",
+                    _ => return false,
+                };
+            if let Ok(fmt) = NAPixelFormaton::from_str(fmt_name) {
+                vhdr.format = fmt;
+                vhdr.flipped = !flip;
+                true
+            } else {
+                false
+            }
+        },
+        _ => false,
+    }
+}
+
+fn find_raw_yuv_fmt(compr: &[u8; 4], vhdr: &mut NAVideoInfo) -> bool {
+    let (fmt_name, swapuv) = match compr {
+            b"UYVY" | b"UYNY" | b"UYNV" | b"2vuy" => ("uyvy", false),
+            b"I420" | b"IYUV" => ("yuv420p", false),
+            b"YV12" => ("yuv420p", true),
+            b"YVU9" => ("yuv410p", true),
+            _ => return false,
+        };
+    if let Ok(fmt) = NAPixelFormaton::from_str(fmt_name) {
+        vhdr.format = fmt;
+        if swapuv {
+            vhdr.format.comp_info.swap(1, 2);
+        }
+        true
+    } else {
+        false
+    }
+}
+
 #[allow(unused_variables)]
 fn parse_strf_auds(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize) -> DemuxerResult<usize> {
     if size < 16 { return Err(InvalidData); }
@@ -446,15 +510,22 @@ fn parse_strf_auds(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize
     let block_align         = dmx.src.read_u16le()?;
     let bits_per_sample     = dmx.src.read_u16le()?;
 
-    let soniton = NASoniton::new(bits_per_sample as u8, SONITON_FLAG_SIGNED);
+    let signed = bits_per_sample > 8;
+    let soniton = NASoniton::new(bits_per_sample as u8, if signed { SONITON_FLAG_SIGNED } else { 0 });
     let ahdr = NAAudioInfo::new(samplespersec, channels as u8, soniton, block_align as usize);
-    let edata = dmx.read_extradata(size - 16)?;
+    let edata = if size > 16 {
+            let edata_size  = dmx.src.read_u16le()? as usize;
+            validate!(edata_size + 18 <= size);
+            dmx.read_extradata(size - 18)?
+        } else {
+            None
+        };
     let cname = match register::find_codec_from_wav_twocc(w_format_tag) {
                     None => "unknown",
                     Some(name) => name,
                 };
     let ainfo = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata);
-    let res = strmgr.add_stream(NAStream::new(StreamType::Audio, u32::from(dmx.sstate.strm_no), ainfo, dmx.tb_num, dmx.tb_den));
+    let res = strmgr.add_stream(NAStream::new(StreamType::Audio, u32::from(dmx.sstate.strm_no), ainfo, dmx.tb_num, dmx.tb_den, u64::from(dmx.strm_duration)));
     if res.is_none() { return Err(MemoryError); }
     dmx.sstate.reset();
     Ok(size)
@@ -463,7 +534,7 @@ fn parse_strf_auds(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize
 fn parse_strf_xxxx(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize) -> DemuxerResult<usize> {
     let edata = dmx.read_extradata(size)?;
     let info = NACodecInfo::new("unknown", NACodecTypeInfo::None, edata);
-    let res = strmgr.add_stream(NAStream::new(StreamType::Data, u32::from(dmx.sstate.strm_no), info, dmx.tb_num, dmx.tb_den));
+    let res = strmgr.add_stream(NAStream::new(StreamType::Data, u32::from(dmx.sstate.strm_no), info, dmx.tb_num, dmx.tb_den, u64::from(dmx.strm_duration)));
     if res.is_none() { return Err(MemoryError); }
     dmx.sstate.reset();
     Ok(size)
@@ -498,17 +569,27 @@ fn parse_junk(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize) ->
     Ok(size)
 }
 
+#[allow(clippy::verbose_bit_mask)]
 fn parse_idx1(src: &mut ByteReader, strmgr: &mut StreamManager, seek_idx: &mut SeekIndex, size: usize, movi_pos: u64, key_offs: &mut Vec<u64>) -> DemuxerResult<usize> {
     validate!((size & 15) == 0);
     let mut tag = [0u8; 4];
     let num_entries = size >> 4;
     let mut counter = [0u64; 100];
+    let mut add_offset = 0;
+    let mut set_offset = false;
     for _ in 0..num_entries {
                               src.read_buf(&mut tag)?;
         let flags           = src.read_u32le()?;
-        let offset          = src.read_u32le()? as u64;
+        let mut offset      = src.read_u32le()? as u64;
         let _length         = src.read_u32le()?;
 
+        if !set_offset && offset < movi_pos {
+            add_offset = movi_pos - offset;
+        }
+        set_offset = true;
+
+        offset += add_offset;
+
         if tag[0] < b'0' || tag[0] > b'9' || tag[1] < b'0' || tag[1] > b'9' {
             return Err(InvalidData);
         }
@@ -520,7 +601,8 @@ fn parse_idx1(src: &mut ByteReader, strmgr: &mut StreamManager, seek_idx: &mut S
                     let (tb_num, tb_den) = str.get_timebase();
                     let pts = counter[stream_no];
                     let time = NATimeInfo::ts_to_time(pts, 1000, tb_num, tb_den);
-                    seek_idx.add_entry(stream_no as u32, SeekEntry { time, pts, pos: offset + movi_pos - 4 });
+                    validate!(offset >= movi_pos);
+                    seek_idx.add_entry(stream_no as u32, SeekEntry { time, pts, pos: offset });
                 }
                 key_offs.push(offset);
             }