mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-04-17 19:43:50 +00:00
[PR #1808] fix(timing): correct caption start/end times to match video frame PTS #2550
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Original Pull Request: https://github.com/CCExtractor/ccextractor/pull/1808
State: closed
Merged: Yes
Summary
This PR fixes multiple timing accuracy issues where caption start times were offset from the actual video frame timestamps. The fixes ensure caption timing matches the authoritative reference (FFmpeg).
Problem 1: cb_field offset for container formats
The
get_visible_start()andget_visible_end()functions were adding acb_fieldoffset (cb_field * 1001/30ms) to caption timestamps. This offset was designed for broadcast MPEG-TS streams where caption data arrives continuously at field rate (59.94 fields/sec).However, for container formats like MP4, all caption data for a video frame is bundled together and should use the frame's PTS directly. The offset was causing:
Problem 2: Leading non-I-frames setting min_pts
Streams recorded mid-broadcast often start with trailing B/P frames from a previous GOP. These frames have earlier PTS values than the first decodable I-frame.
CCExtractor was setting
min_ptsfrom the first PES packet with a PTS, which could be an undecodable B/P frame. FFmpeg's cc_dec uses the first decoded frame (necessarily an I-frame) as its timing reference.Example from
c032183ef01...ts:Problem 3: Pop-on to roll-up mode transition timing
When transitioning from pop-on to roll-up mode, CCExtractor was setting the caption start time when the first character was typed. FFmpeg uses the time when the display state changed to show multiple lines. This caused the first roll-up caption after a mode switch to be timestamped too early (up to 484ms).
Problem 4: First CR timing in pop-on to roll-up transition
When the first CR command happens with only 1 line visible (changes=0),
ts_start_of_current_linewas reset to -1. This caused the next caption's start time to be set when characters were typed (~133ms later), not when the CR command was received.Problem 5: MP4 c608/c708 caption tracks with no video frames
MP4 files with dedicated c608/c708 caption tracks (separate from video) had broken timing because:
min_ptswas never set, staying at initial value (0x01FFFFFFFF)fts_nowcalculation produced huge negative valuesProblem 6: pts_set marked as MinPtsSet before min_pts was actually set
The code was setting
pts_set = MinPtsSetunconditionally, before actually settingmin_pts. This causedfts_nowcalculations to use the uninitializedmin_ptsvalue.Solution
Fix 1 (cb_field offset):
ccxr_get_visible_start()andccxr_get_visible_end()that return base FTS without cb_field offsetFix 2 (min_pts from I-frame only):
set_fts()in timing.rs to only setmin_ptswhencurrent_picture_coding_type == IFrameFix 3 (pop-on to roll-up transition):
rollup_from_poponflag to track mode transitionsts_start_of_current_linewhen buffer scrolls during transitionFix 4 (first CR timing):
rollup_from_popon=1andchanges=0(first CR with only 1 line)ts_start_of_current_lineto the CR timeFix 5 (MP4 c608/c708 tracks):
CCX_FRAME_TYPE_I_FRAMEfor caption-only tracks before callingset_fts()min_ptsto be set from the first caption samplepts_set = MinPtsSetAFTERmin_ptsis actually setfts_nowcalculation to only run whenpts_set == MinPtsSetFix 6 (garbage frame detection):
pending_min_ptsfor ALL frames (not just unknown type)pending_min_ptsand I-frame PTS:pending_min_ptsFiles Changed
src/rust/lib_ccxr/src/time/timing.rs- Only set min_pts from I-frames, defer until frame type known, garbage detectionsrc/rust/src/libccxr_exports/time.rs- Added new FFI functionssrc/rust/src/decoder/timing.rs- Updated timing functions + testssrc/lib_ccx/ccx_decoders_common.c- Don't increment cb_field for container formatssrc/lib_ccx/ccx_decoders_608.c- Handle pop-on to roll-up transition timing, preserve first CR timesrc/lib_ccx/ccx_decoders_608.h- Added rollup_from_popon flagsrc/lib_ccx/sequencing.c- Include CCX_PES in reset_cb logicsrc/lib_ccx/ccx_common_timing.c- Added extern declarationssrc/lib_ccx/mp4.c- Set frame type to I-frame for caption-only tracksVerification
Test 1 (cb_field offset fix):
Start time now matches FFmpeg exactly: 966.499s ✓
Test 2 (min_pts I-frame fix):
Test 3 (pop-on to roll-up transition):
Test 4 (first CR timing):
Test 5 (MP4 c608 track):
Test 6 (TS with B-frame reordering):
Summary of Timing Improvements
Known Limitations
MPEG-PS files (66ms offset): Two MPEG-PS test files show 66ms offset after fixes. Investigation shows FFmpeg uses the lowest PTS (B-frame) as reference for MPEG-PS files, while CCExtractor now uses I-frame PTS. This is a trade-off to fix the more significant issues in TS files.
WTV files (751ms offset): WTV files show a consistent 751ms timing offset. Investigation revealed this is caused by CCExtractor using the MSTV caption stream timing while FFmpeg uses video-embedded CEA-608 timing. These have different timestamp epochs in WTV containers. This is a pre-existing architectural difference and is marked as low priority for future work.
Raw H.264 elementary streams: Files without container timing (raw .h264) cannot have accurate timing as there are no PTS values to reference.
Test plan
🤖 Generated with Claude Code