From 48d633f10b776ed3f98b67a5ed86affc0caa26d6 Mon Sep 17 00:00:00 2001 From: Rahul-2k4 <216878448+Rahul-2k4@users.noreply.github.com> Date: Wed, 17 Dec 2025 00:18:21 +0530 Subject: [PATCH 1/9] Implement multi-stream DVB subtitle extraction (--split-dvb-subs) Fixes #447: Extract each DVB stream to a separate file --- src/lib_ccx/ccx_common_option.c | 4 + src/lib_ccx/ccx_common_option.h | 4 + src/lib_ccx/ccx_demuxer.c | 4 + src/lib_ccx/ccx_demuxer.h | 18 ++++ src/lib_ccx/dvb_subtitle_decoder.c | 65 ++++++++++++ src/lib_ccx/dvb_subtitle_decoder.h | 31 ++++++ src/lib_ccx/general_loop.c | 20 +++- src/lib_ccx/lib_ccx.c | 125 ++++++++++++++++++++++++ src/lib_ccx/lib_ccx.h | 7 ++ src/lib_ccx/ts_tables.c | 73 ++++++++++++++ src/rust/lib_ccxr/src/common/options.rs | 3 + src/rust/src/args.rs | 6 ++ src/rust/src/common.rs | 2 + src/rust/src/parser.rs | 4 + 14 files changed, 361 insertions(+), 5 deletions(-) diff --git a/src/lib_ccx/ccx_common_option.c b/src/lib_ccx/ccx_common_option.c index 21d16be7c..63b52023e 100644 --- a/src/lib_ccx/ccx_common_option.c +++ b/src/lib_ccx/ccx_common_option.c @@ -109,6 +109,10 @@ void init_options(struct ccx_s_options *options) options->noautotimeref = 0; // Do NOT set time automatically? options->input_source = CCX_DS_FILE; // Files, stdin or network options->multiprogram = 0; + + // [ADD THIS] + options->split_dvb_subs = 0; + options->out_interval = -1; options->segment_on_key_frames_only = 0; diff --git a/src/lib_ccx/ccx_common_option.h b/src/lib_ccx/ccx_common_option.h index 3822c290a..56c44cd5f 100644 --- a/src/lib_ccx/ccx_common_option.h +++ b/src/lib_ccx/ccx_common_option.h @@ -98,6 +98,10 @@ struct ccx_s_options // Options from user parameters int nohtmlescape; int notypesetting; struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process + + // [ADD THIS] + int split_dvb_subs; // If 1, extract each DVB stream to a separate file + int print_file_reports; ccx_decoder_608_settings settings_608; // Contains the settings for the 608 decoder. diff --git a/src/lib_ccx/ccx_demuxer.c b/src/lib_ccx/ccx_demuxer.c index 3469d03fa..016681b6a 100644 --- a/src/lib_ccx/ccx_demuxer.c +++ b/src/lib_ccx/ccx_demuxer.c @@ -396,6 +396,10 @@ struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg) ctx->warning_program_not_found_shown = CCX_FALSE; ctx->strangeheader = 0; + + // [ADD THIS] + ctx->potential_stream_count = 0; + memset(&ctx->freport, 0, sizeof(ctx->freport)); for (i = 0; i < MAX_PSI_PID; i++) diff --git a/src/lib_ccx/ccx_demuxer.h b/src/lib_ccx/ccx_demuxer.h index 43495b4e8..5b928b599 100644 --- a/src/lib_ccx/ccx_demuxer.h +++ b/src/lib_ccx/ccx_demuxer.h @@ -7,6 +7,20 @@ #include "activity.h" #include "utility.h" +// [ADD THESE DEFINITIONS] +#define MAX_POTENTIAL_STREAMS 64 +#define CCX_STREAM_TYPE_UNKNOWN 0 +#define CCX_STREAM_TYPE_DVB_SUB 1 +#define CCX_STREAM_TYPE_TELETEXT 2 + +struct ccx_stream_metadata +{ + int pid; + int stream_type; // Logical type (CCX_STREAM_TYPE_*) + int mpeg_type; // Raw MPEG type (0x06) + char lang[4]; // ISO 639-2 +}; + /* Report information */ #define SUB_STREAMS_CNT 10 #define MAX_PID 65536 @@ -143,6 +157,10 @@ struct ccx_demuxer unsigned int filebuffer_pos; // Position of pointer relative to buffer start unsigned int bytesinbuffer; // Number of bytes we actually have on buffer + // [ADD THESE] + struct ccx_stream_metadata potential_streams[MAX_POTENTIAL_STREAMS]; + int potential_stream_count; + int warning_program_not_found_shown; // Remember if the last header was valid. Used to suppress too much output diff --git a/src/lib_ccx/dvb_subtitle_decoder.c b/src/lib_ccx/dvb_subtitle_decoder.c index 4acfb8197..bed07f791 100644 --- a/src/lib_ccx/dvb_subtitle_decoder.c +++ b/src/lib_ccx/dvb_subtitle_decoder.c @@ -559,6 +559,71 @@ int dvbsub_close_decoder(void **dvb_ctx) return 0; } +// New context-based API for multi-stream support + +struct ccx_decoders_dvb_context *dvb_init_decoder(struct dvb_config *cfg, int initialized_ocr) +{ + struct ccx_decoders_dvb_context *dvb_ctx; + DVBSubContext *ctx; + + dvb_ctx = (struct ccx_decoders_dvb_context *)malloc(sizeof(struct ccx_decoders_dvb_context)); + if (!dvb_ctx) + { + fatal(EXIT_NOT_ENOUGH_MEMORY, "In dvb_init_decoder: Out of memory for context."); + } + memset(dvb_ctx, 0, sizeof(struct ccx_decoders_dvb_context)); + + // Initialize the internal DVB context using existing function + ctx = (DVBSubContext *)dvbsub_init_decoder(cfg, initialized_ocr); + if (!ctx) + { + free(dvb_ctx); + return NULL; + } + + dvb_ctx->private_data = ctx; + dvb_ctx->cfg = cfg; + dvb_ctx->initialized_ocr = initialized_ocr; + + return dvb_ctx; +} + +void dvb_free_decoder(struct ccx_decoders_dvb_context **dvb_ctx) +{ + if (!dvb_ctx || !*dvb_ctx) + return; + + // Free the internal DVB context using existing function + if ((*dvb_ctx)->private_data) + { + void *ctx = (*dvb_ctx)->private_data; + dvbsub_close_decoder(&ctx); + } + + free(*dvb_ctx); + *dvb_ctx = NULL; +} + +int dvb_decode(struct ccx_decoders_dvb_context *dvb_ctx, struct encoder_ctx *enc_ctx, + struct lib_cc_decode *dec_ctx, const unsigned char *buf, int buf_size, + struct cc_subtitle *sub) +{ + if (!dvb_ctx || !dvb_ctx->private_data) + return -1; + + // Temporarily set the private_data for the existing decode function + void *original_private_data = dec_ctx->private_data; + dec_ctx->private_data = dvb_ctx->private_data; + + // Call the existing decode function + int result = dvbsub_decode(enc_ctx, dec_ctx, buf, buf_size, sub); + + // Restore original private_data + dec_ctx->private_data = original_private_data; + + return result; +} + static int dvbsub_read_2bit_string(uint8_t *destbuf, int dbuf_len, const uint8_t **srcbuf, int buf_size, int non_mod, uint8_t *map_table, int x_pos) diff --git a/src/lib_ccx/dvb_subtitle_decoder.h b/src/lib_ccx/dvb_subtitle_decoder.h index 879a8a62c..963678137 100644 --- a/src/lib_ccx/dvb_subtitle_decoder.h +++ b/src/lib_ccx/dvb_subtitle_decoder.h @@ -19,11 +19,34 @@ #define MAX_LANGUAGE_PER_DESC 5 #include "lib_ccx.h" +#include "ccx_common_structs.h" +#include "ccx_decoders_structs.h" + #ifdef __cplusplus extern "C" { #endif +// [ADD: Context structure] +typedef struct ccx_decoders_dvb_context +{ + int page_time; + int prev_page_time; + int global_x; + int global_y; + + // Internal lists (opaque pointers here, defined in .c) + void *page_composition; + void *region_composition_list; + void *clut_definition_list; + void *object_data_list; + void *display_definition; + + // Dependencies + struct ccx_common_timing_ctx *timing; + struct encoder_ctx *encoder; +} ccx_decoders_dvb_context; + struct dvb_config { unsigned char n_language; @@ -36,6 +59,14 @@ extern "C" unsigned short ancillary_id[MAX_LANGUAGE_PER_DESC]; }; + // [ADD: Lifecycle functions] + ccx_decoders_dvb_context *dvb_init_decoder(struct ccx_common_timing_ctx *timing, struct encoder_ctx *encoder); + void dvb_free_decoder(ccx_decoders_dvb_context *dvb_ctx); + + // [MODIFY: Decode signature] + void dvb_decode(ccx_decoders_dvb_context *dvb_ctx, unsigned char *data, int length, struct cc_subtitle *sub); + + // Legacy API (kept for compatibility) /** * @param cfg Structure containg configuration * diff --git a/src/lib_ccx/general_loop.c b/src/lib_ccx/general_loop.c index 8bf312da5..4cbc6886c 100644 --- a/src/lib_ccx/general_loop.c +++ b/src/lib_ccx/general_loop.c @@ -629,7 +629,7 @@ void delete_datalist(struct demuxer_data *list) delete_demuxer_data(slist); } } -int process_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, struct demuxer_data *data_node) +int process_data(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, struct demuxer_data *data_node) { size_t got; // Means 'consumed' from buffer actually int ret = 0; @@ -644,9 +644,19 @@ int process_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, str } else if (data_node->bufferdatatype == CCX_DVB_SUBTITLE) { - ret = dvbsub_decode(enc_ctx, dec_ctx, data_node->buffer + 2, data_node->len - 2, dec_sub); + // Check for multi-stream DVB processing + if (ctx->options.split_dvb_subs) + { + ret = process_dvb_multi_stream(ctx, data_node, dec_sub); + } + else + { + // Original single-stream processing + ret = dvbsub_decode(enc_ctx, dec_ctx, data_node->buffer + 2, data_node->len - 2, dec_sub); + } + if (ret < 0) - mprint("Return from dvbsub_decode: %d\n", ret); + mprint("Return from DVB decode: %d\n", ret); set_fts(dec_ctx->timing); got = data_node->len; } @@ -1004,7 +1014,7 @@ int process_non_multiprogram_general_loop(struct lib_ccx_ctx *ctx, } if ((*data_node)->bufferdatatype == CCX_TELETEXT && (*dec_ctx)->private_data) // if we have teletext subs, we set the min_pts here set_tlt_delta(*dec_ctx, (*dec_ctx)->timing->current_pts); - ret = process_data(*enc_ctx, *dec_ctx, *data_node); + ret = process_data(ctx, *enc_ctx, *dec_ctx, *data_node); if (*enc_ctx != NULL) { if ((*enc_ctx)->srt_counter || (*enc_ctx)->cea_708_counter || (*dec_ctx)->saw_caption_block || ret == 1) @@ -1192,7 +1202,7 @@ int general_loop(struct lib_ccx_ctx *ctx) } } - ret = process_data(enc_ctx, dec_ctx, data_node); + ret = process_data(ctx, enc_ctx, dec_ctx, data_node); if (enc_ctx != NULL) { if ( diff --git a/src/lib_ccx/lib_ccx.c b/src/lib_ccx/lib_ccx.c index 420cbe9c1..3283733b6 100644 --- a/src/lib_ccx/lib_ccx.c +++ b/src/lib_ccx/lib_ccx.c @@ -192,6 +192,13 @@ struct lib_ccx_ctx *init_libraries(struct ccx_s_options *opt) INIT_LIST_HEAD(&ctx->dec_ctx_head); INIT_LIST_HEAD(&ctx->enc_ctx_head); + // Initialize DVB multi-stream pipeline if enabled + ret = init_dvb_multi_stream_pipeline(ctx); + if (ret < 0) + { + goto end; + } + // Init timing ccx_common_timing_init(&ctx->demux_ctx->past, opt->nosync); ctx->multiprogram = opt->multiprogram; @@ -274,6 +281,10 @@ void dinit_libraries(struct lib_ccx_ctx **ctx) for (i = 0; i < lctx->num_input_files; i++) freep(&lctx->inputfile[i]); freep(&lctx->inputfile); + + // Cleanup DVB multi-stream pipeline + cleanup_dvb_multi_stream_pipeline(lctx); + freep(ctx); } @@ -479,3 +490,117 @@ struct encoder_ctx *update_encoder_list(struct lib_ccx_ctx *ctx) { return update_encoder_list_cinfo(ctx, NULL); } + +// Multi-stream DVB subtitle processing pipeline + +int init_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) +{ + if (!ctx->options.split_dvb_subs) + return 0; // Multi-stream mode not enabled + + mprint("Initializing DVB multi-stream pipeline\n"); + + // Initialize the registry arrays if needed + // The actual stream contexts will be created on-demand when streams are discovered + + return 0; +} + +void cleanup_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) +{ + if (!ctx->options.split_dvb_subs) + return; + + mprint("Cleaning up DVB multi-stream pipeline\n"); + + // Clean up any registered DVB decoder contexts + for (int i = 0; i < ctx->demux_ctx->potential_stream_count; i++) + { + struct ccx_stream_metadata *stream = &ctx->demux_ctx->potential_streams[i]; + + if (stream->stream_type == CCX_STREAM_TYPE_DVB_SUB && stream->dvb_decoder_ctx) + { + mprint("Cleaning up DVB decoder for stream PID 0x%x\n", stream->pid); + dvb_free_decoder(&stream->dvb_decoder_ctx); + } + } +} + +int process_dvb_multi_stream(struct lib_ccx_ctx *ctx, struct demuxer_data *data, struct cc_subtitle *sub) +{ + if (!ctx->options.split_dvb_subs) + return 0; // Multi-stream mode not enabled + + // Find the stream metadata for this PID + struct ccx_stream_metadata *target_stream = NULL; + + for (int i = 0; i < ctx->demux_ctx->potential_stream_count; i++) + { + struct ccx_stream_metadata *stream = &ctx->demux_ctx->potential_streams[i]; + + if (stream->pid == data->pid && stream->stream_type == CCX_STREAM_TYPE_DVB_SUB) + { + target_stream = stream; + break; + } + } + + if (!target_stream) + { + // Stream not found in registry - this shouldn't happen if PMT parsing worked correctly + mprint("Warning: DVB stream PID 0x%x not found in stream registry\n", data->pid); + return -1; + } + + return route_dvb_stream_to_decoder(ctx, target_stream, data->buffer, data->len, sub); +} + +int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metadata *stream, + const unsigned char *buf, int buf_size, struct cc_subtitle *sub) +{ + // Create decoder context on-demand if not already created + if (!stream->dvb_decoder_ctx) + { + mprint("Creating DVB decoder for stream PID 0x%x (lang: %s)\n", + stream->pid, stream->language[0] ? stream->language : "unknown"); + + // Create DVB config for this specific stream + struct dvb_config cfg; + memset(&cfg, 0, sizeof(cfg)); + cfg.composition_id[0] = 1; // Default composition ID + cfg.ancillary_id[0] = 1; // Default ancillary ID + cfg.lang_index[0] = 1; // Default language index + + stream->dvb_decoder_ctx = dvb_init_decoder(&cfg, 0); + if (!stream->dvb_decoder_ctx) + { + mprint("Failed to create DVB decoder for stream PID 0x%x\n", stream->pid); + return -1; + } + } + + // Get decoder and encoder contexts + struct lib_cc_decode *dec_ctx = update_decoder_list(ctx); + if (!dec_ctx) + { + mprint("Failed to get decoder context for DVB stream PID 0x%x\n", stream->pid); + return -1; + } + + // Create encoder context for this specific stream if needed + struct cap_info cinfo; + memset(&cinfo, 0, sizeof(cinfo)); + cinfo.pid = stream->pid; + cinfo.codec = CCX_CODEC_DVB; + strcpy(cinfo.language, stream->language); + + struct encoder_ctx *enc_ctx = update_encoder_list_cinfo(ctx, &cinfo); + if (!enc_ctx) + { + mprint("Failed to get encoder context for DVB stream PID 0x%x\n", stream->pid); + return -1; + } + + // Decode the DVB subtitle data using the stream-specific decoder + return dvb_decode(stream->dvb_decoder_ctx, enc_ctx, dec_ctx, buf, buf_size, sub); +} diff --git a/src/lib_ccx/lib_ccx.h b/src/lib_ccx/lib_ccx.h index f0a99c6b3..ccb4c3938 100644 --- a/src/lib_ccx/lib_ccx.h +++ b/src/lib_ccx/lib_ccx.h @@ -326,4 +326,11 @@ int process_non_multiprogram_general_loop(struct lib_ccx_ctx *ctx, void segment_output_file(struct lib_ccx_ctx *ctx, struct lib_cc_decode *dec_ctx); int decode_vbi(struct lib_cc_decode *dec_ctx, uint8_t field, unsigned char *buffer, size_t len, struct cc_subtitle *sub); +// Multi-stream DVB subtitle processing pipeline +int init_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx); +void cleanup_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx); +int process_dvb_multi_stream(struct lib_ccx_ctx *ctx, struct demuxer_data *data, struct cc_subtitle *sub); +int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metadata *stream, + const unsigned char *buf, int buf_size, struct cc_subtitle *sub); + #endif diff --git a/src/lib_ccx/ts_tables.c b/src/lib_ccx/ts_tables.c index 06b4e3e9a..5db9300ff 100644 --- a/src/lib_ccx/ts_tables.c +++ b/src/lib_ccx/ts_tables.c @@ -350,6 +350,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr if (CCX_MPEG_DSC_DVB_SUBTITLE == descriptor_tag) { struct dvb_config cnf; + int is_dvb_subtitle = 1; #ifndef ENABLE_OCR if (ccx_options.write_format != CCX_OF_SPUPNG) { @@ -371,6 +372,44 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr break; update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_DVB, program_number, ptr); max_dif = 30; + + // [ADD THIS BLOCK: Stream Discovery] + if (ccx_options.split_dvb_subs) + { + if (ctx->potential_stream_count >= MAX_POTENTIAL_STREAMS) + { + mprint("Warning: Max subtitle streams reached in PMT parsing.\n"); + } + else + { + int exists = 0; + for (int k = 0; k < ctx->potential_stream_count; k++) + { + // Check PID + Logical Type for uniqueness + if (ctx->potential_streams[k].pid == elementary_PID && + ctx->potential_streams[k].stream_type == CCX_STREAM_TYPE_DVB_SUB) + { + exists = 1; + break; + } + } + + if (!exists) + { + struct ccx_stream_metadata *meta = &ctx->potential_streams[ctx->potential_stream_count++]; + meta->pid = elementary_PID; + meta->stream_type = CCX_STREAM_TYPE_DVB_SUB; + meta->mpeg_type = stream_type; + + // Extract language from DVB config + if (cnf.n_language > 0) + snprintf(meta->lang, 4, "%.3s", (char*)&cnf.lang_index[0]); + else + strcpy(meta->lang, "und"); + meta->lang[3] = '\0'; + } + } + } } } } @@ -411,9 +450,43 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr desc_len = (*es_info++); if (!IS_VALID_TELETEXT_DESC(descriptor_tag)) continue; + int is_teletext = 1; update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_TELETEXT, program_number, NULL); mprint("VBI/teletext stream ID %u (0x%x) for SID %u (0x%x)\n", elementary_PID, elementary_PID, program_number, program_number); + + // [ADD THIS BLOCK: Teletext Stream Discovery] + if (ccx_options.split_dvb_subs && is_teletext) + { + if (ctx->potential_stream_count >= MAX_POTENTIAL_STREAMS) + { + mprint("Warning: Max subtitle streams reached in PMT parsing.\n"); + } + else + { + int exists = 0; + for (int k = 0; k < ctx->potential_stream_count; k++) + { + // Check PID + Logical Type for uniqueness + if (ctx->potential_streams[k].pid == elementary_PID && + ctx->potential_streams[k].stream_type == CCX_STREAM_TYPE_TELETEXT) + { + exists = 1; + break; + } + } + + if (!exists) + { + struct ccx_stream_metadata *meta = &ctx->potential_streams[ctx->potential_stream_count++]; + meta->pid = elementary_PID; + meta->stream_type = CCX_STREAM_TYPE_TELETEXT; + meta->mpeg_type = stream_type; + strcpy(meta->lang, "und"); // Teletext language extraction would need more work + meta->lang[3] = '\0'; + } + } + } } } diff --git a/src/rust/lib_ccxr/src/common/options.rs b/src/rust/lib_ccxr/src/common/options.rs index 55d379565..0de19aacf 100644 --- a/src/rust/lib_ccxr/src/common/options.rs +++ b/src/rust/lib_ccxr/src/common/options.rs @@ -512,6 +512,8 @@ pub struct Options { /// If true, the program will ignore PTS jumps. /// Sometimes this parameter is required for DVB subs with > 30s pause time pub ignore_pts_jumps: bool, + /// If true, extract each DVB subtitle stream to a separate file + pub split_dvb_subs: bool, pub multiprogram: bool, pub out_interval: i32, pub segment_on_key_frames_only: bool, @@ -612,6 +614,7 @@ impl Default for Options { cc_to_stdout: Default::default(), pes_header_to_stdout: Default::default(), ignore_pts_jumps: Default::default(), + split_dvb_subs: Default::default(), multiprogram: Default::default(), out_interval: -1, segment_on_key_frames_only: Default::default(), diff --git a/src/rust/src/args.rs b/src/rust/src/args.rs index 1f10ba5bd..63ddb35e6 100644 --- a/src/rust/src/args.rs +++ b/src/rust/src/args.rs @@ -155,6 +155,12 @@ pub struct Args { /// Write the DVB subtitle debug traces to console. #[arg(long, help_heading=FILE_NAME_RELATED_OPTIONS)] pub debugdvbsub: bool, + /// Extract each DVB subtitle stream to a separate file. + /// When multiple DVB subtitle streams are detected, this option + /// creates separate output files for each stream instead of + /// merging them into a single output file. + #[arg(long, verbatim_doc_comment, help_heading=FILE_NAME_RELATED_OPTIONS)] + pub split_dvb_subs: bool, /// Ignore PTS jumps (default). #[arg(long, help_heading=FILE_NAME_RELATED_OPTIONS)] pub ignoreptsjumps: bool, diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs index 3f55ca033..dba87ecea 100755 --- a/src/rust/src/common.rs +++ b/src/rust/src/common.rs @@ -226,6 +226,7 @@ pub unsafe fn copy_from_rust(ccx_s_options: *mut ccx_s_options, options: Options (*ccx_s_options).cc_to_stdout = options.cc_to_stdout as _; (*ccx_s_options).pes_header_to_stdout = options.pes_header_to_stdout as _; (*ccx_s_options).ignore_pts_jumps = options.ignore_pts_jumps as _; + (*ccx_s_options).split_dvb_subs = options.split_dvb_subs as _; (*ccx_s_options).multiprogram = options.multiprogram as _; (*ccx_s_options).out_interval = options.out_interval; (*ccx_s_options).segment_on_key_frames_only = options.segment_on_key_frames_only as _; @@ -480,6 +481,7 @@ pub unsafe fn copy_to_rust(ccx_s_options: *const ccx_s_options) -> Options { options.cc_to_stdout = (*ccx_s_options).cc_to_stdout != 0; options.pes_header_to_stdout = (*ccx_s_options).pes_header_to_stdout != 0; options.ignore_pts_jumps = (*ccx_s_options).ignore_pts_jumps != 0; + options.split_dvb_subs = (*ccx_s_options).split_dvb_subs != 0; options.multiprogram = (*ccx_s_options).multiprogram != 0; options.out_interval = (*ccx_s_options).out_interval; options.segment_on_key_frames_only = (*ccx_s_options).segment_on_key_frames_only != 0; diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index 05eee0fca..9949ce752 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -1008,6 +1008,10 @@ impl OptionsExt for Options { DebugMessageMask::new(DebugMessageFlag::DVB, DebugMessageFlag::VERBOSE); } + if args.split_dvb_subs { + self.split_dvb_subs = true; + } + if args.ignoreptsjumps { self.ignore_pts_jumps = true; } From 20421f74329f33b21581e29fc78f253126784061 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 18:57:53 +0000 Subject: [PATCH 2/9] Initial plan From 6cd26a6c47903e076821f9e1b19883aaa0b3f508 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 16 Dec 2025 19:12:03 +0000 Subject: [PATCH 3/9] Fix compiler errors in DVB multi-stream structures - Add missing fields to ccx_decoders_dvb_context: private_data, cfg, initialized_ocr - Add dvb_decoder_ctx field to ccx_stream_metadata - Add language field to cap_info structure - Add split_dvb_subs field to lib_ccx_ctx - Initialize split_dvb_subs from options in init_libraries - Fix all references to use correct struct field names (lang vs language, stream_pid vs pid) - Update Rust bindings to include new language field in cap_info - Match dvb_init_decoder, dvb_free_decoder, and dvb_decode signatures between header and implementation Co-authored-by: Rahul-2k4 <216878448+Rahul-2k4@users.noreply.github.com> --- src/lib_ccx/ccx_demuxer.h | 2 ++ src/lib_ccx/dvb_subtitle_decoder.h | 13 ++++++++++--- src/lib_ccx/general_loop.c | 2 +- src/lib_ccx/lib_ccx.c | 15 ++++++++------- src/lib_ccx/lib_ccx.h | 3 +++ src/rust/src/common.rs | 1 + 6 files changed, 25 insertions(+), 11 deletions(-) diff --git a/src/lib_ccx/ccx_demuxer.h b/src/lib_ccx/ccx_demuxer.h index 5b928b599..4e96f71e6 100644 --- a/src/lib_ccx/ccx_demuxer.h +++ b/src/lib_ccx/ccx_demuxer.h @@ -19,6 +19,7 @@ struct ccx_stream_metadata int stream_type; // Logical type (CCX_STREAM_TYPE_*) int mpeg_type; // Raw MPEG type (0x06) char lang[4]; // ISO 639-2 + struct ccx_decoders_dvb_context *dvb_decoder_ctx; // DVB decoder context for this stream }; /* Report information */ @@ -77,6 +78,7 @@ struct cap_info int prev_counter; void *codec_private_data; int ignore; + char language[4]; // ISO 639-2 language code /** List joining all stream in TS diff --git a/src/lib_ccx/dvb_subtitle_decoder.h b/src/lib_ccx/dvb_subtitle_decoder.h index 963678137..f891f3d4d 100644 --- a/src/lib_ccx/dvb_subtitle_decoder.h +++ b/src/lib_ccx/dvb_subtitle_decoder.h @@ -45,6 +45,11 @@ typedef struct ccx_decoders_dvb_context // Dependencies struct ccx_common_timing_ctx *timing; struct encoder_ctx *encoder; + + // Internal data for multi-stream support + void *private_data; // Internal DVBSubContext + struct dvb_config *cfg; // Configuration + int initialized_ocr; // OCR initialization flag } ccx_decoders_dvb_context; struct dvb_config @@ -60,11 +65,13 @@ typedef struct ccx_decoders_dvb_context }; // [ADD: Lifecycle functions] - ccx_decoders_dvb_context *dvb_init_decoder(struct ccx_common_timing_ctx *timing, struct encoder_ctx *encoder); - void dvb_free_decoder(ccx_decoders_dvb_context *dvb_ctx); + ccx_decoders_dvb_context *dvb_init_decoder(struct dvb_config *cfg, int initialized_ocr); + void dvb_free_decoder(ccx_decoders_dvb_context **dvb_ctx); // [MODIFY: Decode signature] - void dvb_decode(ccx_decoders_dvb_context *dvb_ctx, unsigned char *data, int length, struct cc_subtitle *sub); + int dvb_decode(ccx_decoders_dvb_context *dvb_ctx, struct encoder_ctx *enc_ctx, + struct lib_cc_decode *dec_ctx, const unsigned char *buf, int buf_size, + struct cc_subtitle *sub); // Legacy API (kept for compatibility) /** diff --git a/src/lib_ccx/general_loop.c b/src/lib_ccx/general_loop.c index 4cbc6886c..a052df971 100644 --- a/src/lib_ccx/general_loop.c +++ b/src/lib_ccx/general_loop.c @@ -645,7 +645,7 @@ int process_data(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, struct li else if (data_node->bufferdatatype == CCX_DVB_SUBTITLE) { // Check for multi-stream DVB processing - if (ctx->options.split_dvb_subs) + if (ctx->split_dvb_subs) { ret = process_dvb_multi_stream(ctx, data_node, dec_sub); } diff --git a/src/lib_ccx/lib_ccx.c b/src/lib_ccx/lib_ccx.c index 3283733b6..ac1b05062 100644 --- a/src/lib_ccx/lib_ccx.c +++ b/src/lib_ccx/lib_ccx.c @@ -186,6 +186,7 @@ struct lib_ccx_ctx *init_libraries(struct ccx_s_options *opt) ctx->hauppauge_mode = opt->hauppauge_mode; ctx->live_stream = opt->live_stream; ctx->binary_concat = opt->binary_concat; + ctx->split_dvb_subs = opt->split_dvb_subs; // Copy DVB multi-stream flag build_parity_table(); ctx->demux_ctx = init_demuxer(ctx, &opt->demux_cfg); @@ -495,7 +496,7 @@ struct encoder_ctx *update_encoder_list(struct lib_ccx_ctx *ctx) int init_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) { - if (!ctx->options.split_dvb_subs) + if (!ctx->split_dvb_subs) return 0; // Multi-stream mode not enabled mprint("Initializing DVB multi-stream pipeline\n"); @@ -508,7 +509,7 @@ int init_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) void cleanup_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) { - if (!ctx->options.split_dvb_subs) + if (!ctx->split_dvb_subs) return; mprint("Cleaning up DVB multi-stream pipeline\n"); @@ -528,7 +529,7 @@ void cleanup_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) int process_dvb_multi_stream(struct lib_ccx_ctx *ctx, struct demuxer_data *data, struct cc_subtitle *sub) { - if (!ctx->options.split_dvb_subs) + if (!ctx->split_dvb_subs) return 0; // Multi-stream mode not enabled // Find the stream metadata for this PID @@ -538,7 +539,7 @@ int process_dvb_multi_stream(struct lib_ccx_ctx *ctx, struct demuxer_data *data, { struct ccx_stream_metadata *stream = &ctx->demux_ctx->potential_streams[i]; - if (stream->pid == data->pid && stream->stream_type == CCX_STREAM_TYPE_DVB_SUB) + if (stream->pid == data->stream_pid && stream->stream_type == CCX_STREAM_TYPE_DVB_SUB) { target_stream = stream; break; @@ -548,7 +549,7 @@ int process_dvb_multi_stream(struct lib_ccx_ctx *ctx, struct demuxer_data *data, if (!target_stream) { // Stream not found in registry - this shouldn't happen if PMT parsing worked correctly - mprint("Warning: DVB stream PID 0x%x not found in stream registry\n", data->pid); + mprint("Warning: DVB stream PID 0x%x not found in stream registry\n", data->stream_pid); return -1; } @@ -562,7 +563,7 @@ int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metad if (!stream->dvb_decoder_ctx) { mprint("Creating DVB decoder for stream PID 0x%x (lang: %s)\n", - stream->pid, stream->language[0] ? stream->language : "unknown"); + stream->pid, stream->lang[0] ? stream->lang : "unknown"); // Create DVB config for this specific stream struct dvb_config cfg; @@ -592,7 +593,7 @@ int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metad memset(&cinfo, 0, sizeof(cinfo)); cinfo.pid = stream->pid; cinfo.codec = CCX_CODEC_DVB; - strcpy(cinfo.language, stream->language); + strcpy(cinfo.language, stream->lang); struct encoder_ctx *enc_ctx = update_encoder_list_cinfo(ctx, &cinfo); if (!enc_ctx) diff --git a/src/lib_ccx/lib_ccx.h b/src/lib_ccx/lib_ccx.h index ccb4c3938..d0cc64a16 100644 --- a/src/lib_ccx/lib_ccx.h +++ b/src/lib_ccx/lib_ccx.h @@ -148,6 +148,9 @@ struct lib_ccx_ctx int segment_on_key_frames_only; int segment_counter; LLONG system_start_time; + + // DVB multi-stream support + int split_dvb_subs; // If 1, extract each DVB stream to a separate file }; struct lib_ccx_ctx *init_libraries(struct ccx_s_options *opt); diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs index dba87ecea..2c7afc2f3 100755 --- a/src/rust/src/common.rs +++ b/src/rust/src/common.rs @@ -939,6 +939,7 @@ impl CType for CapInfo { prev_counter: self.prev_counter, codec_private_data: self.codec_private_data, ignore: self.ignore, + language: [0; 4], // Initialize language field with zeros all_stream: self.all_stream, sib_head: self.sib_head, sib_stream: self.sib_stream, From 2b4faa7a26d0eb94dd858a3f62906fe349b5522a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:11:40 +0000 Subject: [PATCH 4/9] Initial plan From e1b027162906e368cc540a3038c73803fd1fd5c5 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 17 Dec 2025 03:14:54 +0000 Subject: [PATCH 5/9] Apply clang-format and rustfmt formatting fixes Co-authored-by: Rahul-2k4 <216878448+Rahul-2k4@users.noreply.github.com> --- src/lib_ccx/ccx_common_option.c | 4 +-- src/lib_ccx/ccx_demuxer.c | 4 +-- src/lib_ccx/dvb_subtitle_decoder.c | 26 +++++++------- src/lib_ccx/general_loop.c | 2 +- src/lib_ccx/lib_ccx.c | 56 +++++++++++++++--------------- src/lib_ccx/ts_tables.c | 12 +++---- src/rust/src/common.rs | 2 +- 7 files changed, 53 insertions(+), 53 deletions(-) diff --git a/src/lib_ccx/ccx_common_option.c b/src/lib_ccx/ccx_common_option.c index 63b52023e..cdd50a73b 100644 --- a/src/lib_ccx/ccx_common_option.c +++ b/src/lib_ccx/ccx_common_option.c @@ -109,10 +109,10 @@ void init_options(struct ccx_s_options *options) options->noautotimeref = 0; // Do NOT set time automatically? options->input_source = CCX_DS_FILE; // Files, stdin or network options->multiprogram = 0; - + // [ADD THIS] options->split_dvb_subs = 0; - + options->out_interval = -1; options->segment_on_key_frames_only = 0; diff --git a/src/lib_ccx/ccx_demuxer.c b/src/lib_ccx/ccx_demuxer.c index 016681b6a..887761d22 100644 --- a/src/lib_ccx/ccx_demuxer.c +++ b/src/lib_ccx/ccx_demuxer.c @@ -396,10 +396,10 @@ struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg) ctx->warning_program_not_found_shown = CCX_FALSE; ctx->strangeheader = 0; - + // [ADD THIS] ctx->potential_stream_count = 0; - + memset(&ctx->freport, 0, sizeof(ctx->freport)); for (i = 0; i < MAX_PSI_PID; i++) diff --git a/src/lib_ccx/dvb_subtitle_decoder.c b/src/lib_ccx/dvb_subtitle_decoder.c index bed07f791..27f543e90 100644 --- a/src/lib_ccx/dvb_subtitle_decoder.c +++ b/src/lib_ccx/dvb_subtitle_decoder.c @@ -565,14 +565,14 @@ struct ccx_decoders_dvb_context *dvb_init_decoder(struct dvb_config *cfg, int in { struct ccx_decoders_dvb_context *dvb_ctx; DVBSubContext *ctx; - + dvb_ctx = (struct ccx_decoders_dvb_context *)malloc(sizeof(struct ccx_decoders_dvb_context)); if (!dvb_ctx) { fatal(EXIT_NOT_ENOUGH_MEMORY, "In dvb_init_decoder: Out of memory for context."); } memset(dvb_ctx, 0, sizeof(struct ccx_decoders_dvb_context)); - + // Initialize the internal DVB context using existing function ctx = (DVBSubContext *)dvbsub_init_decoder(cfg, initialized_ocr); if (!ctx) @@ -580,11 +580,11 @@ struct ccx_decoders_dvb_context *dvb_init_decoder(struct dvb_config *cfg, int in free(dvb_ctx); return NULL; } - + dvb_ctx->private_data = ctx; dvb_ctx->cfg = cfg; dvb_ctx->initialized_ocr = initialized_ocr; - + return dvb_ctx; } @@ -592,35 +592,35 @@ void dvb_free_decoder(struct ccx_decoders_dvb_context **dvb_ctx) { if (!dvb_ctx || !*dvb_ctx) return; - + // Free the internal DVB context using existing function if ((*dvb_ctx)->private_data) { void *ctx = (*dvb_ctx)->private_data; dvbsub_close_decoder(&ctx); } - + free(*dvb_ctx); *dvb_ctx = NULL; } -int dvb_decode(struct ccx_decoders_dvb_context *dvb_ctx, struct encoder_ctx *enc_ctx, - struct lib_cc_decode *dec_ctx, const unsigned char *buf, int buf_size, - struct cc_subtitle *sub) +int dvb_decode(struct ccx_decoders_dvb_context *dvb_ctx, struct encoder_ctx *enc_ctx, + struct lib_cc_decode *dec_ctx, const unsigned char *buf, int buf_size, + struct cc_subtitle *sub) { if (!dvb_ctx || !dvb_ctx->private_data) return -1; - + // Temporarily set the private_data for the existing decode function void *original_private_data = dec_ctx->private_data; dec_ctx->private_data = dvb_ctx->private_data; - + // Call the existing decode function int result = dvbsub_decode(enc_ctx, dec_ctx, buf, buf_size, sub); - + // Restore original private_data dec_ctx->private_data = original_private_data; - + return result; } diff --git a/src/lib_ccx/general_loop.c b/src/lib_ccx/general_loop.c index a052df971..6edd744ca 100644 --- a/src/lib_ccx/general_loop.c +++ b/src/lib_ccx/general_loop.c @@ -654,7 +654,7 @@ int process_data(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, struct li // Original single-stream processing ret = dvbsub_decode(enc_ctx, dec_ctx, data_node->buffer + 2, data_node->len - 2, dec_sub); } - + if (ret < 0) mprint("Return from DVB decode: %d\n", ret); set_fts(dec_ctx->timing); diff --git a/src/lib_ccx/lib_ccx.c b/src/lib_ccx/lib_ccx.c index ac1b05062..edf83cad8 100644 --- a/src/lib_ccx/lib_ccx.c +++ b/src/lib_ccx/lib_ccx.c @@ -186,7 +186,7 @@ struct lib_ccx_ctx *init_libraries(struct ccx_s_options *opt) ctx->hauppauge_mode = opt->hauppauge_mode; ctx->live_stream = opt->live_stream; ctx->binary_concat = opt->binary_concat; - ctx->split_dvb_subs = opt->split_dvb_subs; // Copy DVB multi-stream flag + ctx->split_dvb_subs = opt->split_dvb_subs; // Copy DVB multi-stream flag build_parity_table(); ctx->demux_ctx = init_demuxer(ctx, &opt->demux_cfg); @@ -282,10 +282,10 @@ void dinit_libraries(struct lib_ccx_ctx **ctx) for (i = 0; i < lctx->num_input_files; i++) freep(&lctx->inputfile[i]); freep(&lctx->inputfile); - + // Cleanup DVB multi-stream pipeline cleanup_dvb_multi_stream_pipeline(lctx); - + freep(ctx); } @@ -497,13 +497,13 @@ struct encoder_ctx *update_encoder_list(struct lib_ccx_ctx *ctx) int init_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) { if (!ctx->split_dvb_subs) - return 0; // Multi-stream mode not enabled - + return 0; // Multi-stream mode not enabled + mprint("Initializing DVB multi-stream pipeline\n"); - + // Initialize the registry arrays if needed // The actual stream contexts will be created on-demand when streams are discovered - + return 0; } @@ -511,14 +511,14 @@ void cleanup_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) { if (!ctx->split_dvb_subs) return; - + mprint("Cleaning up DVB multi-stream pipeline\n"); - + // Clean up any registered DVB decoder contexts for (int i = 0; i < ctx->demux_ctx->potential_stream_count; i++) { struct ccx_stream_metadata *stream = &ctx->demux_ctx->potential_streams[i]; - + if (stream->stream_type == CCX_STREAM_TYPE_DVB_SUB && stream->dvb_decoder_ctx) { mprint("Cleaning up DVB decoder for stream PID 0x%x\n", stream->pid); @@ -530,48 +530,48 @@ void cleanup_dvb_multi_stream_pipeline(struct lib_ccx_ctx *ctx) int process_dvb_multi_stream(struct lib_ccx_ctx *ctx, struct demuxer_data *data, struct cc_subtitle *sub) { if (!ctx->split_dvb_subs) - return 0; // Multi-stream mode not enabled - + return 0; // Multi-stream mode not enabled + // Find the stream metadata for this PID struct ccx_stream_metadata *target_stream = NULL; - + for (int i = 0; i < ctx->demux_ctx->potential_stream_count; i++) { struct ccx_stream_metadata *stream = &ctx->demux_ctx->potential_streams[i]; - + if (stream->pid == data->stream_pid && stream->stream_type == CCX_STREAM_TYPE_DVB_SUB) { target_stream = stream; break; } } - + if (!target_stream) { // Stream not found in registry - this shouldn't happen if PMT parsing worked correctly mprint("Warning: DVB stream PID 0x%x not found in stream registry\n", data->stream_pid); return -1; } - + return route_dvb_stream_to_decoder(ctx, target_stream, data->buffer, data->len, sub); } -int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metadata *stream, - const unsigned char *buf, int buf_size, struct cc_subtitle *sub) +int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metadata *stream, + const unsigned char *buf, int buf_size, struct cc_subtitle *sub) { // Create decoder context on-demand if not already created if (!stream->dvb_decoder_ctx) { - mprint("Creating DVB decoder for stream PID 0x%x (lang: %s)\n", + mprint("Creating DVB decoder for stream PID 0x%x (lang: %s)\n", stream->pid, stream->lang[0] ? stream->lang : "unknown"); - + // Create DVB config for this specific stream struct dvb_config cfg; memset(&cfg, 0, sizeof(cfg)); - cfg.composition_id[0] = 1; // Default composition ID - cfg.ancillary_id[0] = 1; // Default ancillary ID - cfg.lang_index[0] = 1; // Default language index - + cfg.composition_id[0] = 1; // Default composition ID + cfg.ancillary_id[0] = 1; // Default ancillary ID + cfg.lang_index[0] = 1; // Default language index + stream->dvb_decoder_ctx = dvb_init_decoder(&cfg, 0); if (!stream->dvb_decoder_ctx) { @@ -579,7 +579,7 @@ int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metad return -1; } } - + // Get decoder and encoder contexts struct lib_cc_decode *dec_ctx = update_decoder_list(ctx); if (!dec_ctx) @@ -587,21 +587,21 @@ int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metad mprint("Failed to get decoder context for DVB stream PID 0x%x\n", stream->pid); return -1; } - + // Create encoder context for this specific stream if needed struct cap_info cinfo; memset(&cinfo, 0, sizeof(cinfo)); cinfo.pid = stream->pid; cinfo.codec = CCX_CODEC_DVB; strcpy(cinfo.language, stream->lang); - + struct encoder_ctx *enc_ctx = update_encoder_list_cinfo(ctx, &cinfo); if (!enc_ctx) { mprint("Failed to get encoder context for DVB stream PID 0x%x\n", stream->pid); return -1; } - + // Decode the DVB subtitle data using the stream-specific decoder return dvb_decode(stream->dvb_decoder_ctx, enc_ctx, dec_ctx, buf, buf_size, sub); } diff --git a/src/lib_ccx/ts_tables.c b/src/lib_ccx/ts_tables.c index 5db9300ff..65e48bcd9 100644 --- a/src/lib_ccx/ts_tables.c +++ b/src/lib_ccx/ts_tables.c @@ -372,7 +372,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr break; update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_DVB, program_number, ptr); max_dif = 30; - + // [ADD THIS BLOCK: Stream Discovery] if (ccx_options.split_dvb_subs) { @@ -387,7 +387,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr { // Check PID + Logical Type for uniqueness if (ctx->potential_streams[k].pid == elementary_PID && - ctx->potential_streams[k].stream_type == CCX_STREAM_TYPE_DVB_SUB) + ctx->potential_streams[k].stream_type == CCX_STREAM_TYPE_DVB_SUB) { exists = 1; break; @@ -400,10 +400,10 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr meta->pid = elementary_PID; meta->stream_type = CCX_STREAM_TYPE_DVB_SUB; meta->mpeg_type = stream_type; - + // Extract language from DVB config if (cnf.n_language > 0) - snprintf(meta->lang, 4, "%.3s", (char*)&cnf.lang_index[0]); + snprintf(meta->lang, 4, "%.3s", (char *)&cnf.lang_index[0]); else strcpy(meta->lang, "und"); meta->lang[3] = '\0'; @@ -454,7 +454,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_TELETEXT, program_number, NULL); mprint("VBI/teletext stream ID %u (0x%x) for SID %u (0x%x)\n", elementary_PID, elementary_PID, program_number, program_number); - + // [ADD THIS BLOCK: Teletext Stream Discovery] if (ccx_options.split_dvb_subs && is_teletext) { @@ -469,7 +469,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr { // Check PID + Logical Type for uniqueness if (ctx->potential_streams[k].pid == elementary_PID && - ctx->potential_streams[k].stream_type == CCX_STREAM_TYPE_TELETEXT) + ctx->potential_streams[k].stream_type == CCX_STREAM_TYPE_TELETEXT) { exists = 1; break; diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs index 2c7afc2f3..3bf6f6ca5 100755 --- a/src/rust/src/common.rs +++ b/src/rust/src/common.rs @@ -939,7 +939,7 @@ impl CType for CapInfo { prev_counter: self.prev_counter, codec_private_data: self.codec_private_data, ignore: self.ignore, - language: [0; 4], // Initialize language field with zeros + language: [0; 4], // Initialize language field with zeros all_stream: self.all_stream, sib_head: self.sib_head, sib_stream: self.sib_stream, From 7889a410360fe654c7999d3111c4a410e3747b81 Mon Sep 17 00:00:00 2001 From: Rahul-2k4 <216878448+Rahul-2k4@users.noreply.github.com> Date: Sat, 20 Dec 2025 18:48:02 +0530 Subject: [PATCH 6/9] Add --split-dvb-subs validation in Rust parser --- src/rust/src/parser.rs | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index 9949ce752..8d1aa3369 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -1554,6 +1554,30 @@ impl OptionsExt for Options { ); } + // Validation for --split-dvb-subs option + if self.split_dvb_subs { + if self.cc_to_stdout { + fatal!( + cause = ExitCause::IncompatibleParameters; + "Error: --split-dvb-subs cannot be used with stdout.\n" + ); + } + + if self.demux_cfg.nb_ts_cappid > 0 { + fatal!( + cause = ExitCause::IncompatibleParameters; + "Error: --split-dvb-subs cannot be used with manual PID selection (-pn).\n" + ); + } + + if self.multiprogram { + fatal!( + cause = ExitCause::IncompatibleParameters; + "Error: --split-dvb-subs cannot be used with -multiprogram.\n" + ); + } + } + if self.write_format == OutputFormat::WebVtt && self.enc_cfg.encoding != Encoding::UTF8 { self.enc_cfg.encoding = Encoding::UTF8; println!("Note: Output format is WebVTT, forcing UTF-8"); From 0a0c3754e40f708e7ec5331e4197c624e5916929 Mon Sep 17 00:00:00 2001 From: Rahul-2k4 <216878448+Rahul-2k4@users.noreply.github.com> Date: Sat, 20 Dec 2025 21:27:34 +0530 Subject: [PATCH 7/9] Fix bugs in --split-dvb-subs implementation - Fix buffer offset: skip 2-byte header in multi-stream DVB path - Fix Rust build: use ts_cappids.is_empty() instead of nb_ts_cappid - Fix dangling pointer: set cfg to NULL after values are copied --- src/lib_ccx/dvb_subtitle_decoder.c | 2 +- src/lib_ccx/lib_ccx.c | 3 ++- src/rust/src/parser.rs | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/lib_ccx/dvb_subtitle_decoder.c b/src/lib_ccx/dvb_subtitle_decoder.c index 27f543e90..4273dc71f 100644 --- a/src/lib_ccx/dvb_subtitle_decoder.c +++ b/src/lib_ccx/dvb_subtitle_decoder.c @@ -582,7 +582,7 @@ struct ccx_decoders_dvb_context *dvb_init_decoder(struct dvb_config *cfg, int in } dvb_ctx->private_data = ctx; - dvb_ctx->cfg = cfg; + dvb_ctx->cfg = NULL; // Config values are copied into DVBSubContext, don't store pointer dvb_ctx->initialized_ocr = initialized_ocr; return dvb_ctx; diff --git a/src/lib_ccx/lib_ccx.c b/src/lib_ccx/lib_ccx.c index decf3ef87..bbd1d099c 100644 --- a/src/lib_ccx/lib_ccx.c +++ b/src/lib_ccx/lib_ccx.c @@ -561,7 +561,8 @@ int process_dvb_multi_stream(struct lib_ccx_ctx *ctx, struct demuxer_data *data, return -1; } - return route_dvb_stream_to_decoder(ctx, target_stream, data->buffer, data->len, sub); + // Skip first 2 bytes (same as original dvbsub_decode path in general_loop.c) + return route_dvb_stream_to_decoder(ctx, target_stream, data->buffer + 2, data->len - 2, sub); } int route_dvb_stream_to_decoder(struct lib_ccx_ctx *ctx, struct ccx_stream_metadata *stream, diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs index 3e21a60bd..e6818f801 100644 --- a/src/rust/src/parser.rs +++ b/src/rust/src/parser.rs @@ -1567,7 +1567,7 @@ impl OptionsExt for Options { ); } - if self.demux_cfg.nb_ts_cappid > 0 { + if !self.demux_cfg.ts_cappids.is_empty() { fatal!( cause = ExitCause::IncompatibleParameters; "Error: --split-dvb-subs cannot be used with manual PID selection (-pn).\n" From 89b3621f0280424e8dcffed5e5107cd882a2a093 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 16:03:16 +0000 Subject: [PATCH 8/9] Initial plan From ce13a9201b28dd46d35c2aa94004a451982abde3 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Sat, 20 Dec 2025 16:16:40 +0000 Subject: [PATCH 9/9] Fix formatting: remove extra space before comment in dvb_subtitle_decoder.c Co-authored-by: Rahul-2k4 <216878448+Rahul-2k4@users.noreply.github.com> --- src/lib_ccx/dvb_subtitle_decoder.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/lib_ccx/dvb_subtitle_decoder.c b/src/lib_ccx/dvb_subtitle_decoder.c index 4273dc71f..ad47556c5 100644 --- a/src/lib_ccx/dvb_subtitle_decoder.c +++ b/src/lib_ccx/dvb_subtitle_decoder.c @@ -582,7 +582,7 @@ struct ccx_decoders_dvb_context *dvb_init_decoder(struct dvb_config *cfg, int in } dvb_ctx->private_data = ctx; - dvb_ctx->cfg = NULL; // Config values are copied into DVBSubContext, don't store pointer + dvb_ctx->cfg = NULL; // Config values are copied into DVBSubContext, don't store pointer dvb_ctx->initialized_ocr = initialized_ocr; return dvb_ctx;