Skip to content

Commit b0800a1

Browse files
authored
feat(input): Add native SCC (Scenarist Closed Caption) input support
2 parents 14e6919 + 2b0d9ed commit b0800a1

File tree

17 files changed

+671
-7
lines changed

17 files changed

+671
-7
lines changed

src/ccextractor.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,12 @@ int start_ccx()
202202
if (!ret)
203203
ret = tmp;
204204
break;
205+
case CCX_SM_SCC:
206+
mprint("\rAnalyzing data in SCC (Scenarist Closed Caption) mode\n");
207+
tmp = raw_loop(ctx);
208+
if (!ret)
209+
ret = tmp;
210+
break;
205211
case CCX_SM_RCWT:
206212
mprint("\rAnalyzing data in CCExtractor's binary format\n");
207213
tmp = rcwt_loop(ctx);

src/lib_ccx/ccx_common_constants.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,7 @@ enum ccx_stream_mode_enum
212212
CCX_SM_GXF = 11,
213213
CCX_SM_MKV = 12,
214214
CCX_SM_MXF = 13,
215+
CCX_SM_SCC = 14, // Scenarist Closed Caption input
215216

216217
CCX_SM_AUTODETECT = 16
217218
};

src/lib_ccx/ccx_common_option.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,8 @@ void init_options(struct ccx_s_options *options)
152152
options->settings_dtvcc.services_enabled, 0,
153153
CCX_DTVCC_MAX_SERVICES * sizeof(options->settings_dtvcc.services_enabled[0]));
154154

155+
options->scc_framerate = 0; // Default: 29.97fps
156+
155157
#ifdef WITH_LIBCURL
156158
options->curlposturl = NULL;
157159
#endif

src/lib_ccx/ccx_common_option.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ struct ccx_s_options // Options from user parameters
195195
int multiprogram;
196196
int out_interval;
197197
int segment_on_key_frames_only;
198+
int scc_framerate; // SCC input framerate: 0=29.97 (default), 1=24, 2=25, 3=30
198199
#ifdef WITH_LIBCURL
199200
char *curlposturl;
200201
#endif

src/lib_ccx/general_loop.c

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -575,6 +575,7 @@ int raw_loop(struct lib_ccx_ctx *ctx)
575575
struct lib_cc_decode *dec_ctx = NULL;
576576
int caps = 0;
577577
int is_dvdraw = 0; // Flag to track if this is DVD raw format
578+
int is_scc = 0; // Flag to track if this is SCC format
578579
int is_mcc_output = 0; // Flag for MCC output format
579580

580581
dec_ctx = update_decoder_list(ctx);
@@ -607,13 +608,20 @@ int raw_loop(struct lib_ccx_ctx *ctx)
607608
break;
608609

609610
// Check if this is DVD raw format using Rust detection
610-
if (!is_dvdraw && ccxr_is_dvdraw_header(data->buffer, (unsigned int)data->len))
611+
if (!is_dvdraw && !is_scc && ccxr_is_dvdraw_header(data->buffer, (unsigned int)data->len))
611612
{
612613
is_dvdraw = 1;
613614
mprint("Detected McPoodle's DVD raw format\n");
614615
}
615616

616-
if (is_mcc_output && !is_dvdraw)
617+
// Check if this is SCC format using Rust detection
618+
if (!is_scc && !is_dvdraw && ccxr_is_scc_file(data->buffer, (unsigned int)data->len))
619+
{
620+
is_scc = 1;
621+
mprint("Detected SCC (Scenarist Closed Caption) format\n");
622+
}
623+
624+
if (is_mcc_output && !is_dvdraw && !is_scc)
617625
{
618626
// For MCC output, encode raw data directly without decoding
619627
// This preserves the original CEA-608 byte pairs in CDP format
@@ -626,6 +634,11 @@ int raw_loop(struct lib_ccx_ctx *ctx)
626634
// Use Rust implementation - handles timing internally
627635
ret = ccxr_process_dvdraw(dec_ctx, dec_sub, data->buffer, (unsigned int)data->len);
628636
}
637+
else if (is_scc)
638+
{
639+
// Use Rust SCC implementation - handles timing internally via SMPTE timecodes
640+
ret = ccxr_process_scc(dec_ctx, dec_sub, data->buffer, (unsigned int)data->len, ccx_options.scc_framerate);
641+
}
629642
else
630643
{
631644
ret = process_raw(dec_ctx, dec_sub, data->buffer, data->len);

src/lib_ccx/lib_ccx.h

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ struct file_report
4343
};
4444

4545
// Stuff for telxcc.c
46-
#define MAX_TLT_PAGES_EXTRACT 8 // Maximum number of teletext pages to extract simultaneously
46+
#define MAX_TLT_PAGES_EXTRACT 8 // Maximum number of teletext pages to extract simultaneously
4747

