X-Git-Url: https://git.nihav.org/?a=blobdiff_plain;f=nihav-commonfmt%2Fsrc%2Fdemuxers%2Fmov.rs;h=fbe09170cfa5b35ec0b20b4007235dd0b8ba5763;hb=7afec34d769a340e73f9074f387c2b25a9b1d847;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..fbe0917 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) @@ -334,7 +335,7 @@ fn read_hdlr(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult let _comp_flags = br.read_u32be()?; let _comp_flags_mask = br.read_u32be()?; - if comp_type == mktag!(b"mhlr") { + if comp_type == mktag!(b"mhlr") || comp_type == 0 { if comp_subtype == mktag!(b"vide") { track.stream_type = StreamType::Video; } else if comp_subtype == mktag!(b"soun") { @@ -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 }, @@ -583,7 +584,7 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult let packet_size = br.read_u16be()? as usize; validate!(packet_size == 0); let sample_rate = br.read_u32be()?; - validate!(sample_rate > 0); + validate!(sample_rate > (1 << 16)); let cname = if let Some(name) = find_codec_from_mov_audio_fourcc(&fcc) { name } else if let (true, Some(name)) = ((fcc[0] == b'm' && fcc[1] == b's'), find_codec_from_wav_twocc(u16::from(fcc[2]) * 256 + u16::from(fcc[3]))) { @@ -591,8 +592,10 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult } else { "unknown" }; -//todo adjust format for various PCM kinds - let soniton = NASoniton::new(sample_size as u8, SONITON_FLAG_SIGNED | SONITON_FLAG_BE); + let mut soniton = NASoniton::new(sample_size as u8, SONITON_FLAG_SIGNED | SONITON_FLAG_BE); + if &fcc == b"raw " && sample_size == 8 { + soniton.signed = false; + } let block_align = 1; if sver == 1 { let samples_per_packet = br.read_u32be()?; @@ -601,9 +604,19 @@ fn read_stsd(track: &mut Track, br: &mut ByteReader, size: u64) -> DemuxerResult let _bytes_per_sample = br.read_u32be()?; track.bsize = bytes_per_frame as usize; track.frame_samples = samples_per_packet as usize; + track.tb_num = samples_per_packet; } else { - track.bsize = sample_size as usize; + track.bsize = (sample_size / 8) as usize; } + track.tb_den = sample_rate >> 16; + track.raw_audio = match &fcc { + b"NONE" | b"raw " | b"twos" | b"sowt" | + b"in24" | b"in32" | b"fl32" | b"fl64" | + b"ima4" | b"ms\x00\x02" | b"ms\x00\x21" | + b"alaw" | b"ulaw" | + b"MAC3" | b"MAC6" => true, + _ => false, + }; let ahdr = NAAudioInfo::new(sample_rate >> 16, nchannels as u8, soniton, block_align); let edata = parse_audio_edata(br, start_pos, size)?; codec_info = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata); @@ -621,11 +634,46 @@ 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, track.tb_num, 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 >= 8); + 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 == 0 { + } else 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 * stream.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 +765,11 @@ struct Track { track_id: u32, track_str_id: usize, track_no: u32, + tb_num: u32, tb_den: u32, + raw_audio: bool, + raw_apos: u64, + duration: u32, depth: u8, tkhd_found: bool, stsd_found: bool, @@ -731,6 +783,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 +793,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 +845,11 @@ impl Track { track_id: 0, track_str_id: 0, track_no, + tb_num: 1, tb_den, + raw_audio: false, + raw_apos: 0, + duration: 0, stream_type: StreamType::None, width: 0, height: 0, @@ -761,6 +860,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 +871,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 +882,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 +922,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 mut pts = NATimeInfo::new(Some(pts_val), None, None, self.tb_num, 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() { @@ -856,14 +955,34 @@ impl Track { self.samples_left -= 1; } else if self.frame_samples != 0 && self.bsize != 0 { let nblocks = size / self.bsize; + if self.raw_audio { + pts.pts = Some(self.raw_apos); + pts.duration = Some(nblocks as u64); + self.raw_apos += nblocks as u64; + } if nblocks > 0 { let consumed = (nblocks * self.frame_samples).min(self.samples_left); self.samples_left -= consumed; } else { self.samples_left = 0; } + } else if !self.raw_audio { + self.samples_left -= 1; } else { - self.samples_left = 0; + const BLOCK_SAMPLES: usize = 1024 * 6; // should be multiple of 64 and 6 to fit both IMA ADPCM and MACE 6:1 blocks + let max_size = self.calculate_chunk_size(BLOCK_SAMPLES); + let cur_size = self.calculate_chunk_size(self.samples_left); + let add_off = (size - cur_size) as u64; + let dsize = cur_size.min(max_size); + if self.samples_left >= BLOCK_SAMPLES { + self.cur_sample += BLOCK_SAMPLES; + self.samples_left -= BLOCK_SAMPLES; + self.last_offset -= size as u64; + } else { + self.cur_sample += self.samples_left; + self.samples_left = 0; + } + return Some((pts, offset + add_off, dsize)); } self.cur_sample += 1; Some((pts, offset, size)) @@ -886,11 +1005,81 @@ impl Track { self.bsize } } - fn seek(&mut self, pts: u64) { + fn seek(&mut self, pts: u64, tpoint: NATimePoint) -> DemuxerResult<()> { self.cur_sample = pts as usize; self.samples_left = 0; if self.stream_type == StreamType::Audio { - self.cur_chunk = self.cur_sample; + if let NATimePoint::Milliseconds(ms) = tpoint { + let exp_pts = NATimeInfo::time_to_ts(ms, 1000, self.tb_num, self.tb_den); + if self.raw_audio { + if self.frame_samples != 0 { + self.raw_apos = exp_pts / (self.frame_samples as u64); + let mut apos = 0; + self.cur_sample = 0; + self.cur_chunk = 0; + let mut cmap = self.sample_map.iter(); + let mut cur_samps = 0; + let (mut next_idx, mut next_samples) = cmap.next().unwrap(); + loop { + 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() { + next_idx = *new_idx; + next_samples = *new_samples; + } + } + self.raw_apos = apos; + apos += (cur_samps / self.frame_samples) as u64; + if apos > exp_pts { + if cur_samps == self.frame_samples || apos > exp_pts + 1 { + if self.cur_chunk >= self.chunk_offsets.len() { + return Err(DemuxerError::SeekError); + } + self.last_offset = self.chunk_offsets[self.cur_chunk]; + break; + } + } + self.cur_chunk += 1; + } + self.samples_left = cur_samps; + self.cur_chunk += 1; + } else { + self.raw_apos = exp_pts; + self.cur_sample = exp_pts as usize; + let mut csamp = 0; + self.cur_chunk = 0; + let mut cmap = self.sample_map.iter(); + let mut cur_samps = 0; + let (mut next_idx, mut next_samples) = cmap.next().unwrap(); + loop { + 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() { + next_idx = *new_idx; + next_samples = *new_samples; + } + } + csamp += cur_samps; + if csamp > self.cur_sample { + if self.cur_chunk >= self.chunk_offsets.len() { + return Err(DemuxerError::SeekError); + } + self.last_offset = self.chunk_offsets[self.cur_chunk]; + break; + } + self.cur_chunk += 1; + } + self.samples_left = csamp - self.cur_sample; + self.cur_chunk += 1; + } + } else { + self.cur_chunk = self.cur_sample; + } + } else { + self.cur_chunk = self.cur_sample; + } } else if self.chunk_offsets.len() != self.chunk_sizes.len() && !self.sample_map.is_empty() { let mut csamp = 0; self.cur_chunk = 0; @@ -898,7 +1087,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() { @@ -908,17 +1097,22 @@ impl Track { } csamp += cur_samps; if csamp >= self.cur_sample { + if self.cur_chunk >= self.chunk_offsets.len() { + return Err(DemuxerError::SeekError); + } self.last_offset = self.chunk_offsets[self.cur_chunk]; break; } 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; } + Ok(()) } } @@ -969,10 +1163,17 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { } let seek_info = ret.unwrap(); for track in self.tracks.iter_mut() { - track.seek(seek_info.pts); + track.seek(seek_info.pts, time)?; } 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> {