diff --git a/src/rust/lib_ccxr/src/time/timing.rs b/src/rust/lib_ccxr/src/time/timing.rs index 564b61634..72651f3af 100644 --- a/src/rust/lib_ccxr/src/time/timing.rs +++ b/src/rust/lib_ccxr/src/time/timing.rs @@ -95,6 +95,7 @@ pub struct GlobalTimingInfo { pub fts_at_gop_start: Timestamp, pub gop_rollover: bool, pub timing_settings: TimingSettings, + pub mpeg_clock_freq: i64, } impl TimingContext { @@ -132,12 +133,14 @@ impl TimingContext { /// /// It also checks for PTS resets. pub fn set_current_pts(&mut self, pts: MpegClockTick) { + let timing_info = GLOBAL_TIMING_INFO.read().unwrap(); + let prev_pts = self.current_pts; self.current_pts = pts; if self.pts_set == PtsSet::No { self.pts_set = PtsSet::Received } - debug!(msg_type = DebugMessageFlag::VIDEO_STREAM; "PTS: {} ({:8})", self.current_pts.as_timestamp().to_hms_millis_time(':').unwrap(), self.current_pts.as_i64()); + debug!(msg_type = DebugMessageFlag::VIDEO_STREAM; "PTS: {} ({:8})", self.current_pts.as_timestamp(timing_info.mpeg_clock_freq).to_hms_millis_time(':').unwrap(), self.current_pts.as_i64()); debug!(msg_type = DebugMessageFlag::VIDEO_STREAM; " FTS: {} \n", self.fts_now.to_hms_millis_time(':').unwrap()); // Check if PTS reset @@ -162,7 +165,9 @@ impl TimingContext { // Disables sync check. Used for several input formats. 0 } else { - (self.current_pts - self.sync_pts).as_timestamp().seconds() + (self.current_pts - self.sync_pts) + .as_timestamp(timing_info.mpeg_clock_freq) + .seconds() }; // Previously in C equivalent code, its -0.2 @@ -221,7 +226,7 @@ impl TimingContext { self.sync_pts = self.current_pts - self .current_tref - .as_mpeg_clock_tick(timing_info.current_fps); + .as_mpeg_clock_tick(timing_info.current_fps, timing_info.mpeg_clock_freq); if self.current_tref.as_u64() == 0 || (timing_info.total_frames_count - timing_info.frames_since_ref_time).as_u64() @@ -245,7 +250,7 @@ impl TimingContext { debug!( msg_type = DebugMessageFlag::TIME; "\nFirst sync time PTS: {} {:+}ms (time before this PTS)\n", - self.min_pts.as_timestamp().to_hms_millis_time(':').unwrap(), + self.min_pts.as_timestamp(timing_info.mpeg_clock_freq).to_hms_millis_time(':').unwrap(), self.fts_offset.millis() ); debug!( @@ -262,7 +267,7 @@ impl TimingContext { // sync_pts (set at the beginning of the last GOP) plus the // time of the frames since then. self.fts_offset = self.fts_offset - + (self.sync_pts - self.min_pts).as_timestamp() + + (self.sync_pts - self.min_pts).as_timestamp(timing_info.mpeg_clock_freq) + timing_info .frames_since_ref_time .as_timestamp(timing_info.current_fps); @@ -277,14 +282,14 @@ impl TimingContext { self.sync_pts = self.current_pts - self .current_tref - .as_mpeg_clock_tick(timing_info.current_fps); + .as_mpeg_clock_tick(timing_info.current_fps, timing_info.mpeg_clock_freq); // Set min_pts = sync_pts as this is used for fts_now self.min_pts = self.sync_pts; debug!( msg_type = DebugMessageFlag::TIME; "\nNew min PTS time: {} {:+}ms (time before this PTS)\n", - self.min_pts.as_timestamp().to_hms_millis_time(':').unwrap(), + self.min_pts.as_timestamp(timing_info.mpeg_clock_freq).to_hms_millis_time(':').unwrap(), self.fts_offset.millis() ); } @@ -305,7 +310,9 @@ impl TimingContext { // CFS: Remove or think decent condition if self.pts_set != PtsSet::No { // If pts_set is TRUE we have min_pts - self.fts_now = (self.current_pts - self.min_pts).as_timestamp() + self.fts_offset; + self.fts_now = (self.current_pts - self.min_pts) + .as_timestamp(timing_info.mpeg_clock_freq) + + self.fts_offset; if !self.sync_pts2fts_set { self.sync_pts2fts_pts = self.current_pts; self.sync_pts2fts_fts = self.fts_now; @@ -373,7 +380,7 @@ impl TimingContext { info!( "Sync time stamps: PTS: {} ", self.sync_pts - .as_timestamp() + .as_timestamp(timing_info.mpeg_clock_freq) .to_hms_millis_time(':') .unwrap() ); @@ -385,7 +392,8 @@ impl TimingContext { // Length first GOP to last GOP let goplenms = gop_time - first_gop_time; // Length at last sync point - let ptslenms = (self.sync_pts - tempmin_pts).as_timestamp() + self.fts_offset; + let ptslenms = (self.sync_pts - tempmin_pts).as_timestamp(timing_info.mpeg_clock_freq) + + self.fts_offset; info!( "Last FTS: {}", @@ -548,6 +556,7 @@ impl GlobalTimingInfo { no_sync: false, is_elementary_stream: false, }, + mpeg_clock_freq: 0, } } } diff --git a/src/rust/lib_ccxr/src/time/units.rs b/src/rust/lib_ccxr/src/time/units.rs index f44dc0653..16714d142 100644 --- a/src/rust/lib_ccxr/src/time/units.rs +++ b/src/rust/lib_ccxr/src/time/units.rs @@ -1,5 +1,4 @@ use std::convert::TryInto; -use std::ffi::c_int; use std::fmt::Write; use std::num::TryFromIntError; use std::time::{SystemTime, UNIX_EPOCH}; @@ -12,10 +11,6 @@ use time::{ Duration, }; -extern "C" { - static mut MPEG_CLOCK_FREQ: c_int; -} - /// Represents a timestamp in milliseconds. /// /// The number can be negetive. @@ -482,11 +477,6 @@ impl Timestamp { pub struct MpegClockTick(i64); impl MpegClockTick { - /// Returns the ratio to convert a clock tick to time duration. - pub fn mpeg_clock_freq() -> i64 { - unsafe { MPEG_CLOCK_FREQ.into() } - } - /// Create a value representing `ticks` clock ticks. pub fn new(ticks: i64) -> MpegClockTick { MpegClockTick(ticks) @@ -499,9 +489,9 @@ impl MpegClockTick { /// Converts the clock ticks to its equivalent time duration. /// - /// The conversion ratio used is [`MpegClockTick::MPEG_CLOCK_FREQ`]. - pub fn as_timestamp(&self) -> Timestamp { - Timestamp::from_millis(self.0 / (MpegClockTick::mpeg_clock_freq() / 1000)) + /// The conversion ratio used is governed by `mpeg_clock_freq` + pub fn as_timestamp(&self, mpeg_clock_freq: i64) -> Timestamp { + Timestamp::from_millis(self.0 / (mpeg_clock_freq / 1000)) } } @@ -531,9 +521,9 @@ impl FrameCount { /// Converts the frames to its equivalent number of clock ticks. /// - /// The conversion ratio used is [`MpegClockTick::MPEG_CLOCK_FREQ`] and `fps`. - pub fn as_mpeg_clock_tick(&self, fps: f64) -> MpegClockTick { - MpegClockTick::new(((self.0 * MpegClockTick::mpeg_clock_freq() as u64) as f64 / fps) as i64) + /// The conversion ratio used is governed by `fps` and `mpeg_clock_freq` + pub fn as_mpeg_clock_tick(&self, fps: f64, mpeg_clock_freq: i64) -> MpegClockTick { + MpegClockTick::new(((self.0 * mpeg_clock_freq as u64) as f64 / fps) as i64) } } diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 3350d8ff5..74101c95a 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -27,6 +27,7 @@ use std::os::windows::io::{FromRawHandle, RawHandle}; use args::Args; use bindings::*; +use cfg_if::cfg_if; use clap::{error::ErrorKind, Parser}; use common::{copy_from_rust, CType, CType2}; use decoder::Dtvcc; @@ -42,39 +43,62 @@ use std::{ os::raw::{c_char, c_double, c_int, c_long, c_uint}, }; -#[cfg(test)] -static mut cb_708: c_int = 0; -#[cfg(test)] -static mut cb_field1: c_int = 0; -#[cfg(test)] -static mut cb_field2: c_int = 0; - -#[cfg(not(test))] -extern "C" { - static mut cb_708: c_int; - static mut cb_field1: c_int; - static mut cb_field2: c_int; -} - -#[allow(dead_code)] -extern "C" { - static mut usercolor_rgb: [c_int; 8]; - static mut FILEBUFFERSIZE: c_int; - static mut MPEG_CLOCK_FREQ: c_int; - static mut tlt_config: ccx_s_teletext_config; - static mut ccx_options: ccx_s_options; - static mut pts_big_change: c_uint; - static mut current_fps: c_double; - static mut frames_since_ref_time: c_int; - static mut total_frames_count: c_uint; - static mut gop_time: gop_time_code; - static mut first_gop_time: gop_time_code; - static mut fts_at_gop_start: c_long; - static mut gop_rollover: c_int; - static mut ccx_common_timing_settings: ccx_common_timing_settings_t; - static mut capitalization_list: word_list; - static mut profane: word_list; -} +// Mock data for rust unit tests +cfg_if! { + if #[cfg(test)] { + static mut cb_708: c_int = 0; + static mut cb_field1: c_int = 0; + static mut cb_field2: c_int = 0; + static mut current_fps: c_double = 30.0; + static mut usercolor_rgb: [c_int; 8] = [0; 8]; + static mut FILEBUFFERSIZE: c_int = 0; + static mut MPEG_CLOCK_FREQ: c_int = 90000; + + static mut frames_since_ref_time: c_int = 0; + static mut total_frames_count: c_uint = 0; + static mut fts_at_gop_start: c_long = 0; + static mut gop_rollover: c_int = 0; + static mut pts_big_change: c_uint = 0; + + static mut tlt_config: ccx_s_teletext_config = unsafe { std::mem::zeroed() }; + static mut ccx_options: ccx_s_options = unsafe { std::mem::zeroed() }; + static mut gop_time: gop_time_code = unsafe { std::mem::zeroed() }; + static mut first_gop_time: gop_time_code = unsafe { std::mem::zeroed() }; + static mut ccx_common_timing_settings: ccx_common_timing_settings_t = unsafe { std::mem::zeroed() }; + static mut capitalization_list: word_list = unsafe { std::mem::zeroed() }; + static mut profane: word_list = unsafe { std::mem::zeroed() }; + + unsafe extern "C" fn version(_location: *const c_char) {} + unsafe extern "C" fn set_binary_mode() {} + } +} + +// External C symbols (only when not testing) +#[cfg(not(test))] +extern "C" { + static mut cb_708: c_int; + static mut cb_field1: c_int; + static mut cb_field2: c_int; + static mut current_fps: c_double; + static mut usercolor_rgb: [c_int; 8]; + static mut FILEBUFFERSIZE: c_int; + static mut MPEG_CLOCK_FREQ: c_int; + static mut tlt_config: ccx_s_teletext_config; + static mut ccx_options: ccx_s_options; + static mut frames_since_ref_time: c_int; + static mut total_frames_count: c_uint; + static mut gop_time: gop_time_code; + static mut first_gop_time: gop_time_code; + static mut fts_at_gop_start: c_long; + static mut gop_rollover: c_int; + static mut ccx_common_timing_settings: ccx_common_timing_settings_t; + static mut capitalization_list: word_list; + static mut profane: word_list; + static mut pts_big_change: c_uint; + + fn version(location: *const c_char); + fn set_binary_mode(); +} /// Initialize env logger with custom format, using stdout as target #[no_mangle] @@ -216,12 +240,6 @@ extern "C" fn ccxr_close_handle(handle: RawHandle) { } } -extern "C" { - fn version(location: *const c_char); - #[allow(dead_code)] - fn set_binary_mode(); -} - /// # Safety /// Safe if argv is a valid pointer /// diff --git a/src/rust/src/libccxr_exports/time.rs b/src/rust/src/libccxr_exports/time.rs index 915c7e5b0..58bf0aecc 100644 --- a/src/rust/src/libccxr_exports/time.rs +++ b/src/rust/src/libccxr_exports/time.rs @@ -6,7 +6,7 @@ use std::ffi::{c_char, c_int, c_long, CStr}; use crate::{ bindings::*, cb_708, cb_field1, cb_field2, ccx_common_timing_settings as timing_settings, current_fps, first_gop_time, frames_since_ref_time, fts_at_gop_start, gop_rollover, gop_time, - pts_big_change, total_frames_count, + pts_big_change, total_frames_count, MPEG_CLOCK_FREQ, }; use lib_ccxr::common::FrameType; @@ -279,6 +279,7 @@ unsafe fn apply_timing_info() { timing_info.timing_settings.disable_sync_check = timing_settings.disable_sync_check != 0; timing_info.timing_settings.no_sync = timing_settings.no_sync != 0; timing_info.timing_settings.is_elementary_stream = timing_settings.is_elementary_stream != 0; + timing_info.mpeg_clock_freq = MPEG_CLOCK_FREQ.into(); } /// Write from [`GLOBAL_TIMING_INFO`] to the equivalent static variables in C. diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index 3345e7516..c167c2a9e 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -24,13 +24,7 @@ use time::OffsetDateTime; use crate::args::CCXCodec; use crate::args::{self, InFormat}; -cfg_if! { - if #[cfg(test)] { - use crate::parser::tests::{set_binary_mode, MPEG_CLOCK_FREQ, usercolor_rgb, FILEBUFFERSIZE}; - } else { - use crate::{set_binary_mode, MPEG_CLOCK_FREQ, usercolor_rgb, FILEBUFFERSIZE}; - } -} +use crate::{set_binary_mode, usercolor_rgb, FILEBUFFERSIZE, MPEG_CLOCK_FREQ}; cfg_if! { if #[cfg(windows)] { @@ -1684,9 +1678,6 @@ pub mod tests { #[no_mangle] pub unsafe extern "C" fn set_binary_mode() {} - pub static mut MPEG_CLOCK_FREQ: u64 = 0; - pub static mut FILEBUFFERSIZE: i32 = 0; - pub static mut usercolor_rgb: [i32; 8] = [0; 8]; fn parse_args(args: &[&str]) -> (Options, TeletextConfig) { let mut common_args = vec!["./ccextractor", "input_file"];