mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-07-08 18:06:30 +00:00
* 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:
@@ -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 (1–2 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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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(),
|
||||
|
||||
Reference in New Issue
Block a user