4848
struct ccx_s_teletext_config
4949
{
@@ -55,11 +55,11 @@ struct ccx_s_teletext_config
5555
uint8_t nonempty : 1; // produce at least one (dummy) frame
5656
// uint8_t se_mode : 1; // search engine compatible mode => Uses CCExtractor's write_format
5757
// uint64_t utc_refvalue; // UTC referential value => Moved to ccx_decoders_common, so can be used for other decoders (608/xds) too
58-
uint16_t user_page; // Page selected by user (legacy, first page)
58+
uint16_t user_page; // Page selected by user (legacy, first page)
5959
// Multi-page teletext extraction (issue #665)
60-
uint16_t user_pages[MAX_TLT_PAGES_EXTRACT]; // Pages selected by user for extraction
61-
int num_user_pages; // Number of pages to extract (0 = auto-detect single page)
62-
int extract_all_pages; // If 1, extract all detected subtitle pages
60+
uint16_t user_pages[MAX_TLT_PAGES_EXTRACT]; // Pages selected by user for extraction
61+
int num_user_pages; // Number of pages to extract (0 = auto-detect single page)
62+
int extract_all_pages; // If 1, extract all detected subtitle pages
6363
int dolevdist; // 0=Don't attempt to correct errors
6464
int levdistmincnt, levdistmaxpct; // Means 2 fails or less is "the same", 10% or less is also "the same"
6565
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
@@ -183,6 +183,10 @@ size_t process_raw(struct lib_cc_decode *ctx, struct cc_subtitle *sub, unsigned
183183
unsigned int ccxr_process_dvdraw(struct lib_cc_decode *ctx, struct cc_subtitle *sub, const unsigned char *buffer, unsigned int len);
184184
int ccxr_is_dvdraw_header(const unsigned char *buffer, unsigned int len);
185185

186+
// Rust FFI: SCC (Scenarist Closed Caption) format processing (see src/rust/src/demuxer/scc.rs)
187+
unsigned int ccxr_process_scc(struct lib_cc_decode *ctx, struct cc_subtitle *sub, const unsigned char *buffer, unsigned int len, int framerate);
188+
int ccxr_is_scc_file(const unsigned char *buffer, unsigned int len);
189+
186190
int general_loop(struct lib_ccx_ctx *ctx);
187191
void process_hex(struct lib_ccx_ctx *ctx, char *filename);
188192
int rcwt_loop(struct lib_ccx_ctx *ctx);

src/lib_ccx/stream_functions.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,30 @@ void detect_stream_type(struct ccx_demuxer *ctx)
7878
ctx->startbytes[7] == 0xf8)
7979
ctx->stream_mode = CCX_SM_MCPOODLESRAW;
8080
}
81+
// Check for SCC (Scenarist Closed Caption) text format
82+
// SCC files start with "Scenarist_SCC V1.0" (18 bytes), optionally with UTF-8 BOM (3 bytes)
83+
if (ctx->stream_mode == CCX_SM_ELEMENTARY_OR_NOT_FOUND)
84+
{
85+
unsigned char *check_buf = ctx->startbytes;
86+
int check_pos = 0;
87+
88+
// Skip UTF-8 BOM if present
89+
if (ctx->startbytes_avail >= 3 &&
90+
ctx->startbytes[0] == 0xEF &&
91+
ctx->startbytes[1] == 0xBB &&
92+
ctx->startbytes[2] == 0xBF)
93+
{
94+
check_buf += 3;
95+
check_pos = 3;
96+
}
97+
98+
if (ctx->startbytes_avail >= check_pos + 18 &&
99+
memcmp(check_buf, "Scenarist_SCC V1.0", 18) == 0)
100+
{
101+
ctx->stream_mode = CCX_SM_SCC;
102+
mprint("Detected SCC (Scenarist Closed Caption) format\n");
103+
}
104+
}
81105
#ifdef WTV_DEBUG
82106
if (ctx->stream_mode == CCX_SM_ELEMENTARY_OR_NOT_FOUND && ctx->startbytes_avail >= 6)
83107
{

src/rust/lib_ccxr/src/common/constants.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,7 @@ pub enum StreamMode {
278278
Gxf = 11,
279279
Mkv = 12,
280280
Mxf = 13,
281+
Scc = 14, // Scenarist Closed Caption input
281282
Autodetect = 16,
282283
}
283284
#[derive(Debug, Eq, Clone, Copy)]

src/rust/lib_ccxr/src/common/options.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -517,6 +517,8 @@ pub struct Options {
517517
pub multiprogram: bool,
518518
pub out_interval: i32,
519519
pub segment_on_key_frames_only: bool,
520+
/// SCC input framerate: 0=29.97 (default), 1=24, 2=25, 3=30
521+
pub scc_framerate: i32,
520522
pub debug_mask: DebugMessageMask,
521523

522524
#[cfg(feature = "with_libcurl")]
@@ -618,6 +620,7 @@ impl Default for Options {
618620
multiprogram: Default::default(),
619621
out_interval: -1,
620622
segment_on_key_frames_only: Default::default(),
623+
scc_framerate: 0, // 0 = 29.97fps (default)
621624
debug_mask: DebugMessageMask::new(
622625
DebugMessageFlag::GENERIC_NOTICE,
623626
DebugMessageFlag::VERBOSE,

src/rust/src/args.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,11 @@ pub struct Args {
290290
/// DVD Recorder)
291291
#[arg(long="90090", verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)]
292292
pub mpeg90090: bool,
293+
/// Set the frame rate for SCC (Scenarist Closed Caption) input files.
294+
/// Valid values: 29.97 (default), 24, 25, 30
295+
/// Example: --scc-framerate 25
296+
#[arg(long="scc-framerate", verbatim_doc_comment, value_name="fps", help_heading=OPTIONS_AFFECTING_INPUT_FILES)]
297+
pub scc_framerate: Option<String>,
293298
/// By default, ccextractor will process input files in
294299
/// sequence as if they were all one large file (i.e.
295300
/// split by a generic, non video-aware tool. If you

0 commit comments

Comments
 (0)