feat: auto-extract multi-language DVB subtitles into per-language files (#447) (#2243)

* feat: auto-extract multi-language DVB subtitles into per-language files

* style: apply clang-format to multi-DVB subtitle extraction changes

* fix: only add lang suffix when 2+ DVB PID

* fix segfault from uninitialized dvb_lang

* docs: add CHANGES.TXT entry for multi-language DVB subtitle extraction

* fix: skip non-DVB encoders in lookup
This commit is contained in:
ujjwalr27
2026-04-11 14:53:16 +09:00
committed by GitHub
parent 5fdd9b8626
commit ad4886e719
13 changed files with 219 additions and 26 deletions

View File

@@ -2,6 +2,7 @@
-------------------
- New: Allow output \0 terminated frames via --null-terminated
- New: Added ASS/SSA \pos-based positioning for CEA-608 captions when layout is simple (12 rows) (#1726)
- New: Auto-extract multi-language DVB subtitles into per-language files.(#2243)
- 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.
- Fix: Resolve Windows MSVC debug build crash caused by cross-CRT invalid free on Rust-allocated output_filename (#2126)

View File

@@ -402,9 +402,9 @@ struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg)
for (i = 0; i < cfg->nb_ts_cappid; i++)
{
if (ctx->codec == CCX_CODEC_ANY)
update_capinfo(ctx, cfg->ts_cappids[i], cfg->ts_datastreamtype, CCX_CODEC_NONE, 0, NULL);
update_capinfo(ctx, cfg->ts_cappids[i], cfg->ts_datastreamtype, CCX_CODEC_NONE, 0, NULL, NULL);
else
update_capinfo(ctx, cfg->ts_cappids[i], cfg->ts_datastreamtype, ctx->codec, 0, NULL);
update_capinfo(ctx, cfg->ts_cappids[i], cfg->ts_datastreamtype, ctx->codec, 0, NULL, NULL);
}
ctx->flag_ts_forced_cappid = cfg->nb_ts_cappid ? CCX_TRUE : CCX_FALSE;

View File

@@ -47,7 +47,7 @@ struct program_info
int16_t pcr_pid;
uint64_t got_important_streams_min_pts[COUNT];
int has_all_min_pts;
char virtual_channel[16]; // Stores ATSC virtual channel like "2.1"
char virtual_channel[16]; // Stores ATSC virtual channel like "2.1"
};
struct cap_info
@@ -63,6 +63,7 @@ struct cap_info
int prev_counter;
void *codec_private_data;
int ignore;
char lang[4]; /* ISO-639 language code (DVB subtitle) */
/**
List joining all stream in TS
@@ -162,7 +163,8 @@ struct ccx_demuxer
int (*open)(struct ccx_demuxer *ctx, const char *file_name);
int (*is_open)(struct ccx_demuxer *ctx);
int (*get_stream_mode)(struct ccx_demuxer *ctx);
LLONG (*get_filesize)(struct ccx_demuxer *ctx);
LLONG(*get_filesize)
(struct ccx_demuxer *ctx);
};
struct demuxer_data
@@ -185,7 +187,7 @@ struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg);
void ccx_demuxer_delete(struct ccx_demuxer **ctx);
struct demuxer_data *alloc_demuxer_data(void);
void delete_demuxer_data(struct demuxer_data *data);
int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream, enum ccx_code_type codec, int pn, void *private_data);
int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream, enum ccx_code_type codec, int pn, void *private_data, const char *lang);
struct cap_info *get_cinfo(struct ccx_demuxer *ctx, int pid);
int need_cap_info(struct ccx_demuxer *ctx, int program_number);
int need_cap_info_for_pid(struct ccx_demuxer *ctx, int pid);
@@ -194,6 +196,7 @@ struct demuxer_data *get_data_stream(struct demuxer_data *data, int pid);
int get_best_stream(struct ccx_demuxer *ctx);
void ignore_other_stream(struct ccx_demuxer *ctx, int pid);
void dinit_cap(struct ccx_demuxer *ctx);
int copy_capbuf_demux_data(struct ccx_demuxer *ctx, struct demuxer_data **data, struct cap_info *cinfo);
int get_programme_number(struct ccx_demuxer *ctx, int pid);
struct cap_info *get_best_sib_stream(struct cap_info *program);
void ignore_other_sib_stream(struct cap_info *head, int pid);

View File

@@ -813,7 +813,17 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt)
ctx->is_mkv = 0;
ctx->last_string = NULL;
ctx->transcript_settings = &opt->transcript_settings;
/* Deep-copy transcript_settings so the encoder owns it independently of
the caller's encoder_cfg (which may be a stack-local variable). */
ctx->transcript_settings = malloc(sizeof(struct ccx_encoders_transcript_format));
if (!ctx->transcript_settings)
{
freep(&ctx->out);
freep(&ctx->buffer);
free(ctx);
return NULL;
}
memcpy(ctx->transcript_settings, &opt->transcript_settings, sizeof(struct ccx_encoders_transcript_format));
ctx->no_bom = opt->no_bom;
ctx->sentence_cap = opt->sentence_cap;
ctx->filter_profanity = opt->filter_profanity;

View File

@@ -166,6 +166,7 @@ struct encoder_ctx
int new_sentence; // Capitalize next letter?
int program_number;
char dvb_lang[4]; /* ISO-639 language code for DVB subtitle encoder */
struct list_head list;
/* split-by-sentence stuff */

View File

@@ -1317,6 +1317,62 @@ int process_non_multiprogram_general_loop(struct lib_ccx_ctx *ctx,
}
}
}
/* -----------------------------------------------------------------------
* Process additional DVB subtitle PIDs (extra languages).
* ----------------------------------------------------------------------- */
{
struct cap_info *dvb_iter;
list_for_each_entry(dvb_iter, &ctx->demux_ctx->cinfo_tree.all_stream, all_stream, struct cap_info)
{
if (dvb_iter->codec != CCX_CODEC_DVB)
continue;
if (dvb_iter->pid == pid)
continue;
struct demuxer_data *dvb_data = get_data_stream(*datalist, dvb_iter->pid);
if (!dvb_data || dvb_data->len == 0)
continue;
struct encoder_ctx *dvb_enc = update_encoder_list_cinfo(ctx, dvb_iter);
struct lib_cc_decode *dvb_dec = update_decoder_list_cinfo(ctx, dvb_iter);
if (!dvb_enc || !dvb_dec || !dvb_dec->timing)
continue;
if (dvb_data->pts != CCX_NOPTS)
{
struct ccx_rational tb = {1, MPEG_CLOCK_FREQ};
LLONG pts = (dvb_data->tb.num != 1 || dvb_data->tb.den != MPEG_CLOCK_FREQ)
? change_timebase(dvb_data->pts, dvb_data->tb, tb)
: dvb_data->pts;
set_current_pts(dvb_dec->timing, pts);
if (dvb_dec->timing->min_pts == 0x01FFFFFFFFLL)
{
dvb_dec->timing->min_pts = pts;
dvb_dec->timing->pts_set = 2;
dvb_dec->timing->sync_pts = pts;
}
set_fts(dvb_dec->timing);
}
dvb_enc->timing = dvb_dec->timing;
int dvb_ret = process_data(dvb_enc, dvb_dec, dvb_data);
if (dvb_ret || dvb_enc->srt_counter)
*caps = 1;
if (!(!terminate_asap && !end_of_file && is_decoder_processed_enough(ctx) == CCX_FALSE))
{
if (dvb_dec->dec_sub.prev && dvb_dec->dec_sub.prev->end_time == 0)
{
dvb_dec->dec_sub.prev->end_time = get_fts(dvb_dec->timing, dvb_dec->current_field);
if (dvb_enc != NULL && dvb_enc->prev != NULL)
encode_sub(dvb_enc->prev, dvb_dec->dec_sub.prev);
dvb_dec->dec_sub.prev->got_output = 0;
}
}
}
}
return ret;
}

