From 161878867db8881b3739acb75a7f4e2a5dc4cfa7 Mon Sep 17 00:00:00 2001 From: Chandragupt Singh Date: Sat, 10 Jan 2026 01:44:06 +0530 Subject: [PATCH 1/4] feat(report): add machine-readable JSON output for -out=report --- src/lib_ccx/ccx_common_option.c | 1 + src/lib_ccx/ccx_common_option.h | 1 + src/lib_ccx/params_dump.c | 315 +++++++++++++++++++++++- src/rust/lib_ccxr/src/common/options.rs | 2 + src/rust/src/args.rs | 14 +- src/rust/src/common.rs | 10 + src/rust/src/parser.rs | 5 + 7 files changed, 342 insertions(+), 6 deletions(-) diff --git a/src/lib_ccx/ccx_common_option.c b/src/lib_ccx/ccx_common_option.c index ecfe17f7f..0eda6ab06 100644 --- a/src/lib_ccx/ccx_common_option.c +++ b/src/lib_ccx/ccx_common_option.c @@ -145,6 +145,7 @@ void init_options(struct ccx_s_options *options) options->enc_cfg.scc_framerate = 0; // Default: 29.97fps for SCC output options->enc_cfg.scc_accurate_timing = 0; // Default: off for backwards compatibility (issue #1120) options->enc_cfg.extract_only_708 = 0; + options->report_format = NULL; options->settings_dtvcc.enabled = 0; options->settings_dtvcc.active_services_count = 0; diff --git a/src/lib_ccx/ccx_common_option.h b/src/lib_ccx/ccx_common_option.h index aa7e14207..85e0f1562 100644 --- a/src/lib_ccx/ccx_common_option.h +++ b/src/lib_ccx/ccx_common_option.h @@ -189,6 +189,7 @@ struct ccx_s_options // Options from user parameters enum ccx_datasource input_source; // Files, stdin or network char *output_filename; + char *report_format; // NULL = default text, e.g. "json" char **inputfile; // List of files to process int num_input_files; // How many? diff --git a/src/lib_ccx/params_dump.c b/src/lib_ccx/params_dump.c index f679eea73..c4275a7ad 100644 --- a/src/lib_ccx/params_dump.c +++ b/src/lib_ccx/params_dump.c @@ -1,9 +1,83 @@ +#ifdef _WIN32 +#define strcasecmp _stricmp +#endif +#include #include "lib_ccx.h" #include "ccx_common_option.h" #include "teletext.h" - +#include +#include #include "ccx_decoders_708.h" +void print_file_report_json(struct lib_ccx_ctx *ctx); + +static void json_escape(const char *s) +{ + if (!s) + { + printf("null"); + return; + } + + putchar('"'); + for (; *s; s++) + { + switch (*s) + { + case '"': + printf("\\\""); + break; + case '\\': + printf("\\\\"); + break; + case '\b': + printf("\\b"); + break; + case '\f': + printf("\\f"); + break; + case '\n': + printf("\\n"); + break; + case '\r': + printf("\\r"); + break; + case '\t': + printf("\\t"); + break; + default: + if ((unsigned char)*s < 0x20) + printf("\\u%04x", (unsigned char)*s); + else + putchar(*s); + } + } + putchar('"'); +} + +static const char *stream_mode_to_string(enum ccx_stream_mode_enum mode) +{ + switch (mode) + { + case CCX_SM_TRANSPORT: + return "Transport Stream"; + case CCX_SM_PROGRAM: + return "Program Stream"; + case CCX_SM_ASF: + return "ASF"; + case CCX_SM_WTV: + return "WTV"; + case CCX_SM_MP4: + return "MP4"; + case CCX_SM_MCPOODLESRAW: + return "McPoodle Raw"; + case CCX_SM_RCWT: + return "BIN"; + default: + return "Unknown"; + } +} + void params_dump(struct lib_ccx_ctx *ctx) { // Display parsed parameters @@ -256,10 +330,246 @@ void print_cc_report(struct lib_ccx_ctx *ctx, struct cap_info *info) } } +void print_file_report_json(struct lib_ccx_ctx *ctx) +{ + struct ccx_demuxer *demux = ctx->demux_ctx; + struct cap_info *program; + + int real_pg_count = 0; + list_for_each_entry(program, &demux->cinfo_tree.pg_stream, pg_stream, struct cap_info) + { + real_pg_count++; + } + + struct cap_info **pg_array = malloc(sizeof(*pg_array) * real_pg_count); + if (!pg_array) + { + fprintf(stderr, "Out of memory while building report JSON\n"); + return; + } + + int idx = 0; + list_for_each_entry(program, &demux->cinfo_tree.pg_stream, pg_stream, struct cap_info) + { + pg_array[idx++] = program; + } + + /* sort by program number */ + for (int i = 0; i < real_pg_count - 1; i++) + { + for (int j = i + 1; j < real_pg_count; j++) + { + if (pg_array[i]->program_number > pg_array[j]->program_number) + { + struct cap_info *tmp = pg_array[i]; + pg_array[i] = pg_array[j]; + pg_array[j] = tmp; + } + } + } + + printf("{\n"); + /* schema */ + printf(" \"schema\": {\n"); + printf(" \"name\": \"ccextractor-report\",\n"); + printf(" \"version\": \"1.0\"\n"); + printf(" },\n"); + + /* input */ + printf(" \"input\": {\n"); + printf(" \"source\": "); + switch (ccx_options.input_source) + { + case CCX_DS_FILE: + printf("\"file\",\n"); + printf(" \"path\": "); + json_escape(ctx->inputfile[ctx->current_file]); + printf("\n"); + break; + case CCX_DS_STDIN: + printf("\"stdin\"\n"); + break; + default: + printf("\"network\"\n"); + break; + } + printf(" },\n"); + + /* stream */ + printf(" \"stream\": {\n"); + printf(" \"mode\": "); + json_escape(stream_mode_to_string(demux->stream_mode)); + printf(",\n"); + + printf(" \"program_count\": %d,\n", real_pg_count); + + printf(" \"program_numbers\": ["); + for (int i = 0; i < real_pg_count; i++) + { + if (i) + printf(", "); + printf("%u", pg_array[i]->program_number); + } + printf("],\n"); + + /* PIDs */ + printf(" \"pids\": [\n"); + int first = 1; + for (int pid = 0; pid < 65536; pid++) + { + if (!demux->PIDs_programs[pid]) + continue; + + if (!first) + printf(",\n"); + first = 0; + + printf(" { \"pid\": %d, \"program_number\": %u, \"codec\": ", + pid, + demux->PIDs_programs[pid]->program_number); + json_escape(desc[demux->PIDs_programs[pid]->printable_stream_type]); + printf(" }"); + } + printf("\n ]\n"); + printf(" },\n"); + + /* container-level metadata */ + if (ctx->freport.mp4_cc_track_cnt > 0) + { + printf(" \"container\": {\n"); + printf(" \"mp4\": {\n"); + printf(" \"timed_text_tracks\": %d\n", ctx->freport.mp4_cc_track_cnt); + printf(" }\n"); + printf(" },\n"); + } + + /* programs */ + printf(" \"programs\": [\n"); + first = 1; + + for (int pi = 0; pi < real_pg_count; pi++) + { + program = pg_array[pi]; + struct lib_cc_decode *dec_ctx; + struct cap_info *info; + + if (!first) + printf(",\n"); + first = 0; + + dec_ctx = update_decoder_list_cinfo(ctx, program); + + bool has_608 = (dec_ctx->cc_stats[0] || dec_ctx->cc_stats[1]); + bool has_708 = (dec_ctx->cc_stats[2] || dec_ctx->cc_stats[3]); + bool has_any_captions = has_608 || has_708; + + printf(" {\n"); + printf(" \"program_number\": %u,\n", program->program_number); + printf(" \"summary\": {\n"); + printf(" \"has_any_captions\": %s,\n", has_any_captions ? "true" : "false"); + printf(" \"has_608\": %s,\n", has_608 ? "true" : "false"); + printf(" \"has_708\": %s\n", has_708 ? "true" : "false"); + printf(" },\n"); + + printf(" \"services\": {\n"); + printf(" \"dvb_subtitles\": %s,\n", + get_sib_stream_by_type(program, CCX_CODEC_DVB) ? "true" : "false"); + printf(" \"teletext\": %s,\n", + get_sib_stream_by_type(program, CCX_CODEC_TELETEXT) ? "true" : "false"); + printf(" \"atsc_closed_caption\": %s\n", + get_sib_stream_by_type(program, CCX_CODEC_ATSC_CC) ? "true" : "false"); + printf(" },\n"); + + printf(" \"captions\": {\n"); + printf(" \"present\": %s,\n", has_any_captions ? "true" : "false"); + + printf(" \"eia_608\": {\n"); + printf(" \"present\": %s,\n", has_608 ? "true" : "false"); + + // NOTE: EIA-608 / CEA-708 data is currently stream-global and therefore + // identical across programs in multi-program streams. + printf(" \"xds\": %s,\n", + ctx->freport.data_from_608->xds ? "true" : "false"); + printf(" \"channels\": {\n"); + printf(" \"cc1\": %s,\n", ctx->freport.data_from_608->cc_channels[0] ? "true" : "false"); + printf(" \"cc2\": %s,\n", ctx->freport.data_from_608->cc_channels[1] ? "true" : "false"); + printf(" \"cc3\": %s,\n", ctx->freport.data_from_608->cc_channels[2] ? "true" : "false"); + printf(" \"cc4\": %s\n", ctx->freport.data_from_608->cc_channels[3] ? "true" : "false"); + printf(" }\n"); + printf(" },\n"); + + printf(" \"cea_708\": {\n"); + printf(" \"present\": %s,\n", has_708 ? "true" : "false"); + printf(" \"services\": ["); + int sf = 1; + for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++) + { + if (!ctx->freport.data_from_708->services[i]) + continue; + if (!sf) + printf(", "); + sf = 0; + printf("%d", i); + } + printf("]\n"); + printf(" }\n"); // end cea_708 + printf(" }"); // end captions + + // Decide upfront if video will actually be printed + bool print_video = false; + info = get_best_sib_stream(program); + + if (info) + { + dec_ctx = update_decoder_list_cinfo(ctx, info); + if (dec_ctx->in_bufferdatatype == CCX_PES && + (demux->stream_mode == CCX_SM_TRANSPORT || + demux->stream_mode == CCX_SM_PROGRAM || + demux->stream_mode == CCX_SM_ASF || + demux->stream_mode == CCX_SM_WTV)) + { + print_video = true; + } + } + + if (print_video) + { + printf(",\n"); // comma ONLY because video follows + printf(" \"video\": {\n"); + printf(" \"width\": %u,\n", dec_ctx->current_hor_size); + printf(" \"height\": %u,\n", dec_ctx->current_vert_size); + printf(" \"aspect_ratio\": "); + json_escape(aspect_ratio_types[dec_ctx->current_aspect_ratio]); + printf(",\n"); + printf(" \"frame_rate\": "); + json_escape(framerates_types[dec_ctx->current_frame_rate]); + printf("\n"); + printf(" }\n"); + } + else + { + printf("\n"); // no video, just newline + } + + printf(" }"); // end program object + } + + printf("\n ]\n"); + printf("}\n"); + + free(pg_array); +} + void print_file_report(struct lib_ccx_ctx *ctx) { struct lib_cc_decode *dec_ctx = NULL; struct ccx_demuxer *demux_ctx = ctx->demux_ctx; + const char *report_fmt = ccx_options.report_format; + if (report_fmt && strcasecmp(report_fmt, "json") == 0) + { + print_file_report_json(ctx); + goto cleanup; + } printf("File: "); switch (ccx_options.input_source) @@ -422,7 +732,8 @@ void print_file_report(struct lib_ccx_ctx *ctx) printf("MPEG-4 Timed Text tracks count: %d\n", ctx->freport.mp4_cc_track_cnt); } +cleanup: freep(&ctx->freport.data_from_608); memset(&ctx->freport, 0, sizeof(struct file_report)); #undef Y_N -} +} \ No newline at end of file diff --git a/src/rust/lib_ccxr/src/common/options.rs b/src/rust/lib_ccxr/src/common/options.rs index 90bd3d215..4a15e37c8 100644 --- a/src/rust/lib_ccxr/src/common/options.rs +++ b/src/rust/lib_ccxr/src/common/options.rs @@ -367,6 +367,7 @@ pub struct Options { /// The end of the segment we actually process pub extraction_end: Option, pub print_file_reports: bool, + pub report_format: Option, /// Contains the settings for the 608 decoder. pub settings_608: Decoder608Settings, /// Same for 708 decoder @@ -631,6 +632,7 @@ impl Default for Options { segment_on_key_frames_only: Default::default(), scc_framerate: 0, // 0 = 29.97fps (default) scc_accurate_timing: false, // Off by default for backwards compatibility (issue #1120) + report_format: None, debug_mask: DebugMessageMask::new( DebugMessageFlag::GENERIC_NOTICE, DebugMessageFlag::VERBOSE, diff --git a/src/rust/src/args.rs b/src/rust/src/args.rs index b7e9e4f39..bc32dc80f 100644 --- a/src/rust/src/args.rs +++ b/src/rust/src/args.rs @@ -250,10 +250,16 @@ pub struct Args { pub mkv: bool, #[arg(long, hide = true)] pub dvr_ms: bool, - #[arg(long, value_name="format", help_heading=OUTPUT_FORMATS)] - pub out: Option, - #[arg(long, hide = true)] - pub srt: bool, +#[arg(long, value_name="format", help_heading=OUTPUT_FORMATS)] +pub out: Option, + +/// Format for -out=report output (e.g. json) +#[arg(long = "report-format", value_name = "FORMAT", help_heading=OUTPUT_FORMATS)] +pub report_format: Option, + +#[arg(long, hide = true)] +pub srt: bool, + #[arg(long, hide = true)] pub webvtt: bool, #[arg(long, hide = true)] diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs index 0f22e50c2..7ad589846 100755 --- a/src/rust/src/common.rs +++ b/src/rust/src/common.rs @@ -81,6 +81,11 @@ pub unsafe fn copy_from_rust(ccx_s_options: *mut ccx_s_options, options: Options (*ccx_s_options).extraction_start = options.extraction_start.to_ctype(); (*ccx_s_options).extraction_end = options.extraction_end.to_ctype(); (*ccx_s_options).print_file_reports = options.print_file_reports as _; + // Report output format (e.g. "json") + if let Some(ref fmt) = options.report_format { + (*ccx_s_options).report_format = + replace_rust_c_string((*ccx_s_options).report_format, fmt.as_str()); + } // Preserve the original C-managed report pointer to avoid dangling pointer issues. let saved_608_report = (*ccx_s_options).settings_608.report; (*ccx_s_options).settings_608 = options.settings_608.to_ctype(); @@ -323,6 +328,7 @@ pub unsafe fn copy_to_rust(ccx_s_options: *const ccx_s_options) -> Options { .expect("Invalid extraction end time"), ), print_file_reports: (*ccx_s_options).print_file_reports != 0, + report_format: None, // Handle settings_608 and settings_dtvcc - assuming FromCType trait is implemented for these settings_608: Decoder608Settings::from_ctype((*ccx_s_options).settings_608) .unwrap_or(Decoder608Settings::default()), @@ -344,6 +350,10 @@ pub unsafe fn copy_to_rust(ccx_s_options: *const ccx_s_options) -> Options { ..Default::default() }; + if !(*ccx_s_options).report_format.is_null() { + options.report_format = Some(c_char_to_string((*ccx_s_options).report_format)); + } + // Handle sentence_cap_file (C string to PathBuf) if !(*ccx_s_options).sentence_cap_file.is_null() { options.sentence_cap_file = diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index 47573a996..c18bcef11 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -773,6 +773,11 @@ impl OptionsExt for Options { self.set_output_format(args); } + // --- report-format (used by -out=report) --- + if let Some(ref fmt) = args.report_format { + self.report_format = Some(fmt.to_lowercase()); + } + if let Some(ref startcreditstext) = args.startcreditstext { self.enc_cfg.start_credits_text.clone_from(startcreditstext); } From 556392a9fe1901a69d5f8b56886fce372a6472b0 Mon Sep 17 00:00:00 2001 From: Chandragupt Singh Date: Thu, 15 Jan 2026 03:35:09 +0530 Subject: [PATCH 2/4] docs(changelog): mention JSON output support for -out=report --- docs/CHANGES.TXT | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/CHANGES.TXT b/docs/CHANGES.TXT index 053b01d26..de01ec04e 100644 --- a/docs/CHANGES.TXT +++ b/docs/CHANGES.TXT @@ -1,5 +1,6 @@ 0.96.6 (unreleased) ------------------- +- Add optional machine-readable JSON output for -out=report via --report-format json - New: Add Snap packaging support with Snapcraft configuration and GitHub Actions CI workflow. - Fix: Clear status line output on Linux/WSL to prevent text artifacts (#2017) - Fix: Prevent infinite loop on truncated MKV files From f82c231c1cc70974ad918ac580e43a747e7eae03 Mon Sep 17 00:00:00 2001 From: Chandragupt Singh Date: Wed, 4 Feb 2026 00:29:25 +0530 Subject: [PATCH 3/4] fix(report): address program count and caption summary issues in JSON output --- src/lib_ccx/params_dump.c | 146 +++++++++++++++++++++++--------------- 1 file changed, 89 insertions(+), 57 deletions(-) diff --git a/src/lib_ccx/params_dump.c b/src/lib_ccx/params_dump.c index c4275a7ad..8063721a8 100644 --- a/src/lib_ccx/params_dump.c +++ b/src/lib_ccx/params_dump.c @@ -333,37 +333,33 @@ void print_cc_report(struct lib_ccx_ctx *ctx, struct cap_info *info) void print_file_report_json(struct lib_ccx_ctx *ctx) { struct ccx_demuxer *demux = ctx->demux_ctx; - struct cap_info *program; - int real_pg_count = 0; - list_for_each_entry(program, &demux->cinfo_tree.pg_stream, pg_stream, struct cap_info) - { - real_pg_count++; - } + // Use PAT-based program count + int program_count = demux->nb_program; - struct cap_info **pg_array = malloc(sizeof(*pg_array) * real_pg_count); - if (!pg_array) + // Build array of program numbers from PAT + unsigned *program_numbers = malloc(sizeof(unsigned) * program_count); + if (!program_numbers) { fprintf(stderr, "Out of memory while building report JSON\n"); return; } - int idx = 0; - list_for_each_entry(program, &demux->cinfo_tree.pg_stream, pg_stream, struct cap_info) + for (int i = 0; i < program_count; i++) { - pg_array[idx++] = program; + program_numbers[i] = demux->pinfo[i].program_number; } - /* sort by program number */ - for (int i = 0; i < real_pg_count - 1; i++) + // Sort program numbers + for (int i = 0; i < program_count - 1; i++) { - for (int j = i + 1; j < real_pg_count; j++) + for (int j = i + 1; j < program_count; j++) { - if (pg_array[i]->program_number > pg_array[j]->program_number) + if (program_numbers[i] > program_numbers[j]) { - struct cap_info *tmp = pg_array[i]; - pg_array[i] = pg_array[j]; - pg_array[j] = tmp; + unsigned tmp = program_numbers[i]; + program_numbers[i] = program_numbers[j]; + program_numbers[j] = tmp; } } } @@ -401,14 +397,14 @@ void print_file_report_json(struct lib_ccx_ctx *ctx) json_escape(stream_mode_to_string(demux->stream_mode)); printf(",\n"); - printf(" \"program_count\": %d,\n", real_pg_count); + printf(" \"program_count\": %d,\n", program_count); printf(" \"program_numbers\": ["); - for (int i = 0; i < real_pg_count; i++) + for (int i = 0; i < program_count; i++) { if (i) printf(", "); - printf("%u", pg_array[i]->program_number); + printf("%u", program_numbers[i]); } printf("],\n"); @@ -447,24 +443,49 @@ void print_file_report_json(struct lib_ccx_ctx *ctx) printf(" \"programs\": [\n"); first = 1; - for (int pi = 0; pi < real_pg_count; pi++) + for (int pi = 0; pi < program_count; pi++) { - program = pg_array[pi]; - struct lib_cc_decode *dec_ctx; - struct cap_info *info; + unsigned pn = program_numbers[pi]; + + // Find matching cap_info for this program number + struct cap_info *program_ci = NULL; + struct cap_info *iter; + list_for_each_entry(iter, &demux->cinfo_tree.pg_stream, pg_stream, struct cap_info) + { + if (iter->program_number == (int)pn) + { + program_ci = iter; + break; + } + } + + // Compute caption metadata ONLY if cap_info exists + struct lib_cc_decode *dec_ctx = NULL; + bool has_608 = false; + bool has_708 = false; + bool has_dvb = false; + bool has_tt = false; + bool has_any_captions = false; + + if (program_ci) + { + dec_ctx = update_decoder_list_cinfo(ctx, program_ci); + + has_608 = (dec_ctx->cc_stats[0] || dec_ctx->cc_stats[1]); + has_708 = (dec_ctx->cc_stats[2] || dec_ctx->cc_stats[3]); + + has_dvb = (get_sib_stream_by_type(program_ci, CCX_CODEC_DVB) != NULL); + has_tt = (get_sib_stream_by_type(program_ci, CCX_CODEC_TELETEXT) != NULL); + + has_any_captions = has_608 || has_708 || has_dvb || has_tt; + } if (!first) printf(",\n"); first = 0; - dec_ctx = update_decoder_list_cinfo(ctx, program); - - bool has_608 = (dec_ctx->cc_stats[0] || dec_ctx->cc_stats[1]); - bool has_708 = (dec_ctx->cc_stats[2] || dec_ctx->cc_stats[3]); - bool has_any_captions = has_608 || has_708; - printf(" {\n"); - printf(" \"program_number\": %u,\n", program->program_number); + printf(" \"program_number\": %u,\n", pn); printf(" \"summary\": {\n"); printf(" \"has_any_captions\": %s,\n", has_any_captions ? "true" : "false"); printf(" \"has_608\": %s,\n", has_608 ? "true" : "false"); @@ -473,11 +494,11 @@ void print_file_report_json(struct lib_ccx_ctx *ctx) printf(" \"services\": {\n"); printf(" \"dvb_subtitles\": %s,\n", - get_sib_stream_by_type(program, CCX_CODEC_DVB) ? "true" : "false"); + (program_ci && get_sib_stream_by_type(program_ci, CCX_CODEC_DVB)) ? "true" : "false"); printf(" \"teletext\": %s,\n", - get_sib_stream_by_type(program, CCX_CODEC_TELETEXT) ? "true" : "false"); + (program_ci && get_sib_stream_by_type(program_ci, CCX_CODEC_TELETEXT)) ? "true" : "false"); printf(" \"atsc_closed_caption\": %s\n", - get_sib_stream_by_type(program, CCX_CODEC_ATSC_CC) ? "true" : "false"); + (program_ci && get_sib_stream_by_type(program_ci, CCX_CODEC_ATSC_CC)) ? "true" : "false"); printf(" },\n"); printf(" \"captions\": {\n"); @@ -489,12 +510,16 @@ void print_file_report_json(struct lib_ccx_ctx *ctx) // NOTE: EIA-608 / CEA-708 data is currently stream-global and therefore // identical across programs in multi-program streams. printf(" \"xds\": %s,\n", - ctx->freport.data_from_608->xds ? "true" : "false"); + (program_ci && ctx->freport.data_from_608->xds) ? "true" : "false"); printf(" \"channels\": {\n"); - printf(" \"cc1\": %s,\n", ctx->freport.data_from_608->cc_channels[0] ? "true" : "false"); - printf(" \"cc2\": %s,\n", ctx->freport.data_from_608->cc_channels[1] ? "true" : "false"); - printf(" \"cc3\": %s,\n", ctx->freport.data_from_608->cc_channels[2] ? "true" : "false"); - printf(" \"cc4\": %s\n", ctx->freport.data_from_608->cc_channels[3] ? "true" : "false"); + printf(" \"cc1\": %s,\n", + (program_ci && ctx->freport.data_from_608->cc_channels[0]) ? "true" : "false"); + printf(" \"cc2\": %s,\n", + (program_ci && ctx->freport.data_from_608->cc_channels[1]) ? "true" : "false"); + printf(" \"cc3\": %s,\n", + (program_ci && ctx->freport.data_from_608->cc_channels[2]) ? "true" : "false"); + printf(" \"cc4\": %s\n", + (program_ci && ctx->freport.data_from_608->cc_channels[3]) ? "true" : "false"); printf(" }\n"); printf(" },\n"); @@ -502,14 +527,17 @@ void print_file_report_json(struct lib_ccx_ctx *ctx) printf(" \"present\": %s,\n", has_708 ? "true" : "false"); printf(" \"services\": ["); int sf = 1; - for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++) + if (program_ci) { - if (!ctx->freport.data_from_708->services[i]) - continue; - if (!sf) - printf(", "); - sf = 0; - printf("%d", i); + for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++) + { + if (!ctx->freport.data_from_708->services[i]) + continue; + if (!sf) + printf(", "); + sf = 0; + printf("%d", i); + } } printf("]\n"); printf(" }\n"); // end cea_708 @@ -517,18 +545,22 @@ void print_file_report_json(struct lib_ccx_ctx *ctx) // Decide upfront if video will actually be printed bool print_video = false; - info = get_best_sib_stream(program); + struct cap_info *info = NULL; - if (info) + if (program_ci) { - dec_ctx = update_decoder_list_cinfo(ctx, info); - if (dec_ctx->in_bufferdatatype == CCX_PES && - (demux->stream_mode == CCX_SM_TRANSPORT || - demux->stream_mode == CCX_SM_PROGRAM || - demux->stream_mode == CCX_SM_ASF || - demux->stream_mode == CCX_SM_WTV)) + info = get_best_sib_stream(program_ci); + if (info) { - print_video = true; + dec_ctx = update_decoder_list_cinfo(ctx, info); + if (dec_ctx->in_bufferdatatype == CCX_PES && + (demux->stream_mode == CCX_SM_TRANSPORT || + demux->stream_mode == CCX_SM_PROGRAM || + demux->stream_mode == CCX_SM_ASF || + demux->stream_mode == CCX_SM_WTV)) + { + print_video = true; + } } } @@ -557,7 +589,7 @@ void print_file_report_json(struct lib_ccx_ctx *ctx) printf("\n ]\n"); printf("}\n"); - free(pg_array); + free(program_numbers); } void print_file_report(struct lib_ccx_ctx *ctx) From 7b891b99fcd0b72ae1f592c591a9f59856f4de00 Mon Sep 17 00:00:00 2001 From: Chandragupt Singh Date: Wed, 4 Feb 2026 01:29:16 +0530 Subject: [PATCH 4/4] style(rust): format code with rustfmt --- src/rust/src/args.rs | 14 +++++++------- src/rust/src/common.rs | 2 +- src/rust/src/parser.rs | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/rust/src/args.rs b/src/rust/src/args.rs index bc32dc80f..ec394919e 100644 --- a/src/rust/src/args.rs +++ b/src/rust/src/args.rs @@ -250,15 +250,15 @@ pub struct Args { pub mkv: bool, #[arg(long, hide = true)] pub dvr_ms: bool, -#[arg(long, value_name="format", help_heading=OUTPUT_FORMATS)] -pub out: Option, + #[arg(long, value_name="format", help_heading=OUTPUT_FORMATS)] + pub out: Option, -/// Format for -out=report output (e.g. json) -#[arg(long = "report-format", value_name = "FORMAT", help_heading=OUTPUT_FORMATS)] -pub report_format: Option, + /// Format for -out=report output (e.g. json) + #[arg(long = "report-format", value_name = "FORMAT", help_heading=OUTPUT_FORMATS)] + pub report_format: Option, -#[arg(long, hide = true)] -pub srt: bool, + #[arg(long, hide = true)] + pub srt: bool, #[arg(long, hide = true)] pub webvtt: bool, diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs index 7ad589846..aa5df41ff 100755 --- a/src/rust/src/common.rs +++ b/src/rust/src/common.rs @@ -84,7 +84,7 @@ pub unsafe fn copy_from_rust(ccx_s_options: *mut ccx_s_options, options: Options // Report output format (e.g. "json") if let Some(ref fmt) = options.report_format { (*ccx_s_options).report_format = - replace_rust_c_string((*ccx_s_options).report_format, fmt.as_str()); + replace_rust_c_string((*ccx_s_options).report_format, fmt.as_str()); } // Preserve the original C-managed report pointer to avoid dangling pointer issues. let saved_608_report = (*ccx_s_options).settings_608.report; diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index c18bcef11..ca8a2f6a3 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -775,7 +775,7 @@ impl OptionsExt for Options { // --- report-format (used by -out=report) --- if let Some(ref fmt) = args.report_format { - self.report_format = Some(fmt.to_lowercase()); + self.report_format = Some(fmt.to_lowercase()); } if let Some(ref startcreditstext) = args.startcreditstext {