Skip to content

Commit d0caf23

Browse files
cfsmp3claude
andcommitted
fix(timing): Use i64 instead of c_long for Windows compatibility
The Rust FFI functions were using c_long for PTS/FTS timestamps, but: - C code uses LLONG (int64_t, 64 bits on all platforms) - Rust c_long is 32 bits on Windows, 64 bits on Linux This caused timestamp truncation on Windows when PTS values exceeded 2^31 (~24 days at 90kHz), resulting in wrong subtitle timestamps. For example, a file with Min PTS of 23:50:45 (7,726,090,500 ticks) would have its PTS truncated, breaking the teletext delta calculation that normalizes timestamps to start at 0. Changes: - ccxr_add_current_pts: pts parameter i64 - ccxr_set_current_pts: pts parameter i64 - ccxr_get_fts: return type i64 - ccxr_get_visible_end: return type i64 - ccxr_get_visible_start: return type i64 - ccxr_get_fts_max: return type i64 - ccxr_print_mstime_static: mstime parameter i64 - fts_at_gop_start: extern static i64 Fixes tests 18 and 19 on Windows CI which showed raw PTS timestamps (23:50:46) instead of normalized timestamps (00:00:00). 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <[email protected]>
1 parent da3dc52 commit d0caf23

File tree

4 files changed

+24
-25
lines changed

4 files changed

+24
-25
lines changed