View File

@@ -222,10 +222,8 @@ void dinit_libraries(struct lib_ccx_ctx **ctx)
struct encoder_ctx *enc_ctx;
struct lib_cc_decode *dec_ctx;
struct lib_cc_decode *dec_ctx1;
int i;
list_for_each_entry_safe(dec_ctx, dec_ctx1, &lctx->dec_ctx_head, list, struct lib_cc_decode)
{
LLONG cfts;
void *saved_private_data = dec_ctx->private_data; // Save before close NULLs it
if (dec_ctx->codec == CCX_CODEC_DVB)
dvbsub_close_decoder(&dec_ctx->private_data);
@@ -248,7 +246,6 @@ void dinit_libraries(struct lib_ccx_ctx **ctx)
}
flush_cc_decode(dec_ctx, &dec_ctx->dec_sub);
cfts = get_fts(dec_ctx->timing, dec_ctx->current_field);
enc_ctx = get_encoder_by_pn(lctx, dec_ctx->program_number);
if (enc_ctx && dec_ctx->dec_sub.got_output == CCX_TRUE)
{
@@ -257,8 +254,16 @@ void dinit_libraries(struct lib_ccx_ctx **ctx)
}
list_del(&dec_ctx->list);
dinit_cc_decode(&dec_ctx);
if (enc_ctx)
}
/* Clean up all encoders separately.
With multi-DVB, multiple decoders share the same program_number
but have different language-specific encoders. Cleaning encoders
inside the decoder loop caused wrong encoder pairing and double-free. */
{
struct encoder_ctx *enc_tmp;
list_for_each_entry_safe(enc_ctx, enc_tmp, &lctx->enc_ctx_head, list, struct encoder_ctx)
{
LLONG cfts = 0; // Use 0 as fallback since decoders are already freed
list_del(&enc_ctx->list);
dinit_encoder(&enc_ctx, cfts);
}
@@ -337,6 +342,7 @@ struct lib_cc_decode *update_decoder_list(struct lib_ccx_ctx *ctx)
dec_ctx->dec_sub.prev = malloc(sizeof(struct cc_subtitle));
if (!dec_ctx->prev || !dec_ctx->dec_sub.prev)
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "update_decoder_list: Not enough memory for DVB context");
memset(dec_ctx->prev, 0, sizeof(struct lib_cc_decode));
memset(dec_ctx->dec_sub.prev, 0, sizeof(struct cc_subtitle));
}
}
@@ -351,6 +357,14 @@ struct lib_cc_decode *update_decoder_list_cinfo(struct lib_ccx_ctx *ctx, struct
{
if (!cinfo || ctx->multiprogram == CCX_FALSE)
{
/* For DVB subtitles, match by private_data (per-PID decoder) */
if (cinfo && cinfo->codec == CCX_CODEC_DVB)
{
if (dec_ctx->program_number == cinfo->program_number &&
dec_ctx->private_data == cinfo->codec_private_data)
return dec_ctx;
continue;
}
/* Update private_data from cinfo if available.
This is needed after PAT changes when dinit_cap() freed the old context
and a new cap_info was created with a new codec_private_data. */
@@ -375,7 +389,16 @@ struct lib_cc_decode *update_decoder_list_cinfo(struct lib_ccx_ctx *ctx, struct
}
if (ctx->multiprogram == CCX_FALSE)
{
if (list_empty(&ctx->dec_ctx_head))
/* For DVB, always create a new per-PID decoder even in single-program mode,
because each DVB subtitle PID has its own codec_private_data context. */
if (cinfo && cinfo->codec == CCX_CODEC_DVB)
{
dec_ctx = init_cc_decode(ctx->dec_global_setting);
if (!dec_ctx)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In update_decoder_list_cinfo: Not enough memory allocating dec_ctx for DVB\n");
list_add_tail(&(dec_ctx->list), &(ctx->dec_ctx_head));
}
else if (list_empty(&ctx->dec_ctx_head))
{
dec_ctx = init_cc_decode(ctx->dec_global_setting);
if (!dec_ctx)
@@ -394,6 +417,15 @@ struct lib_cc_decode *update_decoder_list_cinfo(struct lib_ccx_ctx *ctx, struct
// DVB related
dec_ctx->prev = NULL;
dec_ctx->dec_sub.prev = NULL;
if (cinfo && cinfo->codec == CCX_CODEC_DVB)
{
dec_ctx->prev = malloc(sizeof(struct lib_cc_decode));
dec_ctx->dec_sub.prev = malloc(sizeof(struct cc_subtitle));
if (!dec_ctx->prev || !dec_ctx->dec_sub.prev)
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "update_decoder_list_cinfo: Not enough memory for DVB context");
memset(dec_ctx->prev, 0, sizeof(struct lib_cc_decode));
memset(dec_ctx->dec_sub.prev, 0, sizeof(struct cc_subtitle));
}
return dec_ctx;
}
@@ -420,7 +452,18 @@ struct encoder_ctx *update_encoder_list_cinfo(struct lib_ccx_ctx *ctx, struct ca
list_for_each_entry(enc_ctx, &ctx->enc_ctx_head, list, struct encoder_ctx)
{
if (ctx->multiprogram == CCX_FALSE)
{
/* For DVB subtitles, match by language — skip non-DVB encoders */
if (cinfo && cinfo->codec == CCX_CODEC_DVB && cinfo->lang[0])
{
if (enc_ctx->dvb_lang[0] &&
enc_ctx->program_number == pn &&
strcmp(enc_ctx->dvb_lang, cinfo->lang) == 0)
return enc_ctx;
continue;
}
return enc_ctx;
}
if (enc_ctx->program_number == pn)
return enc_ctx;
@@ -430,6 +473,47 @@ struct encoder_ctx *update_encoder_list_cinfo(struct lib_ccx_ctx *ctx, struct ca
if (!extension && ccx_options.enc_cfg.write_format != CCX_OF_CURL)
return NULL;
/* Create per-language DVB encoder only when there are 2+ DVB subtitle PIDs.
Single-DVB-stream recordings fall through to the standard encoder path
to preserve backward compatible filenames. */
if (cinfo && cinfo->codec == CCX_CODEC_DVB && cinfo->lang[0])
{
/* Count DVB subtitle PIDs in this program */
int dvb_pid_count = 0;
struct cap_info *ci;
list_for_each_entry(ci, &ctx->demux_ctx->cinfo_tree.all_stream, all_stream, struct cap_info)
{
if (ci->codec == CCX_CODEC_DVB && ci->program_number == cinfo->program_number)
dvb_pid_count++;
}
if (dvb_pid_count >= 2)
{
struct encoder_cfg local_cfg = ccx_options.enc_cfg;
local_cfg.program_number = pn;
local_cfg.in_format = in_format;
char *basefilename = get_basename(ctx->basefilename);
char suffix[8];
snprintf(suffix, sizeof(suffix), "_%s", cinfo->lang);
local_cfg.output_filename = create_outfilename(basefilename, suffix, extension);
free(basefilename);
enc_ctx = init_encoder(&local_cfg);
if (enc_ctx)
{
enc_ctx->program_number = pn;
strncpy(enc_ctx->dvb_lang, cinfo->lang, 3);
enc_ctx->dvb_lang[3] = '\0';
list_add_tail(&enc_ctx->list, &ctx->enc_ctx_head);
enc_ctx->prev = NULL;
enc_ctx->write_previous = 0;
}
free(local_cfg.output_filename);
return enc_ctx;
}
/* Single DVB stream: fall through to standard encoder creation */
}
if (ctx->multiprogram == CCX_FALSE)
{
if (ctx->out_interval != -1)
@@ -492,9 +576,16 @@ struct encoder_ctx *update_encoder_list_cinfo(struct lib_ccx_ctx *ctx, struct ca
}
// DVB related
enc_ctx->prev = NULL;
if (cinfo)
if (cinfo->codec == CCX_CODEC_DVB)
enc_ctx->write_previous = 0;
if (cinfo && cinfo->codec == CCX_CODEC_DVB && cinfo->lang[0])
{
strncpy(enc_ctx->dvb_lang, cinfo->lang, 3);
enc_ctx->dvb_lang[3] = '\0';
enc_ctx->write_previous = 0;
}
else
{
memset(enc_ctx->dvb_lang, 0, sizeof(enc_ctx->dvb_lang));
}
return enc_ctx;
}

View File

@@ -412,7 +412,7 @@ void look_for_caption_data(struct ccx_demuxer *ctx, struct ts_payload *payload)
stream_type == CCX_STREAM_TYPE_VIDEO_H264 ? "H.264" : (stream_type == CCX_STREAM_TYPE_VIDEO_HEVC ? "HEVC" : "MPEG-2"));
// Register this PID as a video stream that may contain captions
update_capinfo(ctx, payload->pid, stream_type, CCX_CODEC_ATSC_CC, 0, NULL);
update_capinfo(ctx, payload->pid, stream_type, CCX_CODEC_ATSC_CC, 0, NULL, NULL);
ctx->PIDs_seen[payload->pid] = 3;
return;
}
@@ -434,7 +434,7 @@ void look_for_caption_data(struct ccx_demuxer *ctx, struct ts_payload *payload)
if (cinfo == NULL)
{
mprint("Registering PID %u as MPEG-2 video with captions (no PAT/PMT detected).\n", payload->pid);
update_capinfo(ctx, payload->pid, CCX_STREAM_TYPE_VIDEO_MPEG2, CCX_CODEC_ATSC_CC, 0, NULL);
update_capinfo(ctx, payload->pid, CCX_STREAM_TYPE_VIDEO_MPEG2, CCX_CODEC_ATSC_CC, 0, NULL, NULL);
}
ctx->PIDs_seen[payload->pid] = 3;

View File

@@ -42,7 +42,7 @@ void ignore_other_stream(struct ccx_demuxer *ctx, int pid)
struct cap_info *iter;
list_for_each_entry(iter, &ctx->cinfo_tree.all_stream, all_stream, struct cap_info)
{
if (iter->pid != pid)
if (iter->pid != pid && iter->codec != CCX_CODEC_DVB)
iter->ignore = 1;
}
}
@@ -92,7 +92,7 @@ void ignore_other_sib_stream(struct cap_info *head, int pid)
struct cap_info *iter;
list_for_each_entry(iter, &head->sib_head, sib_stream, struct cap_info)
{
if (iter->pid != pid)
if (iter->pid != pid && iter->codec != CCX_CODEC_DVB)
iter->ignore = 1;
}
}
@@ -178,7 +178,7 @@ static void *init_private_data(enum ccx_code_type codec)
return NULL;
}
}
int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream, enum ccx_code_type codec, int pn, void *private_data)
int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream, enum ccx_code_type codec, int pn, void *private_data, const char *lang)
{
struct cap_info *ptr;
struct cap_info *tmp;
@@ -220,6 +220,15 @@ int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream
tmp->capbufsize = 0;
tmp->ignore = 0;
}
if (lang)
{
strncpy(tmp->lang, lang, 3);
tmp->lang[3] = '\0';
}
else
{
tmp->lang[0] = '\0';
}
return CCX_OK;
}
}
@@ -247,6 +256,15 @@ int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream
tmp->codec_private_data = init_private_data(codec);
else
tmp->codec_private_data = private_data;
if (lang)
{
strncpy(tmp->lang, lang, 3);
tmp->lang[3] = '\0';
}
else
{
tmp->lang[0] = '\0';
}
list_add_tail(&(tmp->all_stream), &(ptr->all_stream));

