fix(608): handle pop-on to roll-up transition ending at EOF (#2268)

When a stream switches from pop-on to roll-up mode and ends before
check_roll_up() reports changes=1 (fewer lines than the roll-up window
requires), the CR handler never runs the backfill at line 836 and
current_visible_start_ms is still zero when flush_608_context fires
EraseDisplayedMemory. Result: the first emitted caption is timestamped
at 00:00:00,000.

Capture the FTS of the first character after the transition in a new
field ts_first_char_rollup_transition. Unlike ts_start_of_current_line,
this field is not overwritten by intermediate changes=0 CRs, so the
first-char time survives to the end-of-stream flush path.
write_cc_buffer uses it to backfill current_visible_start_ms when
rollup_from_popon is still set at emit time, and clears it together
with the flag. The normal popon->roll-up flow (where a scrolling CR
eventually fires) and every other caption path are unchanged.

Verified on c4dd893cb9d6...ts: first subtitle now starts at
00:00:13,913 (was 00:00:00,000). Regression tested against six master
samples the mentor flagged (0069dffd.mpg 03,603; 5cbb21ad.dvr-ms
00,534; 132d7df7.mov 12,812; 6395b281.asf 02,436; 8849331d.mp4 03,771;
b22260d0.ts 04,838) - all match the reference timings unchanged.

Co-authored-by: GAURAV KARMAKAR <gaurav.k@graeon.ai>
This commit is contained in:
Gaurav karmakar
2026-04-22 11:23:37 +05:30
committed by GitHub
parent 283bfd9896
commit 689b27ce2f
2 changed files with 21 additions and 0 deletions

View File

@@ -150,6 +150,7 @@ ccx_decoder_608_context *ccx_decoder_608_init_library(struct ccx_decoder_608_set
data->my_channel = channel;
data->have_cursor_position = 0;
data->rollup_from_popon = 0;
data->ts_first_char_rollup_transition = -1;
data->output_format = output_format;
data->cc_to_stdout = cc_to_stdout;
data->textprinted = 0;
@@ -246,6 +247,12 @@ void write_char(const unsigned char c, ccx_decoder_608_context *context)
context->cursor_column++;
if (context->ts_start_of_current_line == -1)
context->ts_start_of_current_line = get_fts(context->timing, context->my_field);
// First char after a pop-on -> roll-up transition: remember its FTS so
// write_cc_buffer can use it if EOF fires before a scrolling CR.
// Unlike ts_start_of_current_line, this field is NOT touched by
// intermediate CR commands (changes=0 CRs overwrite ts_start_of_current_line).
if (context->rollup_from_popon && context->ts_first_char_rollup_transition == -1)
context->ts_first_char_rollup_transition = get_fts(context->timing, context->my_field);
context->ts_last_char_received = get_fts(context->timing, context->my_field);
}
}
@@ -311,6 +318,17 @@ int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub)
context->ts_start_of_current_line != -1)
context->current_visible_start_ms = context->ts_start_of_current_line;
// Pop-on -> roll-up transition that never saw a scrolling CR (e.g. EOF
// with fewer lines than the roll-up window). The CR handler never ran,
// so back-fill current_visible_start_ms from the first-char FTS instead
// of emitting a caption starting at 0.
if (context->rollup_from_popon && context->ts_first_char_rollup_transition > 0)
{
context->current_visible_start_ms = context->ts_first_char_rollup_transition;
context->rollup_from_popon = 0;
context->ts_first_char_rollup_transition = -1;
}
start_time = context->current_visible_start_ms;
end_time = get_visible_end(context->timing, context->my_field);
sub->type = CC_608;
@@ -758,6 +776,7 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
// Start time will be set when CR causes scrolling (matching FFmpeg behavior)
context->rollup_from_popon = 1;
context->ts_start_of_current_line = -1;
context->ts_first_char_rollup_transition = -1;
}
erase_memory(context, false);
@@ -817,6 +836,7 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
{
context->current_visible_start_ms = context->ts_start_of_current_line;
context->rollup_from_popon = 0;
context->ts_first_char_rollup_transition = -1;
}
// Only if the roll up would actually cause a line to disappear we write the buffer

View File

@@ -48,6 +48,7 @@ typedef struct ccx_decoder_608_context
int my_field; // Used for sanity checks
int my_channel; // Used for sanity checks
int rollup_from_popon; // Track transition from pop-on/paint-on to roll-up mode
LLONG ts_first_char_rollup_transition; // FTS of first char after pop-on -> roll-up (EOF fallback when no scrolling CR fires). -1 if unset.
int64_t bytes_processed_608; // To be written ONLY by process_608
int have_cursor_position;