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
4 changes: 4 additions & 0 deletions src/lib_ccx/ccx_encoders_common.c
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,10 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
case CCX_OF_CCD:
ret = write(out->fh, ctx->encoded_crlf, ctx->encoded_crlf_length);
break;
case CCX_OF_WEBVTT:
// Ensure header is written even if no subtitles were found
write_webvtt_header(ctx, out);
break;
default: // Nothing to do, no footer on this format
break;
}
Expand Down
1 change: 1 addition & 0 deletions src/lib_ccx/ccx_encoders_common.h
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ int write_cc_bitmap_as_spupng (struct cc_subtitle *sub, struct encoder_
int write_cc_bitmap_as_transcript (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_libcurl (struct cc_subtitle *sub, struct encoder_ctx *context);

void write_webvtt_header(struct encoder_ctx *context, struct ccx_s_write *out);
void write_spumux_header(struct encoder_ctx *ctx, struct ccx_s_write *out);
void write_spumux_footer(struct ccx_s_write *out);

Expand Down
51 changes: 32 additions & 19 deletions src/lib_ccx/ccx_encoders_webvtt.c
Original file line number Diff line number Diff line change
Expand Up @@ -202,36 +202,49 @@ int write_stringz_as_webvtt(char *string, struct encoder_ctx *context, LLONG ms_
return 0;
}

void write_webvtt_header(struct encoder_ctx *context)
void write_webvtt_header(struct encoder_ctx *context, struct ccx_s_write *out)
{
if (context->wrote_webvtt_header) // Already done
return;

if (ccx_options.timestamp_map && context->timing != NULL && context->timing->sync_pts2fts_set)
// Use the provided output handle, or fall back to context->out if not provided
int fh = (out != NULL) ? out->fh : context->out->fh;

if (ccx_options.timestamp_map)
{
char header_string[200];
int used;
unsigned h1, m1, s1, ms1;
millis_to_time(context->timing->sync_pts2fts_fts, &h1, &m1, &s1, &ms1);

// If the user has enabled X-TIMESTAMP-MAP
sprintf(header_string, "X-TIMESTAMP-MAP=MPEGTS:%ld,LOCAL:%02u:%02u:%02u.%03u%s",
context->timing->sync_pts2fts_pts, h1, m1, s1, ms1,
ccx_options.enc_cfg.line_terminator_lf ? "\n\n" : "\r\n\r\n");
if (context->timing != NULL && context->timing->sync_pts2fts_set)
{
unsigned h1, m1, s1, ms1;
millis_to_time(context->timing->sync_pts2fts_fts, &h1, &m1, &s1, &ms1);

// X-TIMESTAMP-MAP with actual timing info from the stream
sprintf(header_string, "X-TIMESTAMP-MAP=MPEGTS:%ld,LOCAL:%02u:%02u:%02u.%03u%s",
context->timing->sync_pts2fts_pts, h1, m1, s1, ms1,
ccx_options.enc_cfg.line_terminator_lf ? "\n\n" : "\r\n\r\n");
}
else
{
// Default X-TIMESTAMP-MAP when no timing info available (e.g., no subtitles found)
sprintf(header_string, "X-TIMESTAMP-MAP=MPEGTS:0,LOCAL:00:00:00.000%s",
ccx_options.enc_cfg.line_terminator_lf ? "\n\n" : "\r\n\r\n");
}

used = encode_line(context, context->buffer, (unsigned char *)header_string);
write_wrapped(context->out->fh, context->buffer, used);
write_wrapped(fh, context->buffer, used);
}
else
{
// Must have another newline if X-TIMESTAMP-MAP is not used
if (ccx_options.enc_cfg.line_terminator_lf == 1) // If -lf parameter is set.
{
write_wrapped(context->out->fh, "\n", 1);
write_wrapped(fh, "\n", 1);
}
else
{
write_wrapped(context->out->fh, "\r\n", 2);
write_wrapped(fh, "\r\n", 2);
}
}

Expand All @@ -252,21 +265,21 @@ void write_webvtt_header(struct encoder_ctx *context)

char *outline_css_file = (char *)malloc((strlen(css_file_name) + strlen(webvtt_outline_css)) * sizeof(char));
sprintf(outline_css_file, webvtt_outline_css, css_file_name);
write_wrapped(context->out->fh, outline_css_file, strlen(outline_css_file));
write_wrapped(fh, outline_css_file, strlen(outline_css_file));
}
else if (ccx_options.use_webvtt_styling)
{
write_wrapped(context->out->fh, webvtt_inline_css, strlen(webvtt_inline_css));
write_wrapped(fh, webvtt_inline_css, strlen(webvtt_inline_css));
if (ccx_options.enc_cfg.line_terminator_lf == 1) // If -lf parameter is set.
{
write_wrapped(context->out->fh, "\n", 1);
write_wrapped(fh, "\n", 1);
}
else
{
write_wrapped(context->out->fh, "\r\n", 2);
write_wrapped(fh, "\r\n", 2);
}
write_wrapped(context->out->fh, "##\n", 3);
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
write_wrapped(fh, "##\n", 3);
write_wrapped(fh, context->encoded_crlf, context->encoded_crlf_length);
}

context->wrote_webvtt_header = 1; // Do it even if couldn't write the header, because it won't be possible anyway
Expand All @@ -288,7 +301,7 @@ int write_cc_bitmap_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *conte
if (sub->nb_data == 0)
return 0;

write_webvtt_header(context);
write_webvtt_header(context, NULL);

if (sub->flags & SUB_EOD_MARKER)
context->prev_start = sub->start_time;
Expand Down Expand Up @@ -425,7 +438,7 @@ int write_cc_buffer_as_webvtt(struct eia608_screen *data, struct encoder_ctx *co
if (empty_buf) // Prevent writing empty screens. Not needed in .vtt
return 0;

write_webvtt_header(context);
write_webvtt_header(context, NULL);

millis_to_time(data->start_time, &h1, &m1, &s1, &ms1);
millis_to_time(data->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
Expand Down
12 changes: 12 additions & 0 deletions src/lib_ccx/lib_ccx.c
Original file line number Diff line number Diff line change
Expand Up @@ -209,6 +209,7 @@ void dinit_libraries(struct lib_ccx_ctx **ctx)
{
struct lib_ccx_ctx *lctx = *ctx;
struct encoder_ctx *enc_ctx;
struct encoder_ctx *enc_ctx1;
struct lib_cc_decode *dec_ctx;
struct lib_cc_decode *dec_ctx1;
int i;
Expand Down Expand Up @@ -240,6 +241,17 @@ void dinit_libraries(struct lib_ccx_ctx **ctx)
}
}

// Cleanup any remaining encoders that weren't associated with a decoder
// (e.g., when no subtitles were found but output file was created)
if (!list_empty(&lctx->enc_ctx_head))
{
list_for_each_entry_safe(enc_ctx, enc_ctx1, &lctx->enc_ctx_head, list, struct encoder_ctx)
{
list_del(&enc_ctx->list);
dinit_encoder(&enc_ctx, 0);
}
}

// free EPG memory
EPG_free(lctx);
freep(&lctx->freport.data_from_608);
Expand Down
Loading