View File

@@ -380,7 +380,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr
ptr = init_isdb_decoder();
if (ptr == NULL)
break;
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ISDB_CC, program_number, ptr);
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ISDB_CC, program_number, ptr, NULL);
}
if (CCX_MPEG_DSC_DVB_SUBTITLE == descriptor_tag)
{
@@ -402,7 +402,16 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr
ptr = dvbsub_init_decoder(&cnf);
if (ptr == NULL)
break;
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_DVB, program_number, ptr);
/* Extract 3-byte ISO-639 language code from DVB subtitle descriptor */
char dvb_lang[4] = {0};
if (desc_len >= 3)
{
dvb_lang[0] = (char)cctolower(es_info[0]);
dvb_lang[1] = (char)cctolower(es_info[1]);
dvb_lang[2] = (char)cctolower(es_info[2]);
}
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_DVB, program_number, ptr, dvb_lang);
mprint("DVB subtitle PID %u language: %s\n", elementary_PID, dvb_lang[0] ? dvb_lang : "(unknown)");
max_dif = 30;
}
}
@@ -440,7 +449,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr
dbg_print(CCX_DMT_PMT, "%s", is_608 ? " CEA-608" : " CEA-708");
}
}
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL);
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL, NULL);
}
}
@@ -462,7 +471,7 @@ 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;
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_TELETEXT, program_number, NULL);
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_TELETEXT, program_number, NULL, NULL);
mprint("VBI/teletext stream ID %u (0x%x) for SID %u (0x%x)\n",
elementary_PID, elementary_PID, program_number, program_number);
}
@@ -474,7 +483,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr
unsigned descriptor_tag = buf[i + 5];
if (descriptor_tag == 0x45)
{
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL);
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL, NULL);
// mprint ("VBI stream ID %u (0x%x) for SID %u (0x%x) - teletext is disabled, will be processed as closed captions.\n",
// elementary_PID, elementary_PID, program_number, program_number);
}
@@ -485,7 +494,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr
{
if (stream_type == CCX_STREAM_TYPE_VIDEO_HEVC)
mprint("Detected HEVC video stream (0x24) - enabling ATSC CC parsing.\n");
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL);
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_ATSC_CC, program_number, NULL, NULL);
}
if (need_cap_info_for_pid(ctx, elementary_PID) == CCX_TRUE)
@@ -498,7 +507,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr
mprint("Please pass -streamtype to select manually.\n");
fatal(EXIT_FAILURE, "-streamtype has to be manually selected.");
}
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_NONE, program_number, NULL);
update_capinfo(ctx, elementary_PID, stream_type, CCX_CODEC_NONE, program_number, NULL, NULL);
continue;
}

View File

@@ -1037,6 +1037,7 @@ impl CType<cap_info> for CapInfo {
prev_counter: self.prev_counter,
codec_private_data: self.codec_private_data,
ignore: self.ignore,
lang: self.lang,
all_stream: self.all_stream,
sib_head: self.sib_head,
sib_stream: self.sib_stream,

View File

@@ -568,6 +568,7 @@ impl FromCType<cap_info> for CapInfo {
prev_counter: info.prev_counter,
codec_private_data: info.codec_private_data,
ignore: info.ignore,
lang: info.lang,
all_stream: list_head {
next: info.all_stream.next,
prev: info.all_stream.prev,

View File

@@ -79,6 +79,7 @@ pub struct CapInfo {
pub prev_counter: i32,
pub codec_private_data: *mut std::ffi::c_void,
pub ignore: i32,
pub lang: [i8; 4],
/**
* List joining all streams in TS
@@ -325,6 +326,7 @@ impl Default for CapInfo {
prev_counter: 0,
codec_private_data: null_mut(),
ignore: 0,
lang: [0; 4],
all_stream: list_head::default(),
sib_head: list_head::default(),