diff --git a/docs/CHANGES.TXT b/docs/CHANGES.TXT index 593f5bc0..4d114298 100644 --- a/docs/CHANGES.TXT +++ b/docs/CHANGES.TXT @@ -1,5 +1,6 @@ 0.96.7 (unreleased) ------------------- +- New: Allow output \0 terminated frames via --null-terminated - New: Added ASS/SSA \pos-based positioning for CEA-608 captions when layout is simple (1–2 rows) (#1726) - Fix: Remove strdup() memory leaks in WebVTT styling encoder, fix invalid CSS rgba(0,256,0) green value, fix missing free(unescaped) on write-error path (#2154) - Fix: Prevent crash in Rust timing module when logging out-of-range PTS/FTS timestamps from malformed streams. diff --git a/src/lib_ccx/ccx_common_option.c b/src/lib_ccx/ccx_common_option.c index 0eda6ab0..07660945 100644 --- a/src/lib_ccx/ccx_common_option.c +++ b/src/lib_ccx/ccx_common_option.c @@ -134,6 +134,7 @@ void init_options(struct ccx_s_options *options) options->enc_cfg.trim_subs = 0; // " Remove spaces at sides? " options->enc_cfg.in_format = 1; options->enc_cfg.line_terminator_lf = 0; // 0 = CRLF + options->enc_cfg.frame_terminator_0 = 0; // 0 = frames terminated by line_terminator_lf options->enc_cfg.start_credits_text = NULL; options->enc_cfg.end_credits_text = NULL; options->enc_cfg.encoding = CCX_ENC_UTF_8; diff --git a/src/lib_ccx/ccx_common_option.h b/src/lib_ccx/ccx_common_option.h index 85e0f156..d81d1409 100644 --- a/src/lib_ccx/ccx_common_option.h +++ b/src/lib_ccx/ccx_common_option.h @@ -67,6 +67,7 @@ struct encoder_cfg int no_type_setting; int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call. int line_terminator_lf; // 0 = CRLF, 1=LF + int frame_terminator_0; // 0 = frames terminated by line_terminator_lf, 1 = frames terminated by \0 LLONG subs_delay; // ms to delay (or advance) subs int program_number; unsigned char in_format; diff --git a/src/lib_ccx/ccx_encoders_common.c b/src/lib_ccx/ccx_encoders_common.c index 411526d4..8cb4b1f0 100644 --- a/src/lib_ccx/ccx_encoders_common.c +++ b/src/lib_ccx/ccx_encoders_common.c @@ -843,6 +843,17 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt) ctx->encoded_br_length = encode_line(ctx, ctx->encoded_br, (unsigned char *)"
"); + if (opt->frame_terminator_0) + { + ctx->encoded_end_frame[0] = '\0'; + ctx->encoded_end_frame_length = 1; + } + else + { + memcpy(ctx->encoded_end_frame, ctx->encoded_crlf, ctx->encoded_crlf_length + 1); + ctx->encoded_end_frame_length = ctx->encoded_crlf_length; + } + for (i = 0; i < ctx->nb_out; i++) write_subtitle_file_header(ctx, ctx->out + i); diff --git a/src/lib_ccx/ccx_encoders_common.h b/src/lib_ccx/ccx_encoders_common.h index b58f2bb0..2165d23e 100644 --- a/src/lib_ccx/ccx_encoders_common.h +++ b/src/lib_ccx/ccx_encoders_common.h @@ -146,6 +146,8 @@ struct encoder_ctx unsigned int encoded_crlf_length; unsigned char encoded_br[16]; unsigned int encoded_br_length; + unsigned char encoded_end_frame[16]; + unsigned int encoded_end_frame_length; // MCC File int header_printed_flag; diff --git a/src/lib_ccx/ccx_encoders_transcript.c b/src/lib_ccx/ccx_encoders_transcript.c index 81f1dc94..3e13652c 100644 --- a/src/lib_ccx/ccx_encoders_transcript.c +++ b/src/lib_ccx/ccx_encoders_transcript.c @@ -92,7 +92,7 @@ int write_cc_bitmap_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *c } } - write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length); + write_wrapped(context->out->fh, context->encoded_end_frame, context->encoded_end_frame_length); } } #endif @@ -134,6 +134,7 @@ int write_cc_subtitle_as_transcript(struct cc_subtitle *sub, struct encoder_ctx str = sub->data; + int wrote_something = 0; str = strtok_r(str, "\r\n", &save_str); do { @@ -143,6 +144,15 @@ int write_cc_subtitle_as_transcript(struct cc_subtitle *sub, struct encoder_ctx continue; } + if (wrote_something) + { + ret = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length); + if (ret < context->encoded_crlf_length) + { + mprint("Warning:Loss of data\n"); + } + } + if (context->transcript_settings->showStartTime) { char buf[80]; @@ -200,14 +210,16 @@ int write_cc_subtitle_as_transcript(struct cc_subtitle *sub, struct encoder_ctx mprint("Warning:Loss of data\n"); } - ret = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length); - if (ret < context->encoded_crlf_length) - { - mprint("Warning:Loss of data\n"); - } + wrote_something = 1; } while ((str = strtok_r(NULL, "\r\n", &save_str))); + ret = write(context->out->fh, context->encoded_end_frame, context->encoded_end_frame_length); + if (ret < context->encoded_end_frame_length) + { + mprint("Warning:Loss of data\n"); + } + freep(&sub->data); lsub = sub; sub = sub->next; @@ -321,18 +333,13 @@ void write_cc_line_as_transcript2(struct eia608_screen *data, struct encoder_ctx { mprint("Warning:Loss of data\n"); } - - ret = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length); - if (ret < context->encoded_crlf_length) - { - mprint("Warning:Loss of data\n"); - } } // fprintf (wb->fh,encoded_crlf); } int write_cc_buffer_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context) { + int ret; int wrote_something = 0; dbg_print(CCX_DMT_DECODER_608, "\n- - - TRANSCRIPT caption - - -\n"); @@ -340,10 +347,29 @@ int write_cc_buffer_as_transcript2(struct eia608_screen *data, struct encoder_ct { if (data->row_used[i]) { + if (wrote_something) + { + ret = write(context->out->fh, context->encoded_crlf, context->encoded_crlf_length); + if (ret < context->encoded_crlf_length) + { + mprint("Warning:Loss of data\n"); + } + } + write_cc_line_as_transcript2(data, context, i); + wrote_something = 1; } - wrote_something = 1; } + + if (wrote_something) + { + ret = write(context->out->fh, context->encoded_end_frame, context->encoded_end_frame_length); + if (ret < context->encoded_end_frame_length) + { + mprint("Warning:Loss of data\n"); + } + } + dbg_print(CCX_DMT_DECODER_608, "- - - - - - - - - - - -\r\n"); return wrote_something; } diff --git a/src/lib_ccx/params.c b/src/lib_ccx/params.c index f344cf16..e9399e7c 100644 --- a/src/lib_ccx/params.c +++ b/src/lib_ccx/params.c @@ -347,6 +347,9 @@ void print_usage(void) mprint(" to the output file.\n"); mprint(" --lf: Use LF (UNIX) instead of CRLF (DOS, Windows) as line\n"); mprint(" terminator.\n"); + mprint(" --null-terminated: Use \\0 instead of CRLF or LF for frame termination (see '--lf').\n"); + mprint(" e.g use '--txt --stdout --null-terminated' when piping to\n"); + mprint(" 'websocat -0' (https://github.com/vi/websocat)\n"); mprint(" --df: For MCC Files, force dropframe frame count.\n"); mprint(" --autodash: Based on position on screen, attempt to determine\n"); mprint(" the different speakers and a dash (-) when each\n"); diff --git a/src/rust/lib_ccxr/src/common/options.rs b/src/rust/lib_ccxr/src/common/options.rs index 4a15e37c..48b3f021 100644 --- a/src/rust/lib_ccxr/src/common/options.rs +++ b/src/rust/lib_ccxr/src/common/options.rs @@ -248,6 +248,7 @@ impl Default for EncoderConfig { no_type_setting: false, cc_to_stdout: false, line_terminator_lf: false, + frame_terminator_0: false, subs_delay: Timestamp::default(), program_number: 0, in_format: 1, @@ -324,6 +325,8 @@ pub struct EncoderConfig { pub cc_to_stdout: bool, /// false = CRLF, true = LF pub line_terminator_lf: bool, + /// false = frames terminated by line_terminator_lf, true = frames terminated by \0 + pub frame_terminator_0: bool, /// ms to delay (or advance) subs pub subs_delay: Timestamp, pub program_number: u32, diff --git a/src/rust/src/args.rs b/src/rust/src/args.rs index ae1b8405..c221ed9f 100644 --- a/src/rust/src/args.rs +++ b/src/rust/src/args.rs @@ -565,6 +565,9 @@ pub struct Args { /// terminator. #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] pub lf: bool, + /// Use \0 instead of CRLF or LF for frame termination (see '--lf') + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub null_terminated: bool, /// For MCC Files, force dropframe frame count. #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] pub df: bool, diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs index aa5df41f..9bb92c72 100755 --- a/src/rust/src/common.rs +++ b/src/rust/src/common.rs @@ -960,6 +960,7 @@ impl CType for EncoderConfig { no_type_setting: self.no_type_setting as _, cc_to_stdout: self.cc_to_stdout as _, line_terminator_lf: self.line_terminator_lf as _, + frame_terminator_0: self.frame_terminator_0 as _, subs_delay: self.subs_delay.millis(), program_number: self.program_number as _, in_format: self.in_format, diff --git a/src/rust/src/ctorust.rs b/src/rust/src/ctorust.rs index f240c502..2e075e0d 100755 --- a/src/rust/src/ctorust.rs +++ b/src/rust/src/ctorust.rs @@ -442,6 +442,7 @@ impl FromCType for EncoderConfig { no_type_setting: cfg.no_type_setting != 0, cc_to_stdout: cfg.cc_to_stdout != 0, line_terminator_lf: cfg.line_terminator_lf != 0, + frame_terminator_0: cfg.frame_terminator_0 != 0, subs_delay: Timestamp::from_millis(cfg.subs_delay), program_number: cfg.program_number as u32, in_format: cfg.in_format, diff --git a/src/rust/src/decoder/output.rs b/src/rust/src/decoder/output.rs index 7cc2b6a7..031f381a 100644 --- a/src/rust/src/decoder/output.rs +++ b/src/rust/src/decoder/output.rs @@ -14,7 +14,7 @@ use log::{debug, warn}; pub struct Writer<'a> { pub cea_708_counter: &'a mut u32, pub subs_delay: LLONG, - pub crlf: String, + pub end_frame: Vec, pub write_format: ccx_output_format, pub writer_ctx: &'a mut dtvcc_writer_ctx, pub no_font_color: bool, @@ -25,6 +25,7 @@ pub struct Writer<'a> { impl<'a> Writer<'a> { /// Create a new writer context + #[allow(clippy::too_many_arguments)] pub fn new( cea_708_counter: &'a mut u32, subs_delay: LLONG, @@ -33,11 +34,12 @@ impl<'a> Writer<'a> { no_font_color: i32, transcript_settings: &'a ccx_encoders_transcript_format, no_bom: i32, + encoded_end_frame: &[u8], ) -> Self { Self { cea_708_counter, subs_delay, - crlf: "\r\n".to_owned(), + end_frame: encoded_end_frame.to_vec(), write_format, writer_ctx, no_font_color: is_true(no_font_color), diff --git a/src/rust/src/decoder/service_decoder.rs b/src/rust/src/decoder/service_decoder.rs index a4b04fdc..f57ab2c2 100644 --- a/src/rust/src/decoder/service_decoder.rs +++ b/src/rust/src/decoder/service_decoder.rs @@ -873,6 +873,7 @@ impl dtvcc_service_decoder { } else { &ccx_encoders_transcript_format::default() }; + let end_frame = &encoder.encoded_end_frame[..encoder.encoded_end_frame_length as usize]; let mut writer = Writer::new( &mut encoder.cea_708_counter, encoder.subs_delay, @@ -881,6 +882,7 @@ impl dtvcc_service_decoder { encoder.no_font_color, transcript_settings, encoder.no_bom, + end_frame, ); tv.writer_output(&mut writer).unwrap(); tv.clear(); @@ -1202,6 +1204,7 @@ impl dtvcc_service_decoder { let sn = tv.service_number; let writer_ctx = &mut encoder.dtvcc_writers[(sn - 1) as usize]; + let end_frame = &encoder.encoded_end_frame[..encoder.encoded_end_frame_length as usize]; let mut writer = Writer::new( &mut encoder.cea_708_counter, encoder.subs_delay, @@ -1210,6 +1213,7 @@ impl dtvcc_service_decoder { encoder.no_font_color, transcript_settings, encoder.no_bom, + end_frame, ); writer.write_done(); } diff --git a/src/rust/src/decoder/tv_screen.rs b/src/rust/src/decoder/tv_screen.rs index 7ee5ec53..df0e2e41 100644 --- a/src/rust/src/decoder/tv_screen.rs +++ b/src/rust/src/decoder/tv_screen.rs @@ -330,8 +330,13 @@ impl dtvcc_tv_screen { let time_show = get_time_str(self.time_ms_show); let time_hide = get_time_str(self.time_ms_hide); + let mut wrote_something = false; for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize { if !self.is_row_empty(row_index) { + if wrote_something { + writer.write_to_file(b"\r\n")?; + } + let mut buf = String::new(); if is_true(writer.transcript_settings.showStartTime) { @@ -350,9 +355,11 @@ impl dtvcc_tv_screen { } writer.write_to_file(buf.as_bytes())?; self.write_row(writer, row_index, false)?; - writer.write_to_file(b"\r\n")?; + wrote_something = true; } } + let end_frame = writer.end_frame.clone(); + writer.write_to_file(&end_frame)?; Ok(()) } @@ -800,6 +807,7 @@ mod test { 0, &transcript_settings, 0, + b"\r\n", ); // This should succeed without error (fd is valid, not -1) @@ -835,6 +843,7 @@ mod test { 0, &transcript_settings, 0, + b"\r\n", ); // This should return an error, not panic diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index 086a8d34..49fa7313 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -1257,6 +1257,10 @@ impl OptionsExt for Options { self.enc_cfg.line_terminator_lf = true; } + if args.null_terminated { + self.enc_cfg.frame_terminator_0 = true; + } + if args.df { self.enc_cfg.force_dropframe = true; }