mov: serve packets with equal timestamps if possible
[nihav.git] / nihav-commonfmt / src / demuxers / mov.rs
index 9a77496fc2717e70ba563b2756adacd18ae65ba6..745f4402246d0e7f1484fc5af0f4c0688261a384 100644 (file)
@@ -205,6 +205,7 @@ const MOOV_CHUNK_HANDLERS: &[RootChunkHandler] = &[
     RootChunkHandler { ctype: mktag!(b"trak"), parse: read_trak },
     RootChunkHandler { ctype: mktag!(b"meta"), parse: read_meta },
     RootChunkHandler { ctype: mktag!(b"mvex"), parse: read_mvex },
+    RootChunkHandler { ctype: mktag!(b"iods"), parse: skip_chunk_mov },
 ];
 
 fn read_mvhd(dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, size: u64) -> DemuxerResult<u64> {
@@ -291,6 +292,10 @@ fn read_mvex(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) ->
     Ok(0)
 }
 
+fn skip_chunk_mov(_dmx: &mut MOVDemuxer, _strmgr: &mut StreamManager, _size: u64) -> DemuxerResult<u64> {
+    Ok(0)
+}
+
 fn read_trak(dmx: &mut MOVDemuxer, strmgr: &mut StreamManager, size: u64) -> DemuxerResult<u64> {
     let mut track = Track::new(dmx.cur_track as u32, dmx.tb_den);
     track.print_chunks = dmx.print_chunks;
@@ -1053,6 +1058,7 @@ struct Track {
     stream:         Option<NAStream>,
     cur_chunk:      usize,
     cur_sample:     usize,
+    cur_ts:         Option<u64>,
     samples_left:   usize,
     last_offset:    u64,
     pal:            Option<Arc<[u8; 1024]>>,
@@ -1077,7 +1083,7 @@ impl TimeSearcher {
     fn reset(&mut self) {
         *self = Self::default();
     }
-    fn map_time(&mut self, sample: u32, tts: &Vec<(u32, u32)>) -> u64 {
+    fn map_time(&mut self, sample: u32, tts: &[(u32, u32)]) -> u64 {
         if tts.is_empty() {
             u64::from(sample)
         } else if sample >= self.sbase {
@@ -1195,6 +1201,7 @@ impl Track {
             depth:          0,
             cur_chunk:      0,
             cur_sample:     0,
+            cur_ts:         None,
             samples_left:   0,
             last_offset:    0,
             pal:            None,
@@ -1363,9 +1370,11 @@ impl Track {
             self.bsize
         }
     }
+    #[allow(clippy::collapsible_if)]
     fn seek(&mut self, pts: u64, tpoint: NATimePoint) -> DemuxerResult<()> {
         self.cur_sample = pts as usize;
         self.samples_left = 0;
+        self.cur_ts = None;
         if self.stream_type == StreamType::Audio {
             if let NATimePoint::Milliseconds(ms) = tpoint {
                 let exp_pts = NATimeInfo::time_to_ts(ms, 1000, self.tb_num, self.tb_den);
@@ -1503,6 +1512,25 @@ impl Track {
     }
 }
 
+fn process_packet(src: &mut ByteReader, strmgr: &StreamManager, track: &mut Track, pts: NATimeInfo, offset: u64, size: usize, first: bool) -> DemuxerResult<NAPacket> {
+    if let Some(cpts) = pts.get_pts() {
+        let ts = NATimeInfo::ts_to_time(cpts, 1000, pts.tb_num, pts.tb_den);
+        track.cur_ts = Some(ts);
+    } else {
+        track.cur_ts = None;
+    }
+    let str = strmgr.get_stream(track.track_str_id);
+    if str.is_none() { return Err(DemuxerError::InvalidData); }
+    let stream = str.unwrap();
+    src.seek(SeekFrom::Start(offset))?;
+    let mut pkt = src.read_packet(stream, pts, false, size)?;
+    if let Some(ref pal) = track.pal {
+        let side_data = NASideData::Palette(first, pal.clone());
+        pkt.add_side_data(side_data);
+    }
+    Ok(pkt)
+}
+
 impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
     fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> {
         self.read_root(strmgr)?;
@@ -1528,6 +1556,30 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
         if self.tracks.is_empty() {
             return Err(DemuxerError::EOF);
         }
+        let mut has_all_time = true;
+        let mut min_ts = std::u64::MAX;
+        for trk in self.tracks.iter() {
+            if let Some(ts) = trk.cur_ts {
+                min_ts = min_ts.min(ts);
+            } else {
+                has_all_time = false;
+                break;
+            }
+        }
+        if has_all_time {
+            for (trk_no, track) in self.tracks.iter_mut().enumerate() {
+                if let Some(ts) = track.cur_ts {
+                    if ts == min_ts {
+                        let first = track.cur_sample == 0;
+                        if let Some((pts, offset, size)) = track.get_next_chunk() {
+                            self.cur_track = trk_no + 1;
+                            return process_packet(&mut self.src, strmgr, track, pts, offset, size, first);
+                        }
+                    }
+                }
+            }
+        }
+
         for _ in 0..self.tracks.len() {
             if self.cur_track >= self.tracks.len() {
                 self.cur_track = 0;
@@ -1536,16 +1588,7 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> {
             self.cur_track += 1;
             let first = track.cur_sample == 0;
             if let Some((pts, offset, size)) = track.get_next_chunk() {
-                let str = strmgr.get_stream(track.track_str_id);
-                if str.is_none() { return Err(DemuxerError::InvalidData); }
-                let stream = str.unwrap();
-                self.src.seek(SeekFrom::Start(offset))?;
-                let mut pkt = self.src.read_packet(stream, pts, false, size)?;
-                if let Some(ref pal) = track.pal {
-                    let side_data = NASideData::Palette(first, pal.clone());
-                    pkt.add_side_data(side_data);
-                }
-                return Ok(pkt);
+                return process_packet(&mut self.src, strmgr, track, pts, offset, size, first);
             }
         }
         Err(DemuxerError::EOF)
@@ -1604,6 +1647,7 @@ const DEMUXER_OPTIONS: &[NAOptionDefinition] = &[
 
 impl<'a> NAOptionHandler for MOVDemuxer<'a> {
     fn get_supported_options(&self) -> &[NAOptionDefinition] { DEMUXER_OPTIONS }
+    #[allow(clippy::single_match)]
     fn set_options(&mut self, options: &[NAOption]) {
         for option in options.iter() {
             for opt_def in DEMUXER_OPTIONS.iter() {