From 11a3aaa7551dae30f1be8aca5408aea995e197f5 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Thu, 22 Jan 2026 19:03:01 +0100 Subject: [PATCH] replace NATimeInfo::{time_to_ts,ts_to_time} with single rescale_ts --- nihav-commonfmt/src/demuxers/avi.rs | 6 +- nihav-commonfmt/src/demuxers/mov.rs | 10 +-- nihav-commonfmt/src/demuxers/y4m.rs | 2 +- nihav-commonfmt/src/muxers/avi.rs | 2 +- nihav-commonfmt/src/muxers/gif.rs | 2 +- nihav-core/src/demuxers/mod.rs | 4 +- nihav-core/src/frame.rs | 75 +++++++++-------------- nihav-flash/src/muxers/flv.rs | 4 +- nihav-llaudio/src/demuxers/flac.rs | 2 +- nihav-ms/src/demuxers/avidib.rs | 2 +- nihav-rad/src/demuxers/bink.rs | 2 +- nihav-realmedia/src/codecs/rv40enc/mod.rs | 2 +- nihav-realmedia/src/muxers/rmvb/mod.rs | 2 +- 13 files changed, 49 insertions(+), 66 deletions(-) diff --git a/nihav-commonfmt/src/demuxers/avi.rs b/nihav-commonfmt/src/demuxers/avi.rs index 2661b30..2b763d5 100644 --- a/nihav-commonfmt/src/demuxers/avi.rs +++ b/nihav-commonfmt/src/demuxers/avi.rs @@ -875,7 +875,7 @@ fn parse_idx1(src: &mut dyn ByteIO, strmgr: &mut StreamManager, seek_idx: &mut S } } } - let time = NATimeInfo::ts_to_time(pts, 1000, tb_nums[stream_no], tb_dens[stream_no]); + let time = NATimeInfo::rescale_ts(pts, tb_nums[stream_no], tb_dens[stream_no], 1, 1000); for avi_str in avi_streams.iter_mut() { if avi_str.strm_no == (stream_no as u8) { @@ -951,7 +951,7 @@ fn parse_iddx_data(src: &mut dyn ByteIO, strmgr: &mut StreamManager, seek_idx: & if stream.get_media_type() == StreamType::Video { let (tb_num, tb_den) = stream.get_timebase(); let pts = counter[stream_no]; - let time = NATimeInfo::ts_to_time(pts, 1000, tb_num, tb_den); + let time = NATimeInfo::rescale_ts(pts, tb_num, tb_den, 1, 1000); validate!(offset >= movi_pos); seek_idx.add_entry(stream_no as u32, SeekEntry { time, pts, pos: offset }); } @@ -990,7 +990,7 @@ fn parse_odml_ix(src: &mut dyn ByteIO, strmgr: &mut StreamManager, seek_idx: &mu let _size = src.read_u32le()?; let pts = start + (i as u64); - let time = NATimeInfo::ts_to_time(pts, 1000, tb_num, tb_den); + let time = NATimeInfo::rescale_ts(pts, tb_num, tb_den, 1, 1000); seek_idx.add_entry(stream_no as u32, SeekEntry { time, pts, pos: base_offset + u64::from(offset - 8) }); } diff --git a/nihav-commonfmt/src/demuxers/mov.rs b/nihav-commonfmt/src/demuxers/mov.rs index bc3191f..4274660 100644 --- a/nihav-commonfmt/src/demuxers/mov.rs +++ b/nihav-commonfmt/src/demuxers/mov.rs @@ -1385,7 +1385,7 @@ impl Track { let mut tsearch = TimeSearcher::new(); for kf_time in self.keyframes.iter() { 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); + let time = NATimeInfo::rescale_ts(pts, self.tb_num, self.tb_den, 1, 1000); seek_index.add_entry(self.track_no, SeekEntry { time, pts: u64::from(*kf_time - 1), pos: 0 }); } } @@ -1526,7 +1526,7 @@ impl Track { 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); + let exp_pts = NATimeInfo::rescale_ts(ms, 1, 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); @@ -1689,14 +1689,14 @@ impl Track { self.cur_chunk += 1; } let cur_pts = self.timesearch.map_time(self.cur_sample as u32, &self.time_to_sample); - let cur_time = NATimeInfo::ts_to_time(cur_pts, 1000, self.tb_num, self.tb_den); + let cur_time = NATimeInfo::rescale_ts(cur_pts, self.tb_num, self.tb_den, 1, 1000); Ok(cur_time) } } fn process_packet(src: &mut dyn ByteIO, strmgr: &StreamManager, track: &mut Track, pts: NATimeInfo, offset: u64, size: usize, first: bool) -> DemuxerResult { if let Some(cpts) = pts.get_pts() { - let ts = NATimeInfo::ts_to_time(cpts, 1000, pts.tb_num, pts.tb_den); + let ts = NATimeInfo::rescale_ts(cpts, pts.tb_num, pts.tb_den, 1, 1000); track.cur_ts = Some(ts); } else { track.cur_ts = None; @@ -1911,7 +1911,7 @@ impl<'a> DemuxCore<'a> for MOVDemuxer<'a> { if vtime.max(atime) - vtime.min(atime) > 500 && atime != 0 { for track in self.tracks.iter_mut() { if track.stream_type == StreamType::Audio { - let new_pts = NATimeInfo::time_to_ts(vtime, 1000, track.tb_num, track.tb_den); + let new_pts = NATimeInfo::rescale_ts(vtime, 1, 1000, track.tb_num, track.tb_den); track.seek(new_pts, NATimePoint::Milliseconds(vtime))?; } } diff --git a/nihav-commonfmt/src/demuxers/y4m.rs b/nihav-commonfmt/src/demuxers/y4m.rs index fd8edea..0fe0976 100644 --- a/nihav-commonfmt/src/demuxers/y4m.rs +++ b/nihav-commonfmt/src/demuxers/y4m.rs @@ -52,7 +52,7 @@ impl<'a> DemuxCore<'a> for Y4MDemuxer<'a> { if (self.fps_num == 0) || (self.fps_den == 0) { return Err(DemuxerError::SeekError); } - NATimeInfo::time_to_ts(ms, 1000, self.fps_num, self.fps_den) + NATimeInfo::rescale_ts(ms, 1, 1000, self.fps_num, self.fps_den) }, NATimePoint::None => return Err(DemuxerError::SeekError), }; diff --git a/nihav-commonfmt/src/muxers/avi.rs b/nihav-commonfmt/src/muxers/avi.rs index 307bbf0..a55bbb4 100644 --- a/nihav-commonfmt/src/muxers/avi.rs +++ b/nihav-commonfmt/src/muxers/avi.rs @@ -176,7 +176,7 @@ impl<'a> MuxCore<'a> for AVIMuxer<'a> { let hdrl_pos = self.bw.tell() + 20; self.bw.write_buf(b"RIFF\0\0\0\0AVI LIST\0\0\0\0hdrlavih")?; self.bw.write_u32le(56)?; // avih size - let ms_per_frame = NATimeInfo::ts_to_time(1, 1000000, tb_num, tb_den); + let ms_per_frame = NATimeInfo::rescale_ts(1, tb_num, tb_den, 1, 1000000); self.bw.write_u32le(ms_per_frame as u32)?; self.bw.write_u32le(0)?; // max transfer rate self.bw.write_u32le(0)?; // padding granularity diff --git a/nihav-commonfmt/src/muxers/gif.rs b/nihav-commonfmt/src/muxers/gif.rs index 42968dd..aebf53f 100644 --- a/nihav-commonfmt/src/muxers/gif.rs +++ b/nihav-commonfmt/src/muxers/gif.rs @@ -100,7 +100,7 @@ impl<'a> MuxCore<'a> for GIFMuxer<'a> { if !self.single { let vstr = strmgr.get_stream(0).unwrap(); - let delay = NATimeInfo::ts_to_time(1, 100, vstr.tb_num, vstr.tb_den) as u16; + let delay = NATimeInfo::rescale_ts(1, vstr.tb_num, vstr.tb_den, 1, 100) as u16; self.bw.write_byte(0x21)?; // graphic control self.bw.write_byte(0xF9)?; // graphic control extension self.bw.write_byte(4)?; // block size diff --git a/nihav-core/src/demuxers/mod.rs b/nihav-core/src/demuxers/mod.rs index 78f4ab5..a956434 100644 --- a/nihav-core/src/demuxers/mod.rs +++ b/nihav-core/src/demuxers/mod.rs @@ -420,7 +420,7 @@ impl<'a> Demuxer<'a> { 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); + let dur = NATimeInfo::rescale_ts(stream.duration, stream.tb_num, stream.tb_den, 1, 1000); if duration < dur { duration = dur; } @@ -593,7 +593,7 @@ impl<'a> RawDemuxer<'a> { 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); + let dur = NATimeInfo::rescale_ts(stream.duration, stream.tb_num, stream.tb_den, 1, 1000); if duration < dur { duration = dur; } diff --git a/nihav-core/src/frame.rs b/nihav-core/src/frame.rs index 1bf5dec..174e594 100644 --- a/nihav-core/src/frame.rs +++ b/nihav-core/src/frame.rs @@ -956,58 +956,41 @@ impl NATimeInfo { /// Sets new duration. pub fn set_duration(&mut self, dur: Option) { self.duration = dur; } - /// Converts time in given scale into timestamp in given base. - #[allow(clippy::collapsible_if)] - #[allow(clippy::collapsible_else_if)] - pub fn time_to_ts(time: u64, base: u64, tb_num: u32, tb_den: u32) -> u64 { - let tb_num = u64::from(tb_num); - let tb_den = u64::from(tb_den); - let tmp = time.checked_mul(tb_den); - if let Some(tmp) = tmp { - tmp / base / tb_num - } else { - if tb_num < base { - let coarse = time / tb_num; - if let Some(tmp) = coarse.checked_mul(tb_den) { - tmp / base - } else { - (coarse / base) * tb_den - } - } else { - let coarse = time / base; - if let Some(tmp) = coarse.checked_mul(tb_den) { - tmp / tb_num - } else { - (coarse / tb_num) * tb_den - } - } - } - } - /// Converts timestamp in given base into time in given scale. - pub fn ts_to_time(ts: u64, base: u64, tb_num: u32, tb_den: u32) -> u64 { - let tb_num = u64::from(tb_num); - let tb_den = u64::from(tb_den); - let tmp = ts.checked_mul(base); - if let Some(tmp) = tmp { - let tmp2 = tmp.checked_mul(tb_num); - if let Some(tmp2) = tmp2 { - tmp2 / tb_den - } else { - (tmp / tb_den) * tb_num - } - } else { - let tmp = ts.checked_mul(tb_num); - if let Some(tmp) = tmp { - (tmp / tb_den) * base - } else { - (ts / tb_den) * base * tb_num + /// Converts time in one given scale into another base. + /// + /// For instance, converting timestamp from 2/25 rate to milliseconds can be done as + /// ``` + /// let pts = 42; + /// let time = NATimeInfo::rescale_ts(pts, 2, 25, 1, 1000); + /// ``` + /// + /// while back conversion is equally simple: + /// ``` + /// let millis = 42; + /// let ts = NATimeInfo::rescale_ts(millis, 1, 1000, 2, 25); + /// ``` + pub fn rescale_ts(ts: u64, src_tb_num: u32, src_tb_den: u32, dst_tb_num: u32, dst_tb_den: u32) -> u64 { + let src_tb_num = u64::from(src_tb_num); + let src_tb_den = u64::from(src_tb_den); + let dst_tb_num = u64::from(dst_tb_num); + let dst_tb_den = u64::from(dst_tb_den); + + // ts * (src_tb_num / src_tb_den) / (dst_tb_num / dst_tb_den) + // reordered to retain the maximum precision, so multiplications go first + if let Some(nval) = ts.checked_mul(dst_tb_den) { + if let Some(nnval) = nval.checked_mul(src_tb_num) { + nnval / src_tb_den / dst_tb_num + } else { // intermediate result is too large, use coarse approximation + (nval / dst_tb_num).saturating_mul(src_tb_num) / src_tb_den } + } else { // intermediate result is too large, use coarse approximation + (ts.saturating_mul(src_tb_num) / src_tb_den).saturating_mul(dst_tb_num) / dst_tb_den } } fn get_cur_ts(&self) -> u64 { self.pts.unwrap_or_else(|| self.dts.unwrap_or(0)) } fn get_cur_millis(&self) -> u64 { let ts = self.get_cur_ts(); - Self::ts_to_time(ts, 1000, self.tb_num, self.tb_den) + Self::rescale_ts(ts, self.tb_num, self.tb_den, 1, 1000) } /// Checks whether the current time information is earler than provided reference time. pub fn less_than(&self, time: NATimePoint) -> bool { diff --git a/nihav-flash/src/muxers/flv.rs b/nihav-flash/src/muxers/flv.rs index b145238..8efaae4 100644 --- a/nihav-flash/src/muxers/flv.rs +++ b/nihav-flash/src/muxers/flv.rs @@ -228,7 +228,7 @@ impl<'a> MuxCore<'a> for FLVMuxer<'a> { fn mux_frame(&mut self, _strmgr: &StreamManager, pkt: NAPacket) -> MuxerResult<()> { let stream = pkt.get_stream(); let pts = pkt.get_pts().unwrap_or(0); - let ms = NATimeInfo::ts_to_time(pts, 1000, pkt.ts.tb_num, pkt.ts.tb_den) as u32; + let ms = NATimeInfo::rescale_ts(pts, pkt.ts.tb_num, pkt.ts.tb_den, 1, 1000) as u32; self.time = self.time.max(ms); match stream.get_media_type() { StreamType::Video => { @@ -241,7 +241,7 @@ impl<'a> MuxCore<'a> for FLVMuxer<'a> { }, AVC_ID => { self.bw.write_byte(1)?; - let cms = NATimeInfo::ts_to_time(pkt.get_pts().unwrap_or(pts), 1000, pkt.ts.tb_num, pkt.ts.tb_den) as u32; + let cms = NATimeInfo::rescale_ts(pkt.get_pts().unwrap_or(pts), pkt.ts.tb_num, pkt.ts.tb_den, 1, 1000) as u32; let cts = cms.wrapping_sub(ms) << 8 >> 8; self.bw.write_u24be(cts)?; }, diff --git a/nihav-llaudio/src/demuxers/flac.rs b/nihav-llaudio/src/demuxers/flac.rs index a9526b7..e42c6aa 100644 --- a/nihav-llaudio/src/demuxers/flac.rs +++ b/nihav-llaudio/src/demuxers/flac.rs @@ -238,7 +238,7 @@ impl<'a> DemuxCore<'a> for FLACDemuxer<'a> { self.src.seek(SeekFrom::Start(self.data_start + seek_info.pos))?; Ok(()) } else if let NATimePoint::Milliseconds(ms) = time { - let samppos = NATimeInfo::time_to_ts(ms, 1000, 1, self.srate); + let samppos = NATimeInfo::rescale_ts(ms, 1, 1000, 1, self.srate); if self.known_frames.last().unwrap_or(&FrameSeekInfo::default()).sampleend >= samppos { for point in self.known_frames.iter().rev() { if point.samplepos <= samppos { diff --git a/nihav-ms/src/demuxers/avidib.rs b/nihav-ms/src/demuxers/avidib.rs index 4852863..b6f1b95 100644 --- a/nihav-ms/src/demuxers/avidib.rs +++ b/nihav-ms/src/demuxers/avidib.rs @@ -332,7 +332,7 @@ fn parse_idx1(src: &mut dyn ByteIO, strmgr: &mut StreamManager, seek_idx: &mut S if &tag == b"dibc" { let (tb_num, tb_den) = stream.get_timebase(); let pts = counter; - let time = NATimeInfo::ts_to_time(pts, 1000, tb_num, tb_den); + let time = NATimeInfo::rescale_ts(pts, tb_num, tb_den, 1, 1000); validate!(offset >= movi_pos); seek_idx.add_entry(0, SeekEntry { time, pts, pos: offset }); key_offs.push(offset); diff --git a/nihav-rad/src/demuxers/bink.rs b/nihav-rad/src/demuxers/bink.rs index 834b5ea..f7720bc 100644 --- a/nihav-rad/src/demuxers/bink.rs +++ b/nihav-rad/src/demuxers/bink.rs @@ -155,7 +155,7 @@ impl<'a> DemuxCore<'a> for BinkDemuxer<'a> { } fn seek(&mut self, time: NATimePoint, seek_idx: &SeekIndex) -> DemuxerResult<()> { let seek_ts = match time { - NATimePoint::Milliseconds(ms) => NATimeInfo::time_to_ts(ms, 1000, self.tb_num, self.tb_den), + NATimePoint::Milliseconds(ms) => NATimeInfo::rescale_ts(ms, 1, 1000, self.tb_num, self.tb_den), NATimePoint::PTS(ts) => ts, _ => return Err(DemuxerError::SeekError), }; diff --git a/nihav-realmedia/src/codecs/rv40enc/mod.rs b/nihav-realmedia/src/codecs/rv40enc/mod.rs index 402cf3d..b3128f9 100644 --- a/nihav-realmedia/src/codecs/rv40enc/mod.rs +++ b/nihav-realmedia/src/codecs/rv40enc/mod.rs @@ -417,7 +417,7 @@ impl RV40Encoder { let buf = frm.get_buffer(); let tinfo = frm.get_time_information(); - let pts = NATimeInfo::ts_to_time(tinfo.pts.unwrap_or(0), 1000, tinfo.tb_num, tinfo.tb_den); + let pts = NATimeInfo::rescale_ts(tinfo.pts.unwrap_or(0), tinfo.tb_num, tinfo.tb_den, 1, 1000); let fpts = (pts & 0x1FFF) as u32; let ts_diff = if ftype == FrameType::B { diff --git a/nihav-realmedia/src/muxers/rmvb/mod.rs b/nihav-realmedia/src/muxers/rmvb/mod.rs index e153aac..0d07460 100644 --- a/nihav-realmedia/src/muxers/rmvb/mod.rs +++ b/nihav-realmedia/src/muxers/rmvb/mod.rs @@ -126,7 +126,7 @@ impl RMStream { fn write_packet(&mut self, bw: &mut dyn ByteIO, pkt: NAPacket, pkt_no: &mut u32) -> MuxerResult<()> { if let Some(pts) = pkt.get_pts() { let (tb_num, tb_den) = pkt.get_stream().get_timebase(); - let ms = NATimeInfo::ts_to_time(pts, 1000, tb_num, tb_den) as u32; + let ms = NATimeInfo::rescale_ts(pts, tb_num, tb_den, 1, 1000) as u32; self.time = self.time.max(ms); self.cur_time = ms; } -- 2.39.5