Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions src/lib_ccx/ccx_decoders_708_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -538,6 +538,36 @@ void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s
// when hiding subtract a frame (1 frame = 34 ms)
struct ccx_boundary_time time_end = get_time(tv->time_ms_hide + encoder->subs_delay - 34);

/* ---- SCC accurate timing: byte budgeting ---- */
int bytes_required = 0;

/* Control codes: RCL + ENM + EOC (each = 2 bytes) */
bytes_required += 6;

for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
if (!dtvcc_is_row_empty(tv, i))
{
int first, last;
dtvcc_get_write_interval(tv, i, &first, &last);
bytes_required += (last - first + 1);
}
}

/* Pad to even number of bytes */
if (bytes_required % 2 != 0)
bytes_required++;

/* ---- SCC accurate timing: scheduling ---- */
int frames_required = (bytes_required + 1) / 2;
int tx_duration_ms = (int)(frames_required * (1000.0 / 29.97));

time_show.time_in_ms -= tx_duration_ms;

/* ---- SCC accurate timing: collision handling ---- */
if (encoder->last_scc_tx_end_ms >= 0 && time_show.time_in_ms < encoder->last_scc_tx_end_ms)
time_show.time_in_ms = encoder->last_scc_tx_end_ms;

#define SCC_SNPRINTF(fmt, ...) \
do \
{ \
Expand Down Expand Up @@ -614,6 +644,7 @@ void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s

#undef SCC_SNPRINTF
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
encoder->last_scc_tx_end_ms = tv->time_ms_show + encoder->subs_delay;

tv->old_cc_time_end = time_end.time_in_ms;
}
Expand Down
1 change: 1 addition & 0 deletions src/lib_ccx/ccx_encoders_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -829,6 +829,7 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt)

ctx->dtvcc_extract = opt->dtvcc_extract;

ctx->last_scc_tx_end_ms = -1;
ctx->segment_pending = 0;
ctx->segment_last_key_frame = 0;
ctx->nospupngocr = opt->nospupngocr;
Expand Down
2 changes: 2 additions & 0 deletions src/lib_ccx/ccx_encoders_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,8 @@ struct encoder_ctx
int sbs_enabled;

// for dvb subs

int last_scc_tx_end_ms;
struct encoder_ctx *prev;
int write_previous;
// for dvb in .mkv
Expand Down
40 changes: 39 additions & 1 deletion src/lib_ccx/ccx_encoders_scc.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,8 +550,44 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
unsigned char current_row = UINT8_MAX;
unsigned char current_column = UINT8_MAX;

/* ---- SCC accurate timing: byte budgeting ---- */
int bytes_required = 0;

/* Control codes: RCL + ENM + EOC (each = 2 bytes) */
bytes_required += 6;

/* Count text bytes and control codes needed */
for (uint8_t row = 0; row < 15; ++row)
{
if (!data->row_used[row])
continue;

int first, last;
find_limit_characters(data->characters[row], &first, &last, CCX_DECODER_608_SCREEN_WIDTH);

/* Preamble code (2 bytes) + potential tab offset (2 bytes) */
bytes_required += 4;

/* Text characters */
bytes_required += (last - first + 1);
}

/* Pad to even number of bytes */
if (bytes_required % 2 != 0)
bytes_required++;

/* ---- SCC accurate timing: scheduling ---- */
int frames_required = (bytes_required + 1) / 2;
int tx_duration_ms = (int)(frames_required * (1000.0 / 29.97));

LLONG adjusted_start_time = data->start_time - tx_duration_ms;

/* ---- SCC accurate timing: collision handling ---- */
if (context->last_scc_tx_end_ms >= 0 && adjusted_start_time < context->last_scc_tx_end_ms)
adjusted_start_time = context->last_scc_tx_end_ms;

// 1. Load the caption
add_timestamp(context, data->start_time, disassemble);
add_timestamp(context, adjusted_start_time, disassemble);
write_control_code(context->out->fh, data->channel, RCL, disassemble, &bytes_written);
for (uint8_t row = 0; row < 15; ++row)
{
Expand Down Expand Up @@ -624,6 +660,8 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
// 3. Clear the caption
clear_screen(context, data->end_time, data->channel, disassemble);

/* Track transmission end time */
context->last_scc_tx_end_ms = adjusted_start_time + tx_duration_ms;
return 1;
}

Expand Down
Loading