src/rust/src/decoder/output.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,9 +114,12 @@ pub fn write_char(sym: &dtvcc_symbol, buf: &mut Vec<u8>, use_utf16: bool) {
114114
/// These are fixed-width 16-bit encodings where even ASCII needs 2 bytes.
115115
pub fn is_utf16_charset(charset: &str) -> bool {
116116
let upper = charset.to_uppercase();
117-
upper.contains("UTF-16") || upper.contains("UTF16") ||
118-
upper.contains("UCS-2") || upper.contains("UCS2") ||
119-
upper.contains("UTF_16") || upper.contains("UCS_2")
117+
upper.contains("UTF-16")
118+
|| upper.contains("UTF16")
119+
|| upper.contains("UCS-2")
120+
|| upper.contains("UCS2")
121+
|| upper.contains("UTF_16")
122+
|| upper.contains("UCS_2")
120123
}
121124

122125
/// Convert from CEA-708 color representation to hex code

src/rust/src/es/pic.rs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -277,12 +277,8 @@ pub unsafe fn read_pic_info(
277277
// set_fts() is not called for each picture when use_gop_as_pts == 1.
278278
if ccx_options.use_gop_as_pts == 1 {
279279
// Calculate current FTS based on GOP start time + frame offset
280-
// Cast fts_at_gop_start to i64 for cross-platform compatibility (c_long is i32 on Windows)
281280
let frame_offset_ms = (dec_ctx.frames_since_last_gop as f64 * 1000.0 / current_fps) as i64;
282-
#[allow(clippy::unnecessary_cast)]
283-
{
284-
(*dec_ctx.timing).fts_now = (fts_at_gop_start as i64) + frame_offset_ms;
285-
}
281+
(*dec_ctx.timing).fts_now = fts_at_gop_start + frame_offset_ms;
286282

287283
// Update fts_max if needed
288284
if (*dec_ctx.timing).fts_now > (*dec_ctx.timing).fts_max {

src/rust/src/lib.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ use std::os::raw::{c_uchar, c_void};
5151
use std::{
5252
ffi::CStr,
5353
io::Write,
54-
os::raw::{c_char, c_double, c_int, c_long, c_uint},
54+
os::raw::{c_char, c_double, c_int, c_uint},
5555
};
5656

5757
// Mock data for rust unit tests
@@ -67,7 +67,7 @@ cfg_if! {
6767

6868
static mut frames_since_ref_time: c_int = 0;
6969
static mut total_frames_count: c_uint = 0;
70-
static mut fts_at_gop_start: c_long = 0;
70+
static mut fts_at_gop_start: i64 = 0;
7171
static mut gop_rollover: c_int = 0;
7272
static mut pts_big_change: c_uint = 0;
7373

@@ -144,7 +144,7 @@ extern "C" {
144144
static mut total_frames_count: c_uint;
145145
static mut gop_time: gop_time_code;
146146
static mut first_gop_time: gop_time_code;
147-
static mut fts_at_gop_start: c_long;
147+
static mut fts_at_gop_start: i64;
148148
static mut gop_rollover: c_int;
149149
static mut ccx_common_timing_settings: ccx_common_timing_settings_t;
150150
static mut capitalization_list: word_list;

src/rust/src/libccxr_exports/time.rs

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
#![allow(clippy::useless_conversion)]
22

33
use std::convert::TryInto;
4-
use std::ffi::{c_char, c_int, c_long, CStr};
4+
use std::ffi::{c_char, c_int, CStr};
55

66
use crate::{
77
bindings::*, cb_708, cb_field1, cb_field2, ccx_common_timing_settings as timing_settings,
@@ -330,7 +330,7 @@ unsafe fn write_back_from_timing_info() {
330330
.unwrap_or(0);
331331
gop_time = write_gop_time_code(timing_info.gop_time);
332332
first_gop_time = write_gop_time_code(timing_info.first_gop_time);
333-
fts_at_gop_start = timing_info.fts_at_gop_start.millis() as c_long;
333+
fts_at_gop_start = timing_info.fts_at_gop_start.millis();
334334
gop_rollover = if timing_info.gop_rollover { 1 } else { 0 };
335335
timing_settings.disable_sync_check = if timing_info.timing_settings.disable_sync_check {
336336
1
@@ -412,7 +412,7 @@ pub unsafe fn write_gop_time_code(g: Option<GopTimeCode>) -> gop_time_code {
412412
///
413413
/// `ctx` must not be null.
414414
#[no_mangle]
415-
pub unsafe extern "C" fn ccxr_add_current_pts(ctx: *mut ccx_common_timing_ctx, pts: c_long) {
415+
pub unsafe extern "C" fn ccxr_add_current_pts(ctx: *mut ccx_common_timing_ctx, pts: i64) {
416416
apply_timing_info();
417417
let mut context = generate_timing_context(ctx);
418418

@@ -428,7 +428,7 @@ pub unsafe extern "C" fn ccxr_add_current_pts(ctx: *mut ccx_common_timing_ctx, p
428428
///
429429
/// `ctx` must not be null.
430430
#[no_mangle]
431-
pub unsafe extern "C" fn ccxr_set_current_pts(ctx: *mut ccx_common_timing_ctx, pts: c_long) {
431+
pub unsafe extern "C" fn ccxr_set_current_pts(ctx: *mut ccx_common_timing_ctx, pts: i64) {
432432
apply_timing_info();
433433
let mut context = generate_timing_context(ctx);
434434

@@ -469,7 +469,7 @@ pub unsafe extern "C" fn ccxr_set_fts(ctx: *mut ccx_common_timing_ctx) -> c_int
469469
pub unsafe extern "C" fn ccxr_get_fts(
470470
ctx: *mut ccx_common_timing_ctx,
471471
current_field: c_int,
472-
) -> c_long {
472+
) -> i64 {
473473
apply_timing_info();
474474
let mut context = generate_timing_context(ctx);
475475

@@ -485,7 +485,7 @@ pub unsafe extern "C" fn ccxr_get_fts(
485485
write_back_to_common_timing_ctx(ctx, &context);
486486
write_back_from_timing_info();
487487

488-
ans.millis().try_into().unwrap_or(0)
488+
ans.millis()
489489
}
490490

491491
/// Rust equivalent for `get_visible_end` function in C. Uses C-native types as input and output.
@@ -503,7 +503,7 @@ pub unsafe extern "C" fn ccxr_get_fts(
503503
pub unsafe extern "C" fn ccxr_get_visible_end(
504504
ctx: *mut ccx_common_timing_ctx,
505505
_current_field: c_int,
506-
) -> c_long {
506+
) -> i64 {
507507
apply_timing_info();
508508
let mut context = generate_timing_context(ctx);
509509

@@ -518,7 +518,7 @@ pub unsafe extern "C" fn ccxr_get_visible_end(
518518
write_back_to_common_timing_ctx(ctx, &context);
519519
write_back_from_timing_info();
520520

521-
fts as c_long
521+
fts
522522
}
523523

524524
/// Rust equivalent for `get_visible_start` function in C. Uses C-native types as input and output.
@@ -537,7 +537,7 @@ pub unsafe extern "C" fn ccxr_get_visible_end(
537537
pub unsafe extern "C" fn ccxr_get_visible_start(
538538
ctx: *mut ccx_common_timing_ctx,
539539
_current_field: c_int,
540-
) -> c_long {
540+
) -> i64 {
541541
apply_timing_info();
542542
let context = generate_timing_context(ctx);
543543

@@ -554,7 +554,7 @@ pub unsafe extern "C" fn ccxr_get_visible_start(
554554
write_back_to_common_timing_ctx(ctx, &context);
555555
write_back_from_timing_info();
556556

557-
fts as c_long
557+
fts
558558
}
559559

560560
/// Rust equivalent for `get_fts_max` function in C. Uses C-native types as input and output.
@@ -563,7 +563,7 @@ pub unsafe extern "C" fn ccxr_get_visible_start(
563563
///
564564
/// `ctx` must not be null.
565565
#[no_mangle]
566-
pub unsafe extern "C" fn ccxr_get_fts_max(ctx: *mut ccx_common_timing_ctx) -> c_long {
566+
pub unsafe extern "C" fn ccxr_get_fts_max(ctx: *mut ccx_common_timing_ctx) -> i64 {
567567
apply_timing_info();
568568
let mut context = generate_timing_context(ctx);
569569

@@ -572,7 +572,7 @@ pub unsafe extern "C" fn ccxr_get_fts_max(ctx: *mut ccx_common_timing_ctx) -> c_
572572
write_back_to_common_timing_ctx(ctx, &context);
573573
write_back_from_timing_info();
574574

575-
ans.millis().try_into().unwrap_or(0)
575+
ans.millis()
576576
}
577577

578578
/// Rust equivalent for `print_mstime_static` function in C. Uses C-native types as input and output.
@@ -581,8 +581,8 @@ pub unsafe extern "C" fn ccxr_get_fts_max(ctx: *mut ccx_common_timing_ctx) -> c_
581581
///
582582
/// `buf` must not be null. It must have sufficient length to hold the time in string form.
583583
#[no_mangle]
584-
pub unsafe extern "C" fn ccxr_print_mstime_static(mstime: c_long, buf: *mut c_char) -> *mut c_char {
585-
let time = Timestamp::from_millis(mstime.into());
584+
pub unsafe extern "C" fn ccxr_print_mstime_static(mstime: i64, buf: *mut c_char) -> *mut c_char {
585+
let time = Timestamp::from_millis(mstime);
586586
let ans = c::print_mstime_static(time, ':');
587587
write_string_into_pointer(buf, &ans);
588588
buf

0 commit comments

Comments
 (0)