]> git.nihav.org Git - nihav.git/commitdiff
replace NATimeInfo::{time_to_ts,ts_to_time} with single rescale_ts master
authorKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 22 Jan 2026 18:03:01 +0000 (19:03 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 22 Jan 2026 18:03:01 +0000 (19:03 +0100)
13 files changed:
nihav-commonfmt/src/demuxers/avi.rs
nihav-commonfmt/src/demuxers/mov.rs
nihav-commonfmt/src/demuxers/y4m.rs
nihav-commonfmt/src/muxers/avi.rs
nihav-commonfmt/src/muxers/gif.rs
nihav-core/src/demuxers/mod.rs
nihav-core/src/frame.rs
nihav-flash/src/muxers/flv.rs
nihav-llaudio/src/demuxers/flac.rs
nihav-ms/src/demuxers/avidib.rs
nihav-rad/src/demuxers/bink.rs
nihav-realmedia/src/codecs/rv40enc/mod.rs
nihav-realmedia/src/muxers/rmvb/mod.rs

index 2661b30fe1161731b1933387742d1d1304b996f8..2b763d52afd00d9e7fffb7422cf4e361d7cfe39d 100644 (file)
@@ -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) });
             }
 
index bc3191f2c7d32dbc3588ec4a33cca962148779b1..427466036e53dd35ed71e7ac86763a2aabb9fcbd 100644 (file)
@@ -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<NAPacket> {
     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))?;
                     }
                 }
index fd8edea205ecbf3c9cb907f38a6a55023bf5a40f..0fe0976ddebbf6fc4af4c0284fe610af0a94e650 100644 (file)
@@ -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),
             };
index 307bbf0b9a8a83af56c0727c2399adf10c75d999..a55bbb4053dacdf6d8a1f7a21f5f9d09c7b07009 100644 (file)
@@ -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
index 42968ddfa73e353b4369086ec649568e9d7726b3..aebf53f5bf847b4bd8fd096d5f0c206a4199bff2 100644 (file)
@@ -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
index 78f4ab5f3c82892e1597853021dfb88a054d0a7b..a9564346f4edb5d057c24cf5b0fd3d77d211eb04 100644 (file)
@@ -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;
                 }
index 1bf5dec96cfcaec1ad2c5814e82321f308acef18..174e59434b03470d70b6580af65d81cb1ff39ae0 100644 (file)
@@ -956,58 +956,41 @@ impl NATimeInfo {
     /// Sets new duration.
     pub fn set_duration(&mut self, dur: Option<u64>) { 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 {
index b145238e5f2d10964c8cdb703af6690ea246932b..8efaae490cb1cc429d170d2ee903cd95fd7f6400 100644 (file)
@@ -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)?;
                         },
index a9526b73d36796c80fcd5f3276fe465de56be65c..e42c6aa9d51e6707bee7bb79a30c924b5b1f95fc 100644 (file)
@@ -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 {
index 4852863c27ffcacc87b2e60b2a733bcf6c7d1f3b..b6f1b95c898a7544923f776eea1e48a451a77d5f 100644 (file)
@@ -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);
index 834b5ea4d623ea85dd05bebf26107cca4138c256..f7720bc4ebafa70187b1a560de66c41d25635b57 100644 (file)
@@ -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),
             };
index 402cf3dd67e6e6eab430653a702108e6f87ddc6c..b3128f91a6184188d745fb1fee9bdd2451a9a57f 100644 (file)
@@ -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 {
index e153aac98f5955d1f1acee12143c5d56ce0cd13a..0d07460d1de9f05fdcf113d57dd722e2642d5d0e 100644 (file)
@@ -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;
         }