From a480a0de101483d802a11e72d758dae00fa4860a Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Sun, 4 Oct 2020 11:53:21 +0200 Subject: [PATCH] introduce stream and container duration --- nihav-commonfmt/src/codecs/cinepakenc.rs | 2 +- nihav-commonfmt/src/codecs/pcm.rs | 2 +- nihav-commonfmt/src/demuxers/avi.rs | 11 ++++-- nihav-commonfmt/src/demuxers/mov.rs | 14 ++++++- nihav-commonfmt/src/demuxers/wav.rs | 22 +++++++++-- nihav-core/src/demuxers/mod.rs | 21 ++++++++++ nihav-core/src/frame.rs | 8 +++- nihav-game/src/demuxers/bmv.rs | 10 +++-- nihav-game/src/demuxers/gdv.rs | 5 ++- nihav-game/src/demuxers/vmd.rs | 6 ++- nihav-game/src/demuxers/vx.rs | 6 ++- nihav-llaudio/src/demuxers/ape.rs | 3 +- nihav-llaudio/src/demuxers/flac.rs | 3 +- nihav-llaudio/src/demuxers/tta.rs | 3 +- nihav-llaudio/src/demuxers/wavpack.rs | 3 +- nihav-ms/src/codecs/msadpcm.rs | 2 +- nihav-ms/src/codecs/msvideo1enc.rs | 2 +- nihav-rad/src/demuxers/bink.rs | 5 ++- nihav-rad/src/demuxers/smacker.rs | 5 ++- nihav-realmedia/src/demuxers/realmedia.rs | 47 ++++++++++++++++------- nihav-vivo/src/demuxers/vivo.rs | 12 +++++- 21 files changed, 144 insertions(+), 48 deletions(-) diff --git a/nihav-commonfmt/src/codecs/cinepakenc.rs b/nihav-commonfmt/src/codecs/cinepakenc.rs index a7cf8c0..d81d893 100644 --- a/nihav-commonfmt/src/codecs/cinepakenc.rs +++ b/nihav-commonfmt/src/codecs/cinepakenc.rs @@ -944,7 +944,7 @@ impl NAEncoder for CinepakEncoder { let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format); let info = NACodecInfo::new("cinepak", NACodecTypeInfo::Video(out_info), None); - let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den); + let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); stream.set_num(stream_id as usize); let stream = stream.into_ref(); diff --git a/nihav-commonfmt/src/codecs/pcm.rs b/nihav-commonfmt/src/codecs/pcm.rs index 43c897a..46b1992 100644 --- a/nihav-commonfmt/src/codecs/pcm.rs +++ b/nihav-commonfmt/src/codecs/pcm.rs @@ -186,7 +186,7 @@ impl NAEncoder for PCMEncoder { NACodecTypeInfo::Video(_) => Err(EncoderError::FormatError), NACodecTypeInfo::Audio(_) => { let info = NACodecInfo::new("pcm", encinfo.format, None); - let mut stream = NAStream::new(StreamType::Audio, stream_id, info, encinfo.tb_num, encinfo.tb_den); + let mut stream = NAStream::new(StreamType::Audio, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); stream.set_num(stream_id as usize); let stream = stream.into_ref(); self.stream = Some(stream.clone()); diff --git a/nihav-commonfmt/src/demuxers/avi.rs b/nihav-commonfmt/src/demuxers/avi.rs index a5e52a7..eb927e7 100644 --- a/nihav-commonfmt/src/demuxers/avi.rs +++ b/nihav-commonfmt/src/demuxers/avi.rs @@ -52,6 +52,7 @@ struct AVIDemuxer<'a> { sstate: StreamState, tb_num: u32, tb_den: u32, + strm_duration: u32, key_offs: Vec, pal: Vec, } @@ -152,6 +153,7 @@ impl<'a> DemuxCore<'a> for AVIDemuxer<'a> { Ok(()) } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for AVIDemuxer<'a> { @@ -173,6 +175,7 @@ impl<'a> AVIDemuxer<'a> { sstate: StreamState::new(), tb_num: 0, tb_den: 0, + strm_duration: 0, key_offs: Vec::new(), pal: Vec::new(), } @@ -360,7 +363,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 @@ -437,7 +440,7 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize 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) @@ -467,7 +470,7 @@ fn parse_strf_auds(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize 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) @@ -476,7 +479,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 { 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) diff --git a/nihav-commonfmt/src/demuxers/mov.rs b/nihav-commonfmt/src/demuxers/mov.rs index 0b6dd81..9236baa 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) @@ -621,7 +622,7 @@ 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) } @@ -718,6 +719,7 @@ struct Track { track_str_id: usize, track_no: u32, tb_den: u32, + duration: u32, depth: u8, tkhd_found: bool, stsd_found: bool, @@ -751,6 +753,7 @@ impl Track { track_str_id: 0, track_no, tb_den, + duration: 0, stream_type: StreamType::None, width: 0, height: 0, @@ -973,6 +976,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> { diff --git a/nihav-commonfmt/src/demuxers/wav.rs b/nihav-commonfmt/src/demuxers/wav.rs index fb522cd..2164619 100644 --- a/nihav-commonfmt/src/demuxers/wav.rs +++ b/nihav-commonfmt/src/demuxers/wav.rs @@ -19,6 +19,7 @@ struct WAVDemuxer<'a> { block_size: usize, is_pcm: bool, avg_bytes: u32, + duration: u64, } impl<'a> DemuxCore<'a> for WAVDemuxer<'a> { @@ -33,7 +34,7 @@ impl<'a> DemuxCore<'a> for WAVDemuxer<'a> { seek_index.mode = SeekIndexMode::Automatic; let mut fmt_parsed = false; - let mut _duration = 0; + let mut duration = 0; while self.src.tell() < riff_end { let ctype = self.src.read_tag()?; let csize = self.src.read_u32le()? as usize; @@ -45,12 +46,21 @@ impl<'a> DemuxCore<'a> for WAVDemuxer<'a> { }, b"fact" => { validate!(csize == 4); - _duration = self.src.read_u32le()? as usize; + duration = self.src.read_u32le()? as usize; }, b"data" => { validate!(fmt_parsed); self.data_pos = self.src.tell(); self.data_end = self.data_pos + (csize as u64); + + if duration != 0 { + self.duration = (duration as u64) * 1000 / u64::from(self.srate); + } else if self.avg_bytes > 0 { + self.duration = (self.data_end - self.data_pos) * 1000 / u64::from(self.avg_bytes); + } else { + self.duration = 0; + } + return Ok(()); }, _ => { @@ -105,6 +115,8 @@ impl<'a> DemuxCore<'a> for WAVDemuxer<'a> { Err(DemuxerError::NotImplemented) } } + + fn get_duration(&self) -> u64 { self.duration } } impl<'a> NAOptionHandler for WAVDemuxer<'a> { @@ -123,6 +135,7 @@ impl<'a> WAVDemuxer<'a> { block_size: 0, is_pcm: false, avg_bytes: 0, + duration: 0, } } fn parse_fmt(&mut self, strmgr: &mut StreamManager, csize: usize) -> DemuxerResult<()> { @@ -165,13 +178,16 @@ impl<'a> WAVDemuxer<'a> { }; let ahdr = NAAudioInfo::new(samples_per_sec, channels as u8, soniton, block_align); let ainfo = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), edata); - let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, 1, samples_per_sec)); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, 1, samples_per_sec, 0)); if res.is_none() { return Err(MemoryError); } self.srate = samples_per_sec; self.block_size = block_align; self.avg_bytes = avg_bytes_per_sec; self.is_pcm = cname == "pcm"; + if self.is_pcm && self.avg_bytes == 0 { + self.avg_bytes = self.block_size as u32 * self.srate; + } Ok(()) } diff --git a/nihav-core/src/demuxers/mod.rs b/nihav-core/src/demuxers/mod.rs index b54e563..dfcd118 100644 --- a/nihav-core/src/demuxers/mod.rs +++ b/nihav-core/src/demuxers/mod.rs @@ -38,6 +38,8 @@ pub trait DemuxCore<'a>: NAOptionHandler { fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult; /// Seeks to the requested time. fn seek(&mut self, time: NATimePoint, seek_idx: &SeekIndex) -> DemuxerResult<()>; + /// Returns container duration in milliseconds (zero if not available). + fn get_duration(&self) -> u64; } /// An auxiliary trait to make bytestream reader read packet data. @@ -415,6 +417,25 @@ impl<'a> Demuxer<'a> { pub fn get_seek_index(&self) -> &SeekIndex { &self.seek_idx } + /// Returns media duration reported by container or its streams. + /// + /// Duration is in milliseconds and set to zero when it is not available. + pub fn get_duration(&self) -> u64 { + let duration = self.dmx.get_duration(); + if duration != 0 { + return duration; + } + let mut duration = 0; + for stream in self.streams.iter() { + if stream.duration > 0 { + let dur = NATimeInfo::ts_to_time(stream.duration, 1000, stream.tb_num, stream.tb_den); + if duration < dur { + duration = dur; + } + } + } + duration + } } impl<'a> NAOptionHandler for Demuxer<'a> { diff --git a/nihav-core/src/frame.rs b/nihav-core/src/frame.rs index fbccfbe..8c58e19 100644 --- a/nihav-core/src/frame.rs +++ b/nihav-core/src/frame.rs @@ -1289,6 +1289,8 @@ pub struct NAStream { pub tb_num: u32, /// Timebase denominator. pub tb_den: u32, + /// Duration in timebase units (zero if not available). + pub duration: u64, } /// A specialised reference-counted `NAStream` type. @@ -1312,9 +1314,9 @@ pub fn reduce_timebase(tb_num: u32, tb_den: u32) -> (u32, u32) { impl NAStream { /// Constructs a new `NAStream` instance. - pub fn new(mt: StreamType, id: u32, info: NACodecInfo, tb_num: u32, tb_den: u32) -> Self { + pub fn new(mt: StreamType, id: u32, info: NACodecInfo, tb_num: u32, tb_den: u32, duration: u64) -> Self { let (n, d) = reduce_timebase(tb_num, tb_den); - NAStream { media_type: mt, id, num: 0, info: info.into_ref(), tb_num: n, tb_den: d } + NAStream { media_type: mt, id, num: 0, info: info.into_ref(), tb_num: n, tb_den: d, duration } } /// Returns stream id. pub fn get_id(&self) -> u32 { self.id } @@ -1334,6 +1336,8 @@ impl NAStream { self.tb_num = n; self.tb_den = d; } + /// Returns stream duration. + pub fn get_duration(&self) -> usize { self.num } /// Converts current instance into a reference-counted one. pub fn into_ref(self) -> NAStreamRef { Arc::new(self) } } diff --git a/nihav-game/src/demuxers/bmv.rs b/nihav-game/src/demuxers/bmv.rs index 83f75b1..f548fdc 100644 --- a/nihav-game/src/demuxers/bmv.rs +++ b/nihav-game/src/demuxers/bmv.rs @@ -18,11 +18,11 @@ impl<'a> DemuxCore<'a> for BMVDemuxer<'a> { let vhdr = NAVideoInfo::new(640, 429, false, PAL8_FORMAT); let vci = NACodecTypeInfo::Video(vhdr); let vinfo = NACodecInfo::new("bmv-video", vci, None); - self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 12)).unwrap(); + self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 12, 0)).unwrap(); let ahdr = NAAudioInfo::new(22050, 2, SND_S16_FORMAT, 1); let ainfo = NACodecInfo::new("bmv-audio", NACodecTypeInfo::Audio(ahdr), None); - self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 22050)).unwrap(); + self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 22050, 0)).unwrap(); self.vpos = 0; self.apos = 0; @@ -73,6 +73,7 @@ impl<'a> DemuxCore<'a> for BMVDemuxer<'a> { fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { Err(DemuxerError::NotPossible) } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for BMVDemuxer<'a> { @@ -151,11 +152,11 @@ impl<'a> DemuxCore<'a> for BMV3Demuxer<'a> { let vhdr = NAVideoInfo::new(width, height, false, RGB565_FORMAT); let vci = NACodecTypeInfo::Video(vhdr); let vinfo = NACodecInfo::new("bmv3-video", vci, None); - self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 256, fps)).unwrap(); + self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 256, fps, nframes as u64)).unwrap(); let ahdr = NAAudioInfo::new(22050, 2, SND_S16_FORMAT, audio_blob_size); let ainfo = NACodecInfo::new("bmv3-audio", NACodecTypeInfo::Audio(ahdr), None); - self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 22050)).unwrap(); + self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, 22050, 0)).unwrap(); self.vpos = 0; self.apos = 0; @@ -221,6 +222,7 @@ impl<'a> DemuxCore<'a> for BMV3Demuxer<'a> { fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { Err(DemuxerError::NotPossible) } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for BMV3Demuxer<'a> { diff --git a/nihav-game/src/demuxers/gdv.rs b/nihav-game/src/demuxers/gdv.rs index af0f857..778329a 100644 --- a/nihav-game/src/demuxers/gdv.rs +++ b/nihav-game/src/demuxers/gdv.rs @@ -81,7 +81,7 @@ impl<'a> DemuxCore<'a> for GremlinVideoDemuxer<'a> { let vhdr = NAVideoInfo::new(width as usize, height as usize, false, PAL8_FORMAT); let vci = NACodecTypeInfo::Video(vhdr); let vinfo = NACodecInfo::new("gdv-video", vci, if edata.is_empty() { None } else { Some(edata) }); - self.v_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, u32::from(fps))); + self.v_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, u32::from(fps), u64::from(frames))); } if (aflags & 1) != 0 { let channels = if (aflags & 2) != 0 { 2 } else { 1 }; @@ -91,7 +91,7 @@ impl<'a> DemuxCore<'a> for GremlinVideoDemuxer<'a> { let ahdr = NAAudioInfo::new(u32::from(rate), channels as u8, if depth == 16 { SND_S16_FORMAT } else { SND_U8_FORMAT }, 2); let ainfo = NACodecInfo::new(if packed != 0 { "gdv-audio" } else { "pcm" }, NACodecTypeInfo::Audio(ahdr), None); - self.a_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, u32::from(rate))); + self.a_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, u32::from(rate), 0)); self.asize = (((rate / fps) * channels * (depth / 8)) >> packed) as usize; self.apacked = (aflags & 8) != 0; @@ -113,6 +113,7 @@ impl<'a> DemuxCore<'a> for GremlinVideoDemuxer<'a> { fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { Err(DemuxerError::NotPossible) } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for GremlinVideoDemuxer<'a> { fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } diff --git a/nihav-game/src/demuxers/vmd.rs b/nihav-game/src/demuxers/vmd.rs index f693e73..a2c93f6 100644 --- a/nihav-game/src/demuxers/vmd.rs +++ b/nihav-game/src/demuxers/vmd.rs @@ -61,7 +61,7 @@ impl<'a> DemuxCore<'a> for VMDDemuxer<'a> { let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT); let vci = NACodecTypeInfo::Video(vhdr); let vinfo = NACodecInfo::new(if !self.is_indeo { "vmd-video" } else { "indeo3" }, vci, Some(edata)); - self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 12)).unwrap(); + self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 12, nframes as u64)).unwrap(); let is_ext_audio = (hdr_size & 0xF) == 4; let ext_audio_id = if is_ext_audio { @@ -100,7 +100,7 @@ impl<'a> DemuxCore<'a> for VMDDemuxer<'a> { } }; let ainfo = NACodecInfo::new(ac_name, NACodecTypeInfo::Audio(ahdr), Some(aedata)); - self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate)).unwrap(); + self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate, 0)).unwrap(); } else { block_size = 0; } @@ -183,6 +183,8 @@ impl<'a> DemuxCore<'a> for VMDDemuxer<'a> { fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { Err(DemuxerError::NotPossible) } + + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for VMDDemuxer<'a> { diff --git a/nihav-game/src/demuxers/vx.rs b/nihav-game/src/demuxers/vx.rs index cfc44e0..5d2786d 100644 --- a/nihav-game/src/demuxers/vx.rs +++ b/nihav-game/src/demuxers/vx.rs @@ -47,7 +47,7 @@ impl<'a> DemuxCore<'a> for VXDemuxer<'a> { let vci = NACodecTypeInfo::Video(vhdr); let edata = [fps as u8].to_vec(); let vinfo = NACodecInfo::new("vxvideo", vci, Some(edata)); - self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, fps)).unwrap(); + self.vid_id = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, fps, nframes as u64)).unwrap(); if num_audio_tracks != 0 { validate!(audio_off + ((num_audio_tracks * AUDIO_EXTRADATA_LEN) as u64) == vinfo_off); @@ -56,7 +56,7 @@ impl<'a> DemuxCore<'a> for VXDemuxer<'a> { src.read_buf(edata.as_mut_slice())?; let ahdr = NAAudioInfo::new(srate, 1, SND_S16P_FORMAT, 1); let ainfo = NACodecInfo::new("vxaudio", NACodecTypeInfo::Audio(ahdr), Some(edata)); - self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate)).unwrap(); + self.aud_id = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, 1, srate, 0)).unwrap(); self.num_afrm = nframes as u64; self.ano = 0; self.num_aud = num_audio_tracks; @@ -98,6 +98,8 @@ impl<'a> DemuxCore<'a> for VXDemuxer<'a> { fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> { Err(DemuxerError::NotPossible) } + + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for VXDemuxer<'a> { diff --git a/nihav-llaudio/src/demuxers/ape.rs b/nihav-llaudio/src/demuxers/ape.rs index f496534..10ebc07 100644 --- a/nihav-llaudio/src/demuxers/ape.rs +++ b/nihav-llaudio/src/demuxers/ape.rs @@ -178,7 +178,7 @@ impl<'a> DemuxCore<'a> for APEDemuxer<'a> { let ahdr = NAAudioInfo::new(srate, channels as u8, SND_S16P_FORMAT, 1); let ainfo = NACodecInfo::new("ape", NACodecTypeInfo::Audio(ahdr), Some(hdr)); - strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, blocksperframe, srate)).unwrap(); + strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, blocksperframe, srate, nframes as u64)).unwrap(); self.cur_frame = 0; @@ -223,6 +223,7 @@ impl<'a> DemuxCore<'a> for APEDemuxer<'a> { Ok(()) } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for APEDemuxer<'a> { diff --git a/nihav-llaudio/src/demuxers/flac.rs b/nihav-llaudio/src/demuxers/flac.rs index 3d6bca2..16f8388 100644 --- a/nihav-llaudio/src/demuxers/flac.rs +++ b/nihav-llaudio/src/demuxers/flac.rs @@ -177,7 +177,7 @@ impl<'a> DemuxCore<'a> for FLACDemuxer<'a> { let base = if self.blk_samples != 0 { u32::from(self.blk_samples) } else { 1 }; let ahdr = NAAudioInfo::new(srate, channels as u8, SND_S16P_FORMAT, base as usize); let ainfo = NACodecInfo::new("flac", NACodecTypeInfo::Audio(ahdr), Some(streaminfo)); - strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, base, srate)).unwrap(); + strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, base, srate, 0)).unwrap(); Ok(()) } @@ -239,6 +239,7 @@ impl<'a> DemuxCore<'a> for FLACDemuxer<'a> { Err(DemuxerError::NotPossible) } } + fn get_duration(&self) -> u64 { self.tot_samples * 1000 / u64::from(self.srate) } } impl<'a> NAOptionHandler for FLACDemuxer<'a> { diff --git a/nihav-llaudio/src/demuxers/tta.rs b/nihav-llaudio/src/demuxers/tta.rs index 97ee808..973ff84 100644 --- a/nihav-llaudio/src/demuxers/tta.rs +++ b/nihav-llaudio/src/demuxers/tta.rs @@ -62,7 +62,7 @@ impl<'a> DemuxCore<'a> for TTADemuxer<'a> { let ahdr = NAAudioInfo::new(srate, channels as u8, SND_S16P_FORMAT, 1); let ainfo = NACodecInfo::new("tta", NACodecTypeInfo::Audio(ahdr), Some(hdr)); - strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, self.framelen, srate)).unwrap(); + strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, self.framelen, srate, u64::from(self.nframes))).unwrap(); self.cur_frame = 0; @@ -96,6 +96,7 @@ impl<'a> DemuxCore<'a> for TTADemuxer<'a> { Ok(()) } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for TTADemuxer<'a> { diff --git a/nihav-llaudio/src/demuxers/wavpack.rs b/nihav-llaudio/src/demuxers/wavpack.rs index d07a5a2..9cd50fa 100644 --- a/nihav-llaudio/src/demuxers/wavpack.rs +++ b/nihav-llaudio/src/demuxers/wavpack.rs @@ -174,7 +174,7 @@ impl<'a> DemuxCore<'a> for WavPackDemuxer<'a> { let ahdr = NAAudioInfo::new(srate, channels, SND_S16P_FORMAT, 1); let ainfo = NACodecInfo::new("wavpack", NACodecTypeInfo::Audio(ahdr), Some(buf.clone())); - strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, 1, srate)).unwrap(); + strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, 1, srate, hdr.tot_samples)).unwrap(); seek_index.mode = SeekIndexMode::Automatic; self.srate = srate; self.known_frames = Vec::with_capacity(((self.nsamples + u64::from(srate) - 1) / u64::from(srate)) as usize); @@ -253,6 +253,7 @@ impl<'a> DemuxCore<'a> for WavPackDemuxer<'a> { Err(DemuxerError::NotPossible) } } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for WavPackDemuxer<'a> { diff --git a/nihav-ms/src/codecs/msadpcm.rs b/nihav-ms/src/codecs/msadpcm.rs index 44baedb..19e8146 100644 --- a/nihav-ms/src/codecs/msadpcm.rs +++ b/nihav-ms/src/codecs/msadpcm.rs @@ -347,7 +347,7 @@ impl NAEncoder for MSADPCMEncoder { let soniton = NASoniton::new(4, 0); let out_ainfo = NAAudioInfo::new(ainfo.sample_rate, ainfo.channels, soniton, Self::calc_block_size(self.block_len, self.channels)); let info = NACodecInfo::new("ms-adpcm", NACodecTypeInfo::Audio(out_ainfo), None); - let mut stream = NAStream::new(StreamType::Audio, stream_id, info.clone(), self.block_len as u32, ainfo.sample_rate); + let mut stream = NAStream::new(StreamType::Audio, stream_id, info.clone(), self.block_len as u32, ainfo.sample_rate, 0); stream.set_num(stream_id as usize); let stream = stream.into_ref(); diff --git a/nihav-ms/src/codecs/msvideo1enc.rs b/nihav-ms/src/codecs/msvideo1enc.rs index b2233c2..ff6189b 100644 --- a/nihav-ms/src/codecs/msvideo1enc.rs +++ b/nihav-ms/src/codecs/msvideo1enc.rs @@ -432,7 +432,7 @@ impl NAEncoder for MSVideo1Encoder { let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, RGB555_FORMAT); let info = NACodecInfo::new("msvideo1", NACodecTypeInfo::Video(out_info), None); - let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den); + let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); stream.set_num(stream_id as usize); let stream = stream.into_ref(); if self.pool.prealloc_video(out_info, 2).is_err() { diff --git a/nihav-rad/src/demuxers/bink.rs b/nihav-rad/src/demuxers/bink.rs index 454290f..83da6ac 100644 --- a/nihav-rad/src/demuxers/bink.rs +++ b/nihav-rad/src/demuxers/bink.rs @@ -20,7 +20,7 @@ impl AudioTrack { let mut edata: Vec = Vec::with_capacity(4); edata.extend_from_slice(&magic); let ainfo = NACodecInfo::new(codecname, NACodecTypeInfo::Audio(ahdr), Some(edata)); - let res = strmgr.add_stream(NAStream::new(StreamType::Audio, (str_id + 1) as u32, ainfo, 1, srate)); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, (str_id + 1) as u32, ainfo, 1, srate, 0)); validate!(res.is_some()); let id = res.unwrap(); Ok(Self{ id }) @@ -78,7 +78,7 @@ impl<'a> DemuxCore<'a> for BinkDemuxer<'a> { let vhdr = NAVideoInfo::new(width, height, false, YUV420_FORMAT); let codec = if magic[0] == b'K' && magic[1] == b'B' && magic[2] == b'2' { "bink2-video" } else { "bink-video" }; let vinfo = NACodecInfo::new(codec, NACodecTypeInfo::Video(vhdr), Some(edata)); - let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, self.tb_num, self.tb_den)); + let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, self.tb_num, self.tb_den, self.frames as u64)); validate!(res.is_some()); self.video_id = res.unwrap(); @@ -163,6 +163,7 @@ impl<'a> DemuxCore<'a> for BinkDemuxer<'a> { self.cur_frame = seek_info.pts as usize; Ok(()) } + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for BinkDemuxer<'a> { diff --git a/nihav-rad/src/demuxers/smacker.rs b/nihav-rad/src/demuxers/smacker.rs index 5580a87..340af85 100644 --- a/nihav-rad/src/demuxers/smacker.rs +++ b/nihav-rad/src/demuxers/smacker.rs @@ -56,7 +56,7 @@ impl AudioTrack { let soniton = if (self.flags & SMK_AUD_FLAG_16BIT) != 0 { SND_S16_FORMAT } else { SND_U8_FORMAT }; let ahdr = NAAudioInfo::new(self.srate, channels, soniton, 1); let ainfo = NACodecInfo::new(codecname, NACodecTypeInfo::Audio(ahdr), None); - let res = strmgr.add_stream(NAStream::new(StreamType::Audio, (str_id + 1) as u32, ainfo, 1, self.srate)); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, (str_id + 1) as u32, ainfo, 1, self.srate, 0)); validate!(res.is_some()); self.id = res.unwrap(); @@ -149,7 +149,7 @@ impl<'a> DemuxCore<'a> for SmackerVideoDemuxer<'a> { let vhdr = NAVideoInfo::new(width, height, false, PAL8_FORMAT); let vinfo = NACodecInfo::new("smacker-video", NACodecTypeInfo::Video(vhdr), Some(treedata)); - let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 100000)); + let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 100000, 0)); validate!(res.is_some()); self.video_id = res.unwrap(); @@ -267,6 +267,7 @@ impl<'a> DemuxCore<'a> for SmackerVideoDemuxer<'a> { } Err(DemuxerError::NotImplemented) } + fn get_duration(&self) -> u64 { self.frames as u64 * self.pts_inc / 100 } } impl<'a> NAOptionHandler for SmackerVideoDemuxer<'a> { diff --git a/nihav-realmedia/src/demuxers/realmedia.rs b/nihav-realmedia/src/demuxers/realmedia.rs index b37c34f..74581d9 100644 --- a/nihav-realmedia/src/demuxers/realmedia.rs +++ b/nihav-realmedia/src/demuxers/realmedia.rs @@ -412,7 +412,7 @@ fn read_multiple_frame(src: &mut ByteReader, stream: NAStreamRef, keyframe: bool struct RMDemuxCommon {} impl RMDemuxCommon { - fn parse_stream_info(str_data: &mut CommonStreamData, strmgr: &mut StreamManager, stream_no: u32, edata: &[u8]) -> DemuxerResult { + fn parse_stream_info(str_data: &mut CommonStreamData, strmgr: &mut StreamManager, stream_no: u32, edata: &[u8], duration: u32) -> DemuxerResult { let mut is_mlti = false; let mut mr = MemoryReader::new_read(edata); let mut src = ByteReader::new(&mut mr); @@ -420,9 +420,9 @@ impl RMDemuxCommon { let tag2 = src.peek_u32be()?; //println!("tag1 {:X} tag2 {:X}", tag, tag2); if tag == mktag!('.', 'r', 'a', 0xFD) { - Self::parse_audio_stream(strmgr, &mut str_data.streams, stream_no, &mut src, edata)?; + Self::parse_audio_stream(strmgr, &mut str_data.streams, stream_no, &mut src, edata, duration)?; } else if ((tag2 == mktag!('V', 'I', 'D', 'O')) || (tag2 == mktag!('I', 'M', 'A', 'G'))) && ((tag as usize) <= edata.len()) { - Self::parse_video_stream(strmgr, &mut str_data.streams, stream_no, &mut src, edata, tag2)?; + Self::parse_video_stream(strmgr, &mut str_data.streams, stream_no, &mut src, edata, tag2, duration)?; } else if tag == mktag!(b"LSD:") { let extradata = Some(edata.to_owned()); @@ -435,7 +435,7 @@ impl RMDemuxCommon { let soniton = NASoniton::new(samp_size as u8, SONITON_FLAG_SIGNED); let ahdr = NAAudioInfo::new(sample_rate, channels as u8, soniton, 1); let nainfo = NACodecInfo::new("ralf", NACodecTypeInfo::Audio(ahdr), extradata); - let res = strmgr.add_stream(NAStream::new(StreamType::Audio, stream_no as u32, nainfo, 1, 1000)); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, stream_no as u32, nainfo, 1, 1000, u64::from(duration))); if res.is_none() { return Err(MemoryError); } let astr = RMAudioStream::new(None); str_data.streams.push(RMStreamType::Audio(astr)); @@ -467,9 +467,9 @@ impl RMDemuxCommon { let stream_no = str_data.mlti_mapper.get_substream_no(); //todo check that all substreams are of the same type"); if tag == mktag!('.', 'r', 'a', 0xFD) { - Self::parse_audio_stream(strmgr, &mut str_data.streams, stream_no, &mut hsrc, hdrsrc)?; + Self::parse_audio_stream(strmgr, &mut str_data.streams, stream_no, &mut hsrc, hdrsrc, duration)?; } else if (tag2 == mktag!('V', 'I', 'D', 'O')) && ((tag as usize) <= hdr_size) { - Self::parse_video_stream(strmgr, &mut str_data.streams, stream_no, &mut hsrc, hdrsrc, tag2)?; + Self::parse_video_stream(strmgr, &mut str_data.streams, stream_no, &mut hsrc, hdrsrc, tag2, duration)?; } else { println!("unknown MLTI substream {:08X} / {:08X}", tag, tag2); return Err(DemuxerError::InvalidData); @@ -482,7 +482,7 @@ println!("unknown MLTI substream {:08X} / {:08X}", tag, tag2); } Ok(is_mlti) } - fn parse_audio_stream(strmgr: &mut StreamManager, streams: &mut Vec, stream_no: u32, src: &mut ByteReader, edata_: &[u8]) -> DemuxerResult<()> { + fn parse_audio_stream(strmgr: &mut StreamManager, streams: &mut Vec, stream_no: u32, src: &mut ByteReader, edata_: &[u8], duration: u32) -> DemuxerResult<()> { let ver = src.read_u16be()?; let ainfo = match ver { 3 => { @@ -516,8 +516,9 @@ println!(" got ainfo {:?}", ainfo); let eslice = &edata_[(src.tell() as usize)..]; Some(eslice.to_vec()) }; + let duration = if duration == 0 { ainfo.get_duration(1000) } else { u64::from(duration) }; let nainfo = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), extradata); - let res = strmgr.add_stream(NAStream::new(StreamType::Audio, stream_no as u32, nainfo, 1, 1000)); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, stream_no as u32, nainfo, 1, 1000, duration)); if res.is_none() { return Err(MemoryError); } let astr = RMAudioStream::new(ainfo.ileave_info); @@ -525,7 +526,7 @@ println!(" got ainfo {:?}", ainfo); Ok(()) } #[allow(unused_variables)] - fn parse_video_stream(strmgr: &mut StreamManager, streams: &mut Vec, stream_no: u32, src: &mut ByteReader, edata_: &[u8], tag2: u32) -> DemuxerResult<()> { + fn parse_video_stream(strmgr: &mut StreamManager, streams: &mut Vec, stream_no: u32, src: &mut ByteReader, edata_: &[u8], tag2: u32, duration: u32) -> DemuxerResult<()> { src.read_skip(4)?; let fcc = src.read_u32be()?; let width = src.read_u16be()? as usize; @@ -550,7 +551,7 @@ println!(" got ainfo {:?}", ainfo); let vhdr = NAVideoInfo::new(width, height, false, RGB24_FORMAT); let vinfo = NACodecInfo::new(cname, NACodecTypeInfo::Video(vhdr), extradata); - let res = strmgr.add_stream(NAStream::new(StreamType::Video, stream_no as u32, vinfo, 1, 1000)); + let res = strmgr.add_stream(NAStream::new(StreamType::Video, stream_no as u32, vinfo, 1, 1000, u64::from(duration))); if res.is_none() { return Err(DemuxerError::MemoryError); } let vstr = RMVideoStream::new(); @@ -762,6 +763,8 @@ impl<'a> DemuxCore<'a> for RealMediaDemuxer<'a> { } Err(DemuxerError::SeekError) } + + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for RealMediaDemuxer<'a> { @@ -808,6 +811,16 @@ struct RealAudioInfo { ileave_info: Option } +impl RealAudioInfo { + fn get_duration(&self, base: u32) -> u64 { + if self.bytes_per_minute != 0 { + u64::from(self.total_bytes) * 60 * u64::from(base) / u64::from(self.bytes_per_minute) + } else { + 0 + } + } +} + fn skip_ra_metadata(src: &mut ByteReader) -> DemuxerResult<()> { let title_len = src.read_byte()? as usize; src.read_skip(title_len)?; @@ -1120,7 +1133,7 @@ impl<'a> RealMediaDemuxer<'a> { let mut is_mlti = false; if edata_size > 8 { if let Some(edata_) = edata { - is_mlti = RMDemuxCommon::parse_stream_info(&mut self.str_data, strmgr, stream_no, &edata_)?; + is_mlti = RMDemuxCommon::parse_stream_info(&mut self.str_data, strmgr, stream_no, &edata_, duration)?; } } else { self.str_data.streams.push(RMStreamType::Unknown); @@ -1228,7 +1241,7 @@ println!(" got ainfo {:?}", ainfo); Some(dta) }; let nainfo = NACodecInfo::new(cname, NACodecTypeInfo::Audio(ahdr), extradata); - let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 0, nainfo, 1, srate)); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 0, nainfo, 1, srate, ainfo.get_duration(ainfo.sample_rate))); if res.is_none() { return Err(MemoryError); } let astr = RMAudioStream::new(ainfo.ileave_info); @@ -1266,6 +1279,8 @@ println!(" got ainfo {:?}", ainfo); fn seek(&mut self, time: NATimePoint, seek_idx: &SeekIndex) -> DemuxerResult<()> { Err(NotImplemented) } + + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for RealAudioDemuxer<'a> { @@ -1420,6 +1435,7 @@ impl RecordDemuxer { cur_str_no += 1; let mut parsed = false; let mut real_stream_no = 0; + let mut duration = 0; for _ in 0..num { let rec = IVRRecord::read(src)?; //println!(" strm property {}", rec); @@ -1428,11 +1444,14 @@ impl RecordDemuxer { if name == b"StreamNumber\0" { real_stream_no = val; } + if name == b"Duration\0" { + duration = val; + } }, IVRRecord::BinaryData(ref name, ref val) => { if name == b"OpaqueData\0" { validate!(!parsed); - let is_mlti = RMDemuxCommon::parse_stream_info(str_data, strmgr, stream_no, val)?; + let is_mlti = RMDemuxCommon::parse_stream_info(str_data, strmgr, stream_no, val, duration)?; if !is_mlti { str_data.str_ids.push(stream_no); } @@ -1611,6 +1630,8 @@ println!("R1M kind"); fn seek(&mut self, time: NATimePoint, seek_idx: &SeekIndex) -> DemuxerResult<()> { Err(NotImplemented) } + + fn get_duration(&self) -> u64 { 0 } } impl<'a> NAOptionHandler for RealIVRDemuxer<'a> { diff --git a/nihav-vivo/src/demuxers/vivo.rs b/nihav-vivo/src/demuxers/vivo.rs index 3c0ac7d..4a6f254 100644 --- a/nihav-vivo/src/demuxers/vivo.rs +++ b/nihav-vivo/src/demuxers/vivo.rs @@ -14,6 +14,7 @@ struct VivoDemuxer<'a> { fps: f32, width: usize, height: usize, + duration: u64, vname: &'static str, aname: &'static str, } @@ -57,7 +58,7 @@ impl<'a> DemuxCore<'a> for VivoDemuxer<'a> { let vhdr = NAVideoInfo::new(self.width, self.height, false, YUV420_FORMAT); let vinfo = NACodecInfo::new(self.vname, NACodecTypeInfo::Video(vhdr), None); - let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, self.v_num, self.v_den)); + let res = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, self.v_num, self.v_den, 0)); validate!(res.is_some()); self.video_id = res.unwrap(); @@ -69,7 +70,7 @@ impl<'a> DemuxCore<'a> for VivoDemuxer<'a> { if self.aname != "none" { let ahdr = NAAudioInfo::new(self.a_den, 1, SND_S16_FORMAT, self.a_num as usize); let ainfo = NACodecInfo::new(self.aname, NACodecTypeInfo::Audio(ahdr), None); - let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, self.a_num, self.a_den)); + let res = strmgr.add_stream(NAStream::new(StreamType::Audio, 1, ainfo, self.a_num, self.a_den, 0)); validate!(res.is_some()); self.audio_id = res.unwrap(); } @@ -132,6 +133,7 @@ impl<'a> DemuxCore<'a> for VivoDemuxer<'a> { fn seek(&mut self, _time: NATimePoint, _seek_idx: &SeekIndex) -> DemuxerResult<()> { Err(DemuxerError::NotPossible) } + fn get_duration(&self) -> u64 { self.duration } } impl<'a> NAOptionHandler for VivoDemuxer<'a> { @@ -156,6 +158,7 @@ impl<'a> VivoDemuxer<'a> { width: 0, height: 0, fps: 0.0, + duration: 0, vname: "none", aname: "none", } @@ -216,6 +219,11 @@ impl<'a> VivoDemuxer<'a> { return Err(DemuxerError::InvalidData); }; }, + b"Duration" => { + self.duration = if let Ok(val) = valstr.parse() { + val + } else { 0 }; + }, /* b"TimeUnitNumerator" => { self.v_num = if let Ok(val) = valstr.parse() { val -- 2.30.2