X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-commonfmt%2Fsrc%2Fdemuxers%2Fmov.rs;h=b271b95e3e777d4b4b31676c30d08293e1865118;hb=bbbf86dde30ec4f81e926b84ff58c7435da08b75;hp=0b6dd81074fa2cb06256d29ced593aa11fc50af3;hpb=237cc1f9576ed23c7a2f9bb34b43e5d07e93f710;p=nihav.git diff --git a/nihav-commonfmt/src/demuxers/mov.rs b/nihav-commonfmt/src/demuxers/mov.rs index 0b6dd81..b271b95 100644 --- a/nihav-commonfmt/src/demuxers/mov.rs +++ b/nihav-commonfmt/src/demuxers/mov.rs @@ -292,7 +292,7 @@ fn read_tkhd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult let _mtime = br.read_u32be()?; let track_id = br.read_u32be()?; br.read_skip(4)?; - let _duration = br.read_u32be()?; + let duration = br.read_u32be()?; br.read_skip(8)?; let _layer = br.read_u16be()?; let _alt_group = br.read_u16be()?; @@ -304,6 +304,7 @@ fn read_tkhd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult track.width = width >> 16; track.height = height >> 16; track.track_id = track_id; + track.duration = duration; track.tkhd_found = true; Ok(KNOWN_TKHD_SIZE) @@ -417,7 +418,7 @@ fn read_stbl(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult const STBL_CHUNK_HANDLERS: &[TrackChunkHandler] = &[ TrackChunkHandler { ctype: mktag!(b"stsd"), parse: read_stsd }, - TrackChunkHandler { ctype: mktag!(b"stts"), parse: skip_chunk }, + TrackChunkHandler { ctype: mktag!(b"stts"), parse: read_stts }, TrackChunkHandler { ctype: mktag!(b"stss"), parse: read_stss }, TrackChunkHandler { ctype: mktag!(b"stsc"), parse: read_stsc }, TrackChunkHandler { ctype: mktag!(b"stsz"), parse: read_stsz }, @@ -621,11 +622,45 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult }; let read_size = br.tell() - start_pos; validate!(read_size <= size); - track.stream = Some(NAStream::new(track.stream_type, track.track_no, codec_info, 1, track.tb_den)); + track.stream = Some(NAStream::new(track.stream_type, track.track_no, codec_info, 1, track.tb_den, u64::from(track.duration))); track.stsd_found = true; Ok(read_size) } +fn read_stts(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult { + validate!(size >= 16); + let start_pos = br.tell(); + let version = br.read_byte()?; + validate!(version == 0); + let _flags = br.read_u24be()?; + let entries = br.read_u32be()? as usize; + validate!(entries as u64 <= (size - 8) / 8); + if entries == 1 { + let _count = br.read_u32be()?; + let tb_num = br.read_u32be()?; + if let Some(ref mut stream) = track.stream { + let tb_den = stream.tb_den; + let (tb_num, tb_den) = reduce_timebase(tb_num, tb_den); + stream.duration /= u64::from(stream.tb_den / tb_den); + stream.tb_num = tb_num; + stream.tb_den = tb_den; + track.tb_num = tb_num; + track.tb_den = tb_den; + } + } else { + track.time_to_sample.truncate(0); + track.time_to_sample.reserve(entries); + for _ in 0..entries { + let count = br.read_u32be()?; + let mult = br.read_u32be()?; + track.time_to_sample.push((count, mult)); + } + } + let read_size = br.tell() - start_pos; + validate!(read_size <= size); + Ok(read_size) +} + fn read_stss(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult { let version = br.read_byte()?; validate!(version == 0); @@ -717,7 +752,9 @@ struct Track { track_id: u32, track_str_id: usize, track_no: u32, + tb_num: u32, tb_den: u32, + duration: u32, depth: u8, tkhd_found: bool, stsd_found: bool, @@ -731,6 +768,7 @@ struct Track { keyframes: Vec, chunk_sizes: Vec, chunk_offsets: Vec, + time_to_sample: Vec<(u32, u32)>, sample_map: Vec<(u32, u32)>, sample_size: u32, frame_samples: usize, @@ -740,6 +778,48 @@ struct Track { samples_left: usize, last_offset: u64, pal: Option>, + timesearch: TimeSearcher, +} + +#[derive(Default)] +struct TimeSearcher { + idx: usize, + base: u64, + sbase: u32, + cur_len: u32, + cur_mul: u32, +} + +impl TimeSearcher { + fn new() -> Self { Self::default() } + fn reset(&mut self) { + *self = Self::default(); + } + fn map_time(&mut self, sample: u32, tts: &Vec<(u32, u32)>) -> u64 { + if tts.is_empty() { + u64::from(sample) + } else if sample >= self.sbase { + let mut sample = sample - self.sbase; + if self.idx == 0 { + let (cur_len, cur_mul) = tts[0]; + self.cur_len = cur_len; + self.cur_mul = cur_mul; + self.idx += 1; + } + while self.idx < tts.len() && sample > self.cur_len { + sample -= self.cur_len; + self.sbase += self.cur_len; + self.base += u64::from(self.cur_len) * u64::from(self.cur_mul); + self.cur_len = tts[self.idx].0; + self.cur_mul = tts[self.idx].1; + self.idx += 1; + } + self.base + u64::from(sample) * u64::from(self.cur_mul) + } else { + self.reset(); + self.map_time(sample, tts) + } + } } impl Track { @@ -750,7 +830,9 @@ impl Track { track_id: 0, track_str_id: 0, track_no, + tb_num: 1, tb_den, + duration: 0, stream_type: StreamType::None, width: 0, height: 0, @@ -761,6 +843,7 @@ impl Track { keyframes: Vec::new(), chunk_sizes: Vec::new(), chunk_offsets: Vec::new(), + time_to_sample: Vec::new(), sample_map: Vec::new(), sample_size: 0, frame_samples: 0, @@ -771,6 +854,7 @@ impl Track { samples_left: 0, last_offset: 0, pal: None, + timesearch: TimeSearcher::new(), } } read_chunk_list!(track; "trak", read_trak, TRAK_CHUNK_HANDLERS); @@ -781,14 +865,11 @@ impl Track { if !self.keyframes.is_empty() { seek_index.mode = SeekIndexMode::Present; } + let mut tsearch = TimeSearcher::new(); for kf_time in self.keyframes.iter() { - let pts = u64::from(*kf_time - 1); - let time = NATimeInfo::ts_to_time(pts, 1000, 1, self.tb_den); - let idx = (*kf_time - 1) as usize; - if idx < self.chunk_offsets.len() { - let pos = self.chunk_offsets[idx]; - seek_index.add_entry(self.track_no as u32, SeekEntry { time, pts, pos }); - } + let pts = tsearch.map_time(*kf_time - 1, &self.time_to_sample); + let time = NATimeInfo::ts_to_time(pts, 1000, self.tb_num, self.tb_den); + seek_index.add_entry(self.track_no as u32, SeekEntry { time, pts: u64::from(*kf_time - 1), pos: 0 }); } } fn calculate_chunk_size(&self, nsamp: usize) -> usize { @@ -824,7 +905,8 @@ impl Track { } } fn get_next_chunk(&mut self) -> Option<(NATimeInfo, u64, usize)> { - let pts = NATimeInfo::new(Some(self.cur_sample as u64), None, None, 1, self.tb_den); + let pts_val = self.timesearch.map_time(self.cur_sample as u32, &self.time_to_sample); + let pts = NATimeInfo::new(Some(pts_val), None, None, 1, self.tb_den); //todo dts decoding if self.chunk_offsets.len() == self.chunk_sizes.len() { // simple one-to-one mapping if self.cur_sample >= self.chunk_sizes.len() { @@ -898,7 +980,7 @@ impl Track { let mut cur_samps = 0; let (mut next_idx, mut next_samples) = cmap.next().unwrap(); loop { - if self.cur_chunk == next_idx as usize { + if self.cur_chunk + 1 == next_idx as usize { self.samples_left = cur_samps; cur_samps = next_samples as usize; if let Some((new_idx, new_samples)) = cmap.next() { @@ -914,10 +996,11 @@ impl Track { self.cur_chunk += 1; } csamp -= cur_samps; - for sample_no in csamp..self.cur_chunk { + for sample_no in csamp..self.cur_sample { self.last_offset += self.get_size(sample_no) as u64; } - self.samples_left = self.cur_sample - csamp - cur_samps; + self.samples_left = csamp + cur_samps - self.cur_sample; + self.cur_chunk += 1; } } } @@ -973,6 +1056,13 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { } Ok(()) } + fn get_duration(&self) -> u64 { + if self.tb_den != 0 { + u64::from(self.duration) * 1000 / u64::from(self.tb_den) + } else { + 0 + } + } } impl<'a> NAOptionHandler for MOVDemuxer<'a> {