From 7d3f3d700bd4a5c0f175fc30b5ae5499590f0730 Mon Sep 17 00:00:00 2001 From: vats004 Date: Sun, 23 Mar 2025 12:21:49 +0530 Subject: [PATCH 1/4] added isdbs module in lib_ccxr --- src/lib_ccx/ccx_decoders_isdb.c | 31 + src/rust/Cargo.lock | 102 +++ src/rust/Cargo.toml | 7 + src/rust/lib_ccxr/Cargo.lock | 135 +++ src/rust/lib_ccxr/Cargo.toml | 8 + src/rust/lib_ccxr/src/isdbs/isdbd.rs | 1250 ++++++++++++++++++++++++++ src/rust/lib_ccxr/src/isdbs/isdbs.rs | 1204 +++++++++++++++++++++++++ src/rust/lib_ccxr/src/isdbs/mod.rs | 2 + src/rust/lib_ccxr/src/lib.rs | 1 + src/rust/src/libccxr_exports/isdb.rs | 72 ++ src/rust/src/libccxr_exports/mod.rs | 1 + 11 files changed, 2813 insertions(+) create mode 100644 src/rust/lib_ccxr/src/isdbs/isdbd.rs create mode 100644 src/rust/lib_ccxr/src/isdbs/isdbs.rs create mode 100644 src/rust/lib_ccxr/src/isdbs/mod.rs create mode 100644 src/rust/src/libccxr_exports/isdb.rs diff --git a/src/lib_ccx/ccx_decoders_isdb.c b/src/lib_ccx/ccx_decoders_isdb.c index b06471e7f..af1223efd 100644 --- a/src/lib_ccx/ccx_decoders_isdb.c +++ b/src/lib_ccx/ccx_decoders_isdb.c @@ -4,6 +4,16 @@ #include "utility.h" #include "limits.h" +#ifndef DISABLE_RUST + +extern int ccxr_isdb_set_global_time(struct lib_cc_decode *dec_ctx, uint64_t timestamp); +extern void ccxr_delete_isdb_decoder(void **isdb_ctx); +extern void *ccxr_init_isdb_decoder(void); +extern int ccxr_isdb_parse_data_group(void *codec_ctx, const uint8_t *buf, int size, struct cc_subtitle *sub); +extern int ccxr_isdbsub_decode(struct lib_cc_decode *dec_ctx, const uint8_t *buf, size_t buf_size, struct cc_subtitle *sub); + +#endif + // #define DEBUG // #define COMMAND_DEBUG @@ -281,6 +291,9 @@ typedef struct */ void delete_isdb_decoder(void **isdb_ctx) { +#ifndef DISABLE_RUST + ccxr_delete_isdb_decoder(isdb_ctx); +#else ISDBSubContext *ctx = *isdb_ctx; struct ISDBText *text = NULL; struct ISDBText *text1 = NULL; @@ -298,6 +311,7 @@ void delete_isdb_decoder(void **isdb_ctx) free(text); } freep(isdb_ctx); +#endif } static void init_layout(ISDBSubLayout *ls) @@ -312,6 +326,9 @@ static void init_layout(ISDBSubLayout *ls) void *init_isdb_decoder(void) { +#ifndef DISABLE_RUST + return ccxr_init_isdb_decoder(); +#else ISDBSubContext *ctx; ctx = malloc(sizeof(ISDBSubContext)); @@ -326,6 +343,7 @@ void *init_isdb_decoder(void) ctx->current_state.rollup_mode = 0; init_layout(&ctx->current_state.layout_state); return ctx; +#endif } /** @@ -1328,6 +1346,9 @@ static int parse_caption_statement_data(ISDBSubContext *ctx, int lang_id, const */ int isdb_parse_data_group(void *codec_ctx, const uint8_t *buf, struct cc_subtitle *sub) { +#ifndef DISABLE_RUST + return ccxr_isdb_parse_data_group(codec_ctx, buf, 0, sub); +#else ISDBSubContext *ctx = codec_ctx; const uint8_t *buf_pivot = buf; int id = (*buf >> 2); @@ -1393,10 +1414,14 @@ int isdb_parse_data_group(void *codec_ctx, const uint8_t *buf, struct cc_subtitl buf += 2; return buf - buf_pivot; +#endif } int isdbsub_decode(struct lib_cc_decode *dec_ctx, const uint8_t *buf, size_t buf_size, struct cc_subtitle *sub) { +#ifndef DISABLE_RUST + return ccxr_isdbsub_decode(dec_ctx, buf, buf_size, sub); +#else const uint8_t *header_end = NULL; int ret = 0; ISDBSubContext *ctx = dec_ctx->private_data; @@ -1421,10 +1446,16 @@ int isdbsub_decode(struct lib_cc_decode *dec_ctx, const uint8_t *buf, size_t buf return -1; return 1; +#endif } + int isdb_set_global_time(struct lib_cc_decode *dec_ctx, uint64_t timestamp) { +#ifndef DISABLE_RUST + return ccxr_isdb_set_global_time(dec_ctx, timestamp); +#else ISDBSubContext *ctx = dec_ctx->private_data; ctx->timestamp = timestamp; return CCX_OK; +#endif } diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 37aa15a8b..75a29f7fe 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -61,6 +61,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + [[package]] name = "approx" version = "0.5.1" @@ -144,12 +150,27 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + [[package]] name = "camino" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" +[[package]] +name = "cc" +version = "1.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" +dependencies = [ + "shlex", +] + [[package]] name = "ccx_rust" version = "0.1.0" @@ -159,12 +180,18 @@ dependencies = [ "clap", "env_logger", "iconv", + "lazy_static", "leptonica-sys", "lib_ccxr", + "libc", "log", + "nanomsg", + "nanomsg-sys", "num-integer", "palette", "pkg-config", + "prost", + "prost-types", "rsmpeg", "strum 0.25.0", "strum_macros 0.25.3", @@ -239,6 +266,15 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "colorchoice" version = "1.0.3" @@ -352,6 +388,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + [[package]] name = "glob" version = "0.3.2" @@ -610,7 +652,13 @@ dependencies = [ "bitflags 2.9.0", "crc32fast", "derive_more", + "lazy_static", + "libc", + "nanomsg", + "nanomsg-sys", "num_enum", + "prost", + "prost-types", "strum 0.26.3", "strum_macros 0.26.4", "thiserror", @@ -664,6 +712,28 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +[[package]] +name = "nanomsg" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617e0160fba522f8667df7bc79f3b4a74a0e3968d08023ebb3ce717a5f3bd3ac" +dependencies = [ + "libc", + "nanomsg-sys", +] + +[[package]] +name = "nanomsg-sys" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78aa3ccb6d007dfecb4f7070725c4b1670a87677babb6621cb0c8cce9cfdc004" +dependencies = [ + "cmake", + "gcc", + "libc", + "pkg-config", +] + [[package]] name = "nom" version = "7.1.3" @@ -850,6 +920,38 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn 2.0.99", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.39" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index b94861db2..3825ec178 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -27,7 +27,14 @@ time = "0.3.39" cfg-if = "1.0.0" num-integer = "0.1.46" lib_ccxr = { path = "lib_ccxr" } + url = "2.5.4" +libc = "0.2.169" +nanomsg = { version = "0.7.2" } +prost = "0.13.4" +prost-types = "0.13.4" +lazy_static = "1.5.0" +nanomsg-sys = "0.7.2" [build-dependencies] bindgen = "0.64.0" diff --git a/src/rust/lib_ccxr/Cargo.lock b/src/rust/lib_ccxr/Cargo.lock index 8ec94ecf1..d787dc251 100644 --- a/src/rust/lib_ccxr/Cargo.lock +++ b/src/rust/lib_ccxr/Cargo.lock @@ -2,18 +2,48 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "anyhow" +version = "1.0.97" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" + [[package]] name = "bitflags" version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" + +[[package]] +name = "cc" +version = "1.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" +dependencies = [ + "shlex", +] + [[package]] name = "cfg-if" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "cmake" +version = "0.1.54" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" +dependencies = [ + "cc", +] + [[package]] name = "convert_case" version = "0.4.0" @@ -62,6 +92,12 @@ dependencies = [ "syn", ] +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + [[package]] name = "equivalent" version = "1.0.2" @@ -77,6 +113,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "gcc" +version = "0.3.55" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" + [[package]] name = "hashbrown" version = "0.15.2" @@ -238,12 +280,27 @@ dependencies = [ "hashbrown", ] +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + [[package]] name = "lib_ccxr" version = "0.1.0" @@ -251,7 +308,13 @@ dependencies = [ "bitflags", "crc32fast", "derive_more", + "lazy_static", + "libc", + "nanomsg", + "nanomsg-sys", "num_enum", + "prost", + "prost-types", "strum", "strum_macros", "thiserror", @@ -259,6 +322,12 @@ dependencies = [ "url", ] +[[package]] +name = "libc" +version = "0.2.171" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c19937216e9d3aa9956d9bb8dfc0b0c8beb6058fc4f7a4dc4d850edf86a237d6" + [[package]] name = "litemap" version = "0.7.5" @@ -271,6 +340,28 @@ version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +[[package]] +name = "nanomsg" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617e0160fba522f8667df7bc79f3b4a74a0e3968d08023ebb3ce717a5f3bd3ac" +dependencies = [ + "libc", + "nanomsg-sys", +] + +[[package]] +name = "nanomsg-sys" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78aa3ccb6d007dfecb4f7070725c4b1670a87677babb6621cb0c8cce9cfdc004" +dependencies = [ + "cmake", + "gcc", + "libc", + "pkg-config", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -310,6 +401,12 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + [[package]] name = "powerfmt" version = "0.2.0" @@ -335,6 +432,38 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +dependencies = [ + "bytes", + "prost-derive", +] + +[[package]] +name = "prost-derive" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +dependencies = [ + "anyhow", + "itertools", + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "prost-types" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +dependencies = [ + "prost", +] + [[package]] name = "quote" version = "1.0.39" @@ -385,6 +514,12 @@ dependencies = [ "syn", ] +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + [[package]] name = "smallvec" version = "1.14.0" diff --git a/src/rust/lib_ccxr/Cargo.toml b/src/rust/lib_ccxr/Cargo.toml index 6162aef3a..9344aef7e 100644 --- a/src/rust/lib_ccxr/Cargo.toml +++ b/src/rust/lib_ccxr/Cargo.toml @@ -15,6 +15,12 @@ strum = "0.26.3" strum_macros = "0.26.4" crc32fast = "1.4.2" num_enum = "0.6.1" +libc = "0.2.169" +nanomsg = "0.7.2" +prost = "0.13.4" +prost-types = "0.13.4" +lazy_static = "1.5.0" +nanomsg-sys = "0.7.2" [features] default = [ @@ -30,3 +36,5 @@ enable_ffmpeg = [] debug_out = [] debug = [] with_libcurl = [] + + diff --git a/src/rust/lib_ccxr/src/isdbs/isdbd.rs b/src/rust/lib_ccxr/src/isdbs/isdbd.rs new file mode 100644 index 000000000..926dee158 --- /dev/null +++ b/src/rust/lib_ccxr/src/isdbs/isdbd.rs @@ -0,0 +1,1250 @@ +// command-line bindgen used + +use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_ulonglong, c_void}; + +#[allow(dead_code)] + +const CCX_DTVCC_MAX_SERVICES: usize = 63; +const CCX_DTVCC_MAX_WINDOWS: usize = 8; +const CCX_DTVCC_MAX_ROWS: usize = 15; +const CCX_DTVCC_SCREENGRID_COLUMNS: usize = 210; +const CCX_DTVCC_MAX_PACKET_LENGTH: usize = 128; +const CCX_DTVCC_SCREENGRID_ROWS: usize = 75; + +pub const CCX_MESSAGES_QUIET: i32 = 0; +pub const CCX_MESSAGES_STDOUT: i32 = 1; +pub const CCX_MESSAGES_STDERR: i32 = 2; + +const SORTBUF: usize = 2 * MAXBFRAMES + 1; +const MAXBFRAMES: usize = 50; + +#[repr(C)] +pub struct LibCcDecode { + pub cc_stats: [c_int; 4], + pub saw_caption_block: c_int, + pub processed_enough: c_int, // If 1, we have enough lines, time, etc. + + /* 608 contexts - note that this shouldn't be global, they should be + per program */ + pub context_cc608_field_1: *mut c_void, + pub context_cc608_field_2: *mut c_void, + + pub no_rollup: c_int, // If 1, write one line at a time + pub noscte20: c_int, + pub fix_padding: c_int, // Replace 0000 with 8080 in HDTV (needed for some cards) + pub write_format: CcxOutputFormat, // 0 = Raw, 1 = srt, 2 = SMI + pub extraction_start: CcxBoundaryTime, + pub extraction_end: CcxBoundaryTime, // Segment we actually process + pub subs_delay: i64, // ms to delay (or advance) subs + pub extract: c_int, // Extract 1st, 2nd or both fields + pub fullbin: c_int, // Disable pruning of padding cc blocks + pub dec_sub: CcSubtitle, + pub in_bufferdatatype: CcxBufferdataType, + pub hauppauge_mode: c_uint, // If 1, use PID=1003, process specially and so on + + pub frames_since_last_gop: c_int, + /* GOP-based timing */ + pub saw_gop_header: c_int, + /* Time info for timed-transcript */ + pub max_gop_length: c_int, // (Maximum) length of a group of pictures + pub last_gop_length: c_int, // Length of the previous group of pictures + pub total_pulldownfields: c_uint, + pub total_pulldownframes: c_uint, + pub program_number: c_int, + pub list: ListHead, + pub timing: *mut CcxCommonTimingCtx, + pub codec: CcxCodeType, + // Set to true if data is buffered + pub has_ccdata_buffered: c_int, + pub is_alloc: c_int, + + pub avc_ctx: *mut AvcCtx, + pub private_data: *mut c_void, + + /* General video information */ + pub current_hor_size: c_uint, + pub current_vert_size: c_uint, + pub current_aspect_ratio: c_uint, + pub current_frame_rate: c_uint, // Assume standard fps, 29.97 + + /* Required in es_function.c */ + pub no_bitstream_error: c_int, + pub saw_seqgoppic: c_int, + pub in_pic_data: c_int, + + pub current_progressive_sequence: c_uint, + pub current_pulldownfields: c_uint, + + pub temporal_reference: c_int, + pub picture_coding_type: CcxFrameType, + pub num_key_frames: c_uint, + pub picture_structure: c_uint, + pub repeat_first_field: c_uint, + pub progressive_frame: c_uint, + pub pulldownfields: c_uint, + /* Required in es_function.c and es_userdata.c */ + pub top_field_first: c_uint, // Needs to be global + + /* Stats. Modified in es_userdata.c */ + pub stat_numuserheaders: c_int, + pub stat_dvdccheaders: c_int, + pub stat_scte20ccheaders: c_int, + pub stat_replay5000headers: c_int, + pub stat_replay4000headers: c_int, + pub stat_dishheaders: c_int, + pub stat_hdtv: c_int, + pub stat_divicom: c_int, + pub false_pict_header: c_int, + + pub dtvcc: *mut DtvccCtx, + pub current_field: c_int, + // Analyse/use the picture information + pub maxtref: c_int, // Use to remember the temporal reference number + + pub cc_data_count: [c_int; SORTBUF], + // Store fts; + pub cc_fts: [i64; SORTBUF], + // Store HD CC packets + pub cc_data_pkts: [[u8; 10 * 31 * 3 + 1]; SORTBUF], // *10, because MP4 seems to have different limits + + // The sequence number of the current anchor frame. All currently read + // B-Frames belong to this I- or P-frame. + pub anchor_seq_number: c_int, + pub xds_ctx: *mut CcxDecodersXdsContext, + pub vbi_decoder: *mut CcxDecoderVbiCtx, + + pub writedata: Option< + extern "C" fn( + data: *const u8, + length: c_int, + private_data: *mut c_void, + sub: *mut CcSubtitle, + ) -> c_int, + >, + + // dvb subtitle related + pub ocr_quantmode: c_int, + pub prev: *mut LibCcDecode, +} + +// Define the necessary enums and structs +#[repr(C)] +pub enum CcxOutputFormat { + Raw = 0, + Srt = 1, + Sami = 2, + Transcript = 3, + Rcwt = 4, + Null = 5, + Smptett = 6, + Spupng = 7, + DvdRaw = 8, + Webvtt = 9, + SimpleXml = 10, + G608 = 11, + Curl = 12, + Ssa = 13, + Mcc = 14, + Scc = 15, + Ccd = 16, +} + +#[repr(C)] +pub struct CcxBoundaryTime { + pub hh: i32, + pub mm: i32, + pub ss: i32, + pub time_in_ms: i64, + pub set: i32, +} + +#[repr(C)] +pub struct CcSubtitle { + /** + * A generic data which contain data according to decoder + * @warn decoder cant output multiple types of data + */ + pub data: *mut c_void, + pub datatype: Subdatatype, + + /** number of data */ + pub nb_data: c_uint, + + /** type of subtitle */ + pub type_: Subtype, + + /** Encoding type of Text, must be ignored in case of subtype as bitmap or cc_screen*/ + pub enc_type: CcxEncodingType, + + /* set only when all the data is to be displayed at same time + * Unit is of time is ms + */ + pub start_time: i64, // Assuming LLONG is equivalent to i64 + pub end_time: i64, + + /* flags */ + pub flags: c_int, + + /* index of language table */ + pub lang_index: c_int, + + /** flag to tell that decoder has given output */ + pub got_output: c_int, + + pub mode: [u8; 5], + pub info: [u8; 4], + + /** Used for DVB end time in ms */ + pub time_out: c_int, + + pub next: *mut CcSubtitle, + pub prev: *mut CcSubtitle, +} + +#[repr(C)] +pub enum Subdatatype { + Generic = 0, + Dvb = 1, +} + +#[repr(C)] +pub enum Subtype { + Bitmap, + Cc608, + Text, + Raw, +} + +#[repr(C)] +#[derive(Copy, Clone)] +pub enum CcxEncodingType { + Unicode = 0, + Latin1 = 1, + Utf8 = 2, + Ascii = 3, +} + +#[repr(C)] +pub enum CcxBufferdataType { + Unknown = 0, + Pes = 1, + Raw = 2, + H264 = 3, + Hauppage = 4, + Teletext = 5, + PrivateMpeg2Cc = 6, + DvbSubtitle = 7, + IsdbSubtitle = 8, + RawType = 9, // Buffer where cc data contain 3 byte cc_valid ccdata 1 ccdata 2 + DvdSubtitle = 10, +} + +#[repr(C)] +pub enum CcxFrameType { + ResetOrUnknown = 0, + IFrame = 1, + PFrame = 2, + BFrame = 3, + DFrame = 4, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ListHead { + pub next: *mut ListHead, + pub prev: *mut ListHead, +} + +#[repr(C)] +pub struct CcxCommonTimingCtx { + pub pts_set: i32, // 0 = No, 1 = received, 2 = min_pts set + pub min_pts_adjusted: i32, // 0 = No, 1=Yes (don't adjust again) + pub current_pts: i64, // Assuming LLONG is equivalent to i64 + pub current_picture_coding_type: CcxFrameType, + pub current_tref: i32, // Store temporal reference of current frame + pub min_pts: i64, + pub max_pts: i64, + pub sync_pts: i64, + pub minimum_fts: i64, // No screen should start before this FTS + pub fts_now: i64, // Time stamp of current file (w/ fts_offset, w/o fts_global) + pub fts_offset: i64, // Time before first sync_pts + pub fts_fc_offset: i64, // Time before first GOP + pub fts_max: i64, // Remember the maximum fts that we saw in current file + pub fts_global: i64, // Duration of previous files (-ve mode) + pub sync_pts2fts_set: i32, // 0 = No, 1 = Yes + pub sync_pts2fts_fts: i64, + pub sync_pts2fts_pts: i64, + pub pts_reset: i32, // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev +} + +#[repr(C)] +pub enum CcxCodeType { + Any = 0, + Teletext = 1, + Dvb = 2, + IsdbCc = 3, + AtscCc = 4, + None = 5, +} + +#[repr(C)] +pub struct AvcCtx { + pub cc_count: c_uchar, + // buffer to hold cc data + pub cc_data: *mut c_uchar, + pub cc_databufsize: c_long, + pub cc_buffer_saved: c_int, // Was the CC buffer saved after it was last updated? + + pub got_seq_para: c_int, + pub nal_ref_idc: c_uint, + pub seq_parameter_set_id: i64, // Assuming LLONG is equivalent to i64 + pub log2_max_frame_num: c_int, + pub pic_order_cnt_type: c_int, + pub log2_max_pic_order_cnt_lsb: c_int, + pub frame_mbs_only_flag: c_int, + + // Use and throw stats for debug, remove this ugliness soon + pub num_nal_unit_type_7: c_long, + pub num_vcl_hrd: c_long, + pub num_nal_hrd: c_long, + pub num_jump_in_frames: c_long, + pub num_unexpected_sei_length: c_long, + + pub ccblocks_in_avc_total: c_int, + pub ccblocks_in_avc_lost: c_int, + + pub frame_num: i64, + pub lastframe_num: i64, + pub currref: c_int, + pub maxidx: c_int, + pub lastmaxidx: c_int, + + // Used to find tref zero in PTS mode + pub minidx: c_int, + pub lastminidx: c_int, + + // Used to remember the max temporal reference number (poc mode) + pub maxtref: c_int, + pub last_gop_maxtref: c_int, + + // Used for PTS ordering of CC blocks + pub currefpts: i64, + pub last_pic_order_cnt_lsb: i64, + pub last_slice_pts: i64, +} + +#[repr(C)] +pub struct DtvccCtx { + pub is_active: c_int, + pub active_services_count: c_int, + pub services_active: [c_int; CCX_DTVCC_MAX_SERVICES], // 0 - inactive, 1 - active + pub report_enabled: c_int, + pub report: *mut CcxDecoderDtvccReport, + pub decoders: [DtvccServiceDecoder; CCX_DTVCC_MAX_SERVICES], + pub current_packet: [c_uchar; CCX_DTVCC_MAX_PACKET_LENGTH], + pub current_packet_length: c_int, + pub is_current_packet_header_parsed: c_int, + pub last_sequence: c_int, + pub encoder: *mut c_void, // we can't include header, so keeping it this way + pub no_rollup: c_int, + pub timing: *mut CcxCommonTimingCtx, +} + +#[repr(C)] +pub struct CcxDecoderDtvccReport { + pub reset_count: c_int, + pub services: [c_uint; CCX_DTVCC_MAX_SERVICES], +} + +#[repr(C)] +pub struct DtvccServiceDecoder { + pub windows: [DtvccWindow; CCX_DTVCC_MAX_WINDOWS], + pub current_window: c_int, + pub tv: *mut DtvccTvScreen, + pub cc_count: c_int, +} + +#[repr(C)] +pub struct DtvccWindow { + pub is_defined: c_int, + pub number: c_int, + pub priority: c_int, + pub col_lock: c_int, + pub row_lock: c_int, + pub visible: c_int, + pub anchor_vertical: c_int, + pub relative_pos: c_int, + pub anchor_horizontal: c_int, + pub row_count: c_int, + pub anchor_point: c_int, + pub col_count: c_int, + pub pen_style: c_int, + pub win_style: c_int, + pub commands: [c_uchar; 6], // Commands used to create this window + pub attribs: DtvccWindowAttribs, + pub pen_row: c_int, + pub pen_column: c_int, + pub rows: [*mut DtvccSymbol; CCX_DTVCC_MAX_ROWS], + pub pen_colors: [[DtvccPenColor; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_MAX_ROWS], + pub pen_attribs: [[DtvccPenAttribs; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_MAX_ROWS], + pub pen_color_pattern: DtvccPenColor, + pub pen_attribs_pattern: DtvccPenAttribs, + pub memory_reserved: c_int, + pub is_empty: c_int, + pub time_ms_show: i64, // Assuming LLONG is equivalent to i64 + pub time_ms_hide: i64, +} + +#[repr(C)] +pub struct DtvccWindowAttribs { + pub justify: c_int, + pub print_direction: c_int, + pub scroll_direction: c_int, + pub word_wrap: c_int, + pub display_effect: c_int, + pub effect_direction: c_int, + pub effect_speed: c_int, + pub fill_color: c_int, + pub fill_opacity: c_int, + pub border_type: c_int, + pub border_color: c_int, +} + +#[repr(C)] +pub struct DtvccSymbol { + pub sym: u16, // symbol itself, at least 16 bit + pub init: c_uchar, // initialized or not. could be 0 or 1 +} + +#[repr(C)] +pub struct DtvccPenColor { + pub fg_color: c_int, + pub fg_opacity: c_int, + pub bg_color: c_int, + pub bg_opacity: c_int, + pub edge_color: c_int, +} + +#[repr(C)] +pub struct DtvccPenAttribs { + pub pen_size: c_int, + pub offset: c_int, + pub text_tag: c_int, + pub font_tag: c_int, + pub edge_type: c_int, + pub underline: c_int, + pub italic: c_int, +} + +#[repr(C)] +pub struct DtvccTvScreen { + pub chars: [[DtvccSymbol; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], + pub pen_colors: [[DtvccPenColor; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], + pub pen_attribs: [[DtvccPenAttribs; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], + pub time_ms_show: i64, + pub time_ms_hide: i64, + pub cc_count: c_uint, + pub service_number: c_int, + pub old_cc_time_end: c_int, +} + +const NUM_BYTES_PER_PACKET: usize = 35; +const NUM_XDS_BUFFERS: usize = 9; + +#[repr(C)] +pub struct CcxDecodersXdsContext { + // Program Identification Number (Start Time) for current program + pub current_xds_min: c_int, + pub current_xds_hour: c_int, + pub current_xds_date: c_int, + pub current_xds_month: c_int, + pub current_program_type_reported: c_int, // No. + pub xds_start_time_shown: c_int, + pub xds_program_length_shown: c_int, + pub xds_program_description: [[c_uchar; 33]; 8], + + pub current_xds_network_name: [c_uchar; 33], + pub current_xds_program_name: [c_uchar; 33], + pub current_xds_call_letters: [c_uchar; 7], + pub current_xds_program_type: [c_uchar; 33], + + pub xds_buffers: [XdsBuffer; NUM_XDS_BUFFERS], + pub cur_xds_buffer_idx: c_int, + pub cur_xds_packet_class: c_int, + pub cur_xds_payload: *mut c_uchar, + pub cur_xds_payload_length: c_int, + pub cur_xds_packet_type: c_int, + pub timing: *mut CcxCommonTimingCtx, + + pub current_ar_start: c_uint, + pub current_ar_end: c_uint, + + pub xds_write_to_file: c_int, // Set to 1 if XDS data is to be written to file +} + +#[repr(C)] +pub struct XdsBuffer { + pub in_use: c_uint, + pub xds_class: c_int, + pub xds_type: c_int, + pub bytes: [c_uchar; NUM_BYTES_PER_PACKET], // Class + type (repeated for convenience) + data + zero + pub used_bytes: c_uchar, +} + +#[repr(C)] +pub struct CcxDecoderVbiCtx { + pub vbi_decoder_inited: c_int, + pub zvbi_decoder: VbiRawDecoder, + // vbi3_raw_decoder zvbi_decoder; + // #ifdef VBI_DEBUG + // pub vbi_debug_dump: *mut FILE, + // #endif +} + +#[repr(C)] +pub struct VbiRawDecoder { + /* Sampling parameters */ + /** + * Either 525 (M/NTSC, M/PAL) or 625 (PAL, SECAM), describing the + * scan line system all line numbers refer to. + */ + pub scanning: c_int, + /** + * Format of the raw vbi data. + */ + pub sampling_format: VbiPixfmt, + /** + * Sampling rate in Hz, the number of samples or pixels + * captured per second. + */ + pub sampling_rate: c_int, // Hz + /** + * Number of samples or pixels captured per scan line, + * in bytes. This determines the raw vbi image width and you + * want it large enough to cover all data transmitted in the line (with + * headroom). + */ + pub bytes_per_line: c_int, + /** + * The distance from 0H (leading edge hsync, half amplitude point) + * to the first sample (pixel) captured, in samples (pixels). You want + * an offset small enough not to miss the start of the data + * transmitted. + */ + pub offset: c_int, // 0H, samples + /** + * First scan line to be captured, first and second field + * respectively, according to the ITU-R line numbering scheme + * (see vbi_sliced). Set to zero if the exact line number isn't + * known. + */ + pub start: [c_int; 2], // ITU-R numbering + /** + * Number of scan lines captured, first and second + * field respectively. This can be zero if only data from one + * field is required. The sum @a count[0] + @a count[1] determines the + * raw vbi image height. + */ + pub count: [c_int; 2], // field lines + /** + * In the raw vbi image, normally all lines of the second + * field are supposed to follow all lines of the first field. When + * this flag is set, the scan lines of first and second field + * will be interleaved in memory. This implies @a count[0] and @a count[1] + * are equal. + */ + pub interlaced: c_int, + /** + * Fields must be stored in temporal order, i. e. as the + * lines have been captured. It is assumed that the first field is + * also stored first in memory, however if the hardware cannot reliable + * distinguish fields this flag shall be cleared, which disables + * decoding of data services depending on the field number. + */ + pub synchronous: c_int, + + pub services: c_uint, + pub num_jobs: c_int, + + pub pattern: *mut i8, + pub jobs: [VbiRawDecoderJob; 8], +} + +#[repr(C)] +pub struct VbiRawDecoderJob { + pub id: c_uint, + pub offset: c_int, + pub slicer: VbiBitSlicer, +} + +#[repr(C)] +pub enum VbiPixfmt { + Yuv420 = 1, + Yuyv, + Yvyu, + Uyvy, + Vyuy, + Pal8, + Rgba32Le = 32, + Rgba32Be, + Bgra32Le, + Bgra32Be, + Abgr32Be, /* = 32, // synonyms */ + Abgr32Le, + Argb32Be, + Argb32Le, + Rgb24, + Bgr24, + Rgb16Le, + Rgb16Be, + Bgr16Le, + Bgr16Be, + Rgba15Le, + Rgba15Be, + Bgra15Le, + Bgra15Be, + Argb15Le, + Argb15Be, + Abgr15Le, + Abgr15Be, +} + +#[repr(C)] +pub struct VbiBitSlicer { + pub func: Option< + extern "C" fn(slicer: *mut VbiBitSlicer, raw: *mut c_uchar, buf: *mut c_uchar) -> c_int, + >, + pub cri: c_uint, + pub cri_mask: c_uint, + pub thresh: c_int, + pub cri_bytes: c_int, + pub cri_rate: c_int, + pub oversampling_rate: c_int, + pub phase_shift: c_int, + pub step: c_int, + pub frc: c_uint, + pub frc_bits: c_int, + pub payload: c_int, + pub endian: c_int, + pub skip: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ISDBSubContext { + pub nb_char: c_int, + pub nb_line: c_int, + pub timestamp: u64, + pub prev_timestamp: u64, + pub text_list_head: ListHead, + pub buffered_text: ListHead, + pub current_state: ISDBSubState, + pub tmd: IsdbTmd, + pub nb_lang: c_int, + pub offset_time: OffsetTime, + pub dmf: c_uchar, + pub dc: c_uchar, + pub cfg_no_rollup: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ISDBSubState { + pub auto_display: c_int, // bool + pub rollup_mode: c_int, // bool + pub need_init: c_uchar, // bool + pub clut_high_idx: c_uchar, + pub fg_color: c_uint, + pub bg_color: c_uint, + pub hfg_color: c_uint, + pub hbg_color: c_uint, + pub mat_color: c_uint, + pub raster_color: c_uint, + pub layout_state: ISDBSubLayout, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ISDBSubLayout { + pub format: WritingFormat, + pub display_area: DispArea, + pub font_size: c_int, + pub font_scale: FScale, + pub cell_spacing: Spacing, + pub cursor_pos: ISDBPos, + pub ccc: IsdbCCComposition, + pub acps: [c_int; 2], +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct DispArea { + pub x: c_int, + pub y: c_int, + pub w: c_int, + pub h: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct FScale { + pub fscx: c_int, + pub fscy: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct Spacing { + pub col: c_int, + pub row: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ISDBPos { + pub x: c_int, + pub y: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct OffsetTime { + pub hour: c_int, + pub min: c_int, + pub sec: c_int, + pub milli: c_int, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug)] +pub enum WritingFormat { + HorizontalStdDensity = 0, + VerticalStdDensity = 1, + HorizontalHighDensity = 2, + VerticalHighDensity = 3, + HorizontalWesternLang = 4, + Horizontal1920x1080 = 5, + Vertical1920x1080 = 6, + Horizontal960x540 = 7, + Vertical960x540 = 8, + Horizontal720x480 = 9, + Vertical720x480 = 10, + Horizontal1280x720 = 11, + Vertical1280x720 = 12, + HorizontalCustom = 100, + None, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub enum IsdbCCComposition { + None = 0, + And = 2, + Or = 3, + Xor = 4, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub enum IsdbTmd { + Free = 0, + RealTime = 0x1, + OffsetTime = 0x2, +} + +#[repr(C)] +pub enum FontSize { + SmallFontSize, + MiddleFontSize, + StandardFontSize, +} + +#[repr(C)] +pub enum CsiCommand { + Gsm = 0x42, + Swf = 0x53, + Ccc = 0x54, + Sdf = 0x56, + Ssm = 0x57, + Shs = 0x58, + Svs = 0x59, + Pld = 0x5B, + Plu = 0x5C, + Gaa = 0x5D, + Src = 0x5E, + Sdp = 0x5F, + Acps = 0x61, + Tcc = 0x62, + Orn = 0x63, + Mdf = 0x64, + Cfs = 0x65, + Xcs = 0x66, + Pra = 0x68, + Acs = 0x69, + Rcs = 0x6E, + Scs = 0x6F, +} + +#[repr(C)] +pub enum Color { + CcxIsdbBlack, + FiRed, + FiGreen, + FiYellow, + FiBlue, + FiMagenta, + FiCyan, + FiWhite, + CcxIsdbTransparent, + HiRed, + HiGreen, + HiYellow, + HiBlue, + HiMagenta, + HiCyan, + HiWhite, +} + +#[no_mangle] +pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> u32 { + ((255 - a as u32) << 24) | ((b as u32) << 16) | ((g as u32) << 8) | (r as u32) +} + +type Rgba = u32; +pub const ZATA: u32 = 0; +pub const DEFAULT_CLUT: [Rgba; 128] = [ + // 0-7 + rgba(0, 0, 0, 255), + rgba(255, 0, 0, 255), + rgba(0, 255, 0, 255), + rgba(255, 255, 0, 255), + rgba(0, 0, 255, 255), + rgba(255, 0, 255, 255), + rgba(0, 255, 255, 255), + rgba(255, 255, 255, 255), + // 8-15 + rgba(0, 0, 0, 0), + rgba(170, 0, 0, 255), + rgba(0, 170, 0, 255), + rgba(170, 170, 0, 255), + rgba(0, 0, 170, 255), + rgba(170, 0, 170, 255), + rgba(0, 170, 170, 255), + rgba(170, 170, 170, 255), + // 16-23 + rgba(0, 0, 85, 255), + rgba(0, 85, 0, 255), + rgba(0, 85, 85, 255), + rgba(0, 85, 170, 255), + rgba(0, 85, 255, 255), + rgba(0, 170, 85, 255), + rgba(0, 170, 255, 255), + rgba(0, 255, 85, 255), + // 24-31 + rgba(0, 255, 170, 255), + rgba(85, 0, 0, 255), + rgba(85, 0, 85, 255), + rgba(85, 0, 170, 255), + rgba(85, 0, 255, 255), + rgba(85, 85, 0, 255), + rgba(85, 85, 85, 255), + rgba(85, 85, 170, 255), + // 32-39 + rgba(85, 85, 255, 255), + rgba(85, 170, 0, 255), + rgba(85, 170, 85, 255), + rgba(85, 170, 170, 255), + rgba(85, 170, 255, 255), + rgba(85, 255, 0, 255), + rgba(85, 255, 85, 255), + rgba(85, 255, 170, 255), + // 40-47 + rgba(85, 255, 255, 255), + rgba(170, 0, 85, 255), + rgba(170, 0, 255, 255), + rgba(170, 85, 0, 255), + rgba(170, 85, 85, 255), + rgba(170, 85, 170, 255), + rgba(170, 85, 255, 255), + rgba(170, 170, 85, 255), + // 48-55 + rgba(170, 170, 255, 255), + rgba(170, 255, 0, 255), + rgba(170, 255, 85, 255), + rgba(170, 255, 170, 255), + rgba(170, 255, 255, 255), + rgba(255, 0, 85, 255), + rgba(255, 0, 170, 255), + rgba(255, 85, 0, 255), + // 56-63 + rgba(255, 85, 85, 255), + rgba(255, 85, 170, 255), + rgba(255, 85, 255, 255), + rgba(255, 170, 0, 255), + rgba(255, 170, 85, 255), + rgba(255, 170, 170, 255), + rgba(255, 170, 255, 255), + rgba(255, 255, 85, 255), + // 64 + rgba(255, 255, 170, 255), + // 65-127 are calculated later. + // Initialize remaining elements to 0 + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, +]; + +#[repr(C)] +pub struct ISDBText { + pub buf: *mut c_char, + pub len: usize, + pub used: usize, + pub pos: ISDBPos, + pub txt_tail: usize, // tail of the text, excluding trailing control sequences. + pub timestamp: c_ulonglong, // Time Stamp when first string is received + pub refcount: c_int, + pub list: ListHead, +} + +#[repr(i32)] +pub enum CcxStreamMode { + ElementaryOrNotFound = 0, + Transport = 1, + Program = 2, + Asf = 3, + McPoodlesRaw = 4, + Rcwt = 5, // Raw Captions With Time, not used yet. + Myth = 6, // Use the myth loop + Mp4 = 7, // MP4, ISO- + Wtv = 9, + Gxf = 11, + Mkv = 12, + Mxf = 13, + Autodetect = 16, +} + +#[repr(i32)] +pub enum CcxOutputDateFormat { + None = 0, + Hhmmss = 1, + Seconds = 2, + Date = 3, + Hhmmssms = 4, // HH:MM:SS,MILIS (.srt style) +} + +pub struct CcxEncodersTranscriptFormat { + show_start_time: i32, // Show start and/or end time. + show_end_time: i32, // Show start and/or end time. + show_mode: i32, // Show which mode if available (E.G.: POP, RU1, ...) + show_cc: i32, // Show which CC channel has been captured. + relative_timestamp: i32, // Timestamps relative to start of sample or in UTC? + xds: i32, // Show XDS or not + use_colors: i32, // Add colors or no colors + is_final: i32, // Used to determine if these parameters should be changed afterwards. +} + +#[repr(i32)] +pub enum CcxDataSource { + File = 0, + Stdin = 1, + Network = 2, + Tcp = 3, +} + +#[repr(C)] +pub struct CcxSOptions { + pub extract: i32, + pub no_rollup: i32, + pub noscte20: i32, + pub webvtt_create_css: i32, + pub cc_channel: i32, + pub buffer_input: i32, + pub nofontcolor: i32, + pub nohtmlescape: i32, + pub notypesetting: i32, + pub extraction_start: CcxBoundaryTime, + pub extraction_end: CcxBoundaryTime, + pub print_file_reports: i32, + pub settings_608: CcxDecoder608Settings, + pub settings_dtvcc: CcxDecoderDtvccSettings, + pub is_608_enabled: i32, + pub is_708_enabled: i32, + pub millis_separator: c_char, + pub binary_concat: i32, + pub use_gop_as_pts: i32, + pub fix_padding: i32, + pub gui_mode_reports: i32, + pub no_progress_bar: i32, + pub sentence_cap_file: *mut c_char, + pub live_stream: i32, + pub filter_profanity_file: *mut c_char, + pub messages_target: i32, + pub timestamp_map: i32, + pub dolevdist: i32, + pub levdistmincnt: i32, + pub levdistmaxpct: i32, + pub investigate_packets: i32, + pub fullbin: i32, + pub nosync: i32, + pub hauppauge_mode: u32, + pub wtvconvertfix: i32, + pub wtvmpeg2: i32, + pub auto_myth: i32, + pub mp4vidtrack: u32, + pub extract_chapters: i32, + pub usepicorder: i32, + pub xmltv: i32, + pub xmltvliveinterval: i32, + pub xmltvoutputinterval: i32, + pub xmltvonlycurrent: i32, + pub keep_output_closed: i32, + pub force_flush: i32, + pub append_mode: i32, + pub ucla: i32, + pub tickertext: i32, + pub hardsubx: i32, + pub hardsubx_and_common: i32, + pub dvblang: *mut c_char, + pub ocrlang: *const c_char, + pub ocr_oem: i32, + pub psm: i32, + pub ocr_quantmode: i32, + pub mkvlang: *mut c_char, + pub analyze_video_stream: i32, + pub hardsubx_ocr_mode: i32, + pub hardsubx_subcolor: i32, + pub hardsubx_min_sub_duration: f32, + pub hardsubx_detect_italics: i32, + pub hardsubx_conf_thresh: f32, + pub hardsubx_hue: f32, + pub hardsubx_lum_thresh: f32, + pub transcript_settings: CcxEncodersTranscriptFormat, + pub date_format: CcxOutputDateFormat, + pub send_to_srv: u32, + pub write_format: CcxOutputFormat, + pub write_format_rewritten: i32, + pub use_ass_instead_of_ssa: i32, + pub use_webvtt_styling: i32, + pub debug_mask: i64, + pub debug_mask_on_debug: i64, + pub udpsrc: *mut c_char, + pub udpaddr: *mut c_char, + pub udpport: u32, + pub tcpport: *mut c_char, + pub tcp_password: *mut c_char, + pub tcp_desc: *mut c_char, + pub srv_addr: *mut c_char, + pub srv_port: *mut c_char, + pub noautotimeref: i32, + pub input_source: CcxDatasource, + pub output_filename: *mut c_char, + pub inputfile: *mut *mut c_char, + pub num_input_files: i32, + pub demux_cfg: DemuxerCfg, + pub enc_cfg: EncoderCfg, + pub subs_delay: i64, + pub cc_to_stdout: i32, + pub pes_header_to_stdout: i32, + pub ignore_pts_jumps: i32, + pub multiprogram: i32, + pub out_interval: i32, + pub segment_on_key_frames_only: i32, +} + +#[repr(C)] +pub struct CcxDecoder608Settings { + pub direct_rollup: i32, + pub force_rollup: i32, + pub no_rollup: i32, + pub default_color: CcxDecoder608ColorCode, + pub screens_to_process: i32, + pub report: *mut CcxDecoder608Report, +} + +#[repr(C)] +pub enum CcxDecoder608ColorCode { + ColWhite = 0, + ColGreen = 1, + ColBlue = 2, + ColCyan = 3, + ColRed = 4, + ColYellow = 5, + ColMagenta = 6, + ColUserdefined = 7, + ColBlack = 8, + ColTransparent = 9, + ColMax, +} + +#[repr(C)] +pub struct CcxDecoder608Report { + pub xds: u8, + pub cc_channels: [u8; 4], +} + +#[repr(C)] +pub struct CcxDecoderDtvccSettings { + pub enabled: i32, + pub print_file_reports: i32, + pub no_rollup: i32, + pub report: *mut CcxDecoderDtvccReport, + pub active_services_count: i32, + pub services_enabled: [i32; CCX_DTVCC_MAX_SERVICES], + pub timing: *mut CcxCommonTimingCtx, +} + +#[repr(C)] +pub enum CcxDatasource { + File = 0, + Stdin = 1, + Network = 2, + Tcp = 3, +} + +#[repr(C)] +pub struct DemuxerCfg { + pub m2ts: i32, + pub auto_stream: CcxStreamModeEnum, + pub codec: CcxCodeType, + pub nocodec: CcxCodeType, + pub ts_autoprogram: u32, + pub ts_allprogram: u32, + pub ts_cappids: [u32; 128], + pub nb_ts_cappid: i32, + pub ts_forced_cappid: u32, + pub ts_forced_program: i32, + pub ts_forced_program_selected: u32, + pub ts_datastreamtype: i32, + pub ts_forced_streamtype: u32, +} + +#[repr(C)] +pub struct EncoderCfg { + pub extract: i32, + pub dtvcc_extract: i32, + pub gui_mode_reports: i32, + pub output_filename: *mut c_char, + pub write_format: CcxOutputFormat, + pub keep_output_closed: i32, + pub force_flush: i32, + pub append_mode: i32, + pub ucla: i32, + pub encoding: CcxEncodingType, + pub date_format: CcxOutputDateFormat, + pub millis_separator: c_char, + pub autodash: i32, + pub trim_subs: i32, + pub sentence_cap: i32, + pub splitbysentence: i32, + pub filter_profanity: i32, + pub with_semaphore: i32, + pub start_credits_text: *mut c_char, + pub end_credits_text: *mut c_char, + pub startcreditsnotbefore: CcxBoundaryTime, + pub startcreditsnotafter: CcxBoundaryTime, + pub startcreditsforatleast: CcxBoundaryTime, + pub startcreditsforatmost: CcxBoundaryTime, + pub endcreditsforatleast: CcxBoundaryTime, + pub endcreditsforatmost: CcxBoundaryTime, + pub transcript_settings: CcxEncodersTranscriptFormat, + pub send_to_srv: u32, + pub no_bom: i32, + pub first_input_file: *mut c_char, + pub multiple_files: i32, + pub no_font_color: i32, + pub no_type_setting: i32, + pub cc_to_stdout: i32, + pub line_terminator_lf: i32, + pub subs_delay: i64, + pub program_number: i32, + pub in_format: u8, + pub nospupngocr: i32, + pub force_dropframe: i32, + pub render_font: *mut c_char, + pub render_font_italics: *mut c_char, + pub services_enabled: [i32; CCX_DTVCC_MAX_SERVICES], + pub services_charsets: *mut *mut c_char, + pub all_services_charset: *mut c_char, + pub extract_only_708: i32, +} + +#[repr(C)] +pub enum CcxStreamModeEnum { + ElementaryOrNotFound = 0, + Transport = 1, + Program = 2, + Asf = 3, + McPoodlesRaw = 4, + Rcwt = 5, + Myth = 6, + Mp4 = 7, + Wtv = 9, + Gxf = 11, + Mkv = 12, + Mxf = 13, + Autodetect = 16, +} + +pub struct CcxOptions { + pub messages_target: i32, +} diff --git a/src/rust/lib_ccxr/src/isdbs/isdbs.rs b/src/rust/lib_ccxr/src/isdbs/isdbs.rs new file mode 100644 index 000000000..14791bd94 --- /dev/null +++ b/src/rust/lib_ccxr/src/isdbs/isdbs.rs @@ -0,0 +1,1204 @@ +extern crate libc; +use crate::isdbs::*; +use isdbd::*; + +use std::alloc::{alloc, realloc, Layout}; + +use std::io::{self, Write}; +use std::os::raw::{c_char, c_void}; +use std::ptr; + +#[allow(unused_assignments)] +#[allow(dead_code)] +#[allow(unused_comparisons)] +#[no_mangle] +pub fn is_horizontal_layout(format: WritingFormat) -> bool { + matches!( + format, + WritingFormat::HorizontalStdDensity + | WritingFormat::HorizontalHighDensity + | WritingFormat::HorizontalWesternLang + | WritingFormat::Horizontal1920x1080 + | WritingFormat::Horizontal960x540 + | WritingFormat::Horizontal720x480 + | WritingFormat::Horizontal1280x720 + | WritingFormat::HorizontalCustom + ) +} + +#[no_mangle] +pub fn layout_get_width(format: WritingFormat) -> u32 { + match format { + WritingFormat::Horizontal960x540 | WritingFormat::Vertical960x540 => 960, + _ => 720, + } +} + +#[no_mangle] +pub fn layout_get_height(format: WritingFormat) -> u32 { + match format { + WritingFormat::Horizontal960x540 | WritingFormat::Vertical960x540 => 540, + _ => 480, + } +} + +pub fn isdb_set_global_time(ctx: &mut ISDBSubContext, timestamp: u64) { + ctx.timestamp = timestamp; +} + +pub fn init_layout(ls: *mut ISDBSubLayout) { + unsafe { + (*ls).font_size = 36; + (*ls).display_area.x = 0; + (*ls).display_area.y = 0; + (*ls).font_scale.fscx = 100; + (*ls).font_scale.fscy = 100; + } +} + +pub fn init_list_head(ptr: *mut ListHead) { + unsafe { + (*ptr).next = ptr; + (*ptr).prev = ptr; + } +} + +extern "C" { + pub fn free(ptr: *mut c_void); +} + +pub unsafe fn freep(arg: *mut *mut c_void) { + if !arg.is_null() && !(*arg).is_null() { + free(*arg); + *arg = ptr::null_mut(); + } +} + +pub const LIST_POISON1: *mut ListHead = 0x001 as *mut ListHead; +pub const LIST_POISON2: *mut ListHead = 0x002 as *mut ListHead; + +#[no_mangle] +pub unsafe fn __list_del(prev: *mut ListHead, next: *mut ListHead) { + (*next).prev = prev; + (*prev).next = next; +} + +#[no_mangle] +pub unsafe fn list_del(entry: *mut ListHead) { + __list_del((*entry).prev, (*entry).next); + (*entry).next = LIST_POISON1; + (*entry).prev = LIST_POISON2; +} + +pub unsafe fn list_for_each_entry_safe( + mut pos: *mut ISDBText, + mut n: *mut ISDBText, + head: *mut ListHead, + member: *const u8, +) { + pos = list_entry((*head).next, member); + n = list_entry((*pos).list.next, member); + while &(*pos).list as *const ListHead != head { + // Your code to process each entry goes here + pos = n; + n = list_entry((*n).list.next, member); + } +} + +pub unsafe fn list_entry(ptr: *mut ListHead, member: *const u8) -> *mut ISDBText { + container_of(ptr, member) +} + +pub unsafe fn container_of(ptr: *mut ListHead, member: *const u8) -> *mut ISDBText { + (ptr as *mut u8).offset(-(ccx_offsetof(member) as isize)) as *mut ISDBText +} + +pub unsafe fn ccx_offsetof(_member: *const u8) -> usize { + let offset = std::mem::offset_of!(ISDBText, list); + offset +} + +pub fn delete_isdb_decoder(ctx: *mut ISDBSubContext) { + unsafe { + let mut text: *mut ISDBText = ptr::null_mut(); + let mut text1: *mut ISDBText = ptr::null_mut(); + + list_for_each_entry_safe(text, text1, &mut (*ctx).text_list_head, b"list\0".as_ptr()); + while !text.is_null() { + list_del(&mut (*text).list); + free((*text).buf as *mut c_void); + free(text as *mut c_void); + list_for_each_entry_safe(text, text1, &mut (*ctx).text_list_head, b"list\0".as_ptr()); + } + + list_for_each_entry_safe(text, text1, &mut (*ctx).buffered_text, b"list\0".as_ptr()); + while !text.is_null() { + list_del(&mut (*text).list); + free((*text).buf as *mut c_void); + free(text as *mut c_void); + list_for_each_entry_safe(text, text1, &mut (*ctx).buffered_text, b"list\0".as_ptr()); + } + } +} + +// this is in case DEBUG is not defined, +// which is the case in the C code +#[no_mangle] +pub fn isdb_log(args: std::fmt::Arguments) { + // nothing said, nothing done +} + +#[no_mangle] +pub fn allocate_text_node(_ls: &ISDBSubLayout) -> *mut ISDBText { + unsafe { + let text_layout = Layout::new::(); + let text_ptr = alloc(text_layout) as *mut ISDBText; + if text_ptr.is_null() { + return ptr::null_mut(); + } + + (*text_ptr).used = 0; + let buf_layout = Layout::from_size_align(128, 1).unwrap(); + let buf_ptr = alloc(buf_layout) as *mut u8; + if buf_ptr.is_null() { + return ptr::null_mut(); + } + + (*text_ptr).buf = buf_ptr as *mut i8; + (*text_ptr).len = 128; + *(*text_ptr).buf = 0; + text_ptr + } +} + +pub fn reserve_buf(text: &mut ISDBText, len: usize) -> i32 { + const CCX_OK: i32 = 0; + const CCX_ENOMEM: i32 = 1; + + if text.len >= text.used + len { + return CCX_OK; + } + + // alloc always in 128 ka multiple + let blen = ((text.used + len + 127) >> 7) << 7; + unsafe { + let layout = Layout::from_size_align(text.len, 1).unwrap(); + let new_ptr = realloc(text.buf as *mut u8, layout, blen) as *mut u8; + if new_ptr.is_null() { + isdb_log(format_args!("ISDB: out of memory for ctx->text.buf\n")); + return CCX_ENOMEM; + } + text.buf = new_ptr as *mut i8; + } + text.len = blen; + isdb_log(format_args!("expanded ctx->text({})\n", blen)); + CCX_OK +} + +#[no_mangle] +pub unsafe fn __list_add(elem: *mut ListHead, prev: *mut ListHead, next: *mut ListHead) { + (*next).prev = elem; + (*elem).next = next; + (*elem).prev = prev; + (*prev).next = elem; +} + +#[no_mangle] +pub unsafe fn list_add_tail(elem: *mut ListHead, head: *mut ListHead) { + __list_add(elem, (*head).prev, head); +} + +pub fn list_for_each_entry(head: *mut ListHead, member: usize, mut func: F) +where + F: FnMut(*mut ISDBText), +{ + unsafe { + let member_ptr = member.to_string().as_ptr(); + let mut pos = list_entry((*head).next, member_ptr); + while &(*pos).list as *const _ != head { + func(pos); + pos = list_entry((*pos).list.next, member_ptr); + } + } +} + +pub fn list_add(elem: &mut ListHead, head: &mut ListHead) { + unsafe { + __list_add(elem, head, (*head).next); + } +} + +pub fn append_char(ctx: &mut ISDBSubContext, ch: char) -> i32 { + let ls = &mut ctx.current_state.layout_state; + let mut text: *mut ISDBText = std::ptr::null_mut(); + let cur_lpos; + let csp; + + if is_horizontal_layout(ls.format) { + cur_lpos = ls.cursor_pos.x; + csp = ls.font_size * ls.font_scale.fscx / 100; + } else { + cur_lpos = ls.cursor_pos.y; + csp = ls.font_size * ls.font_scale.fscy / 100; + } + + unsafe { + list_for_each_entry( + &mut ctx.text_list_head as *mut ListHead, + std::mem::offset_of!(ISDBText, list), + |t| { + text = t; + let text_lpos = if is_horizontal_layout(ls.format) { + (*text).pos.x + } else { + (*text).pos.y + }; + + if text_lpos == cur_lpos { + return; + } else if text_lpos > cur_lpos { + let text1 = allocate_text_node(ls); + (*text1).pos.x = ls.cursor_pos.x; + (*text1).pos.y = ls.cursor_pos.y; + list_add(&mut (*text1).list, &mut *(*text).list.prev); + text = text1; + return; + } + }, + ); + + if &(*text).list as *const ListHead == &ctx.text_list_head as *const ListHead { + text = allocate_text_node(ls); + (*text).pos.x = ls.cursor_pos.x; + (*text).pos.y = ls.cursor_pos.y; + list_add_tail(&mut (*text).list, &mut ctx.text_list_head); + } + + if is_horizontal_layout(ls.format) { + if ls.cursor_pos.y < (*text).pos.y { + (*text).pos.y = ls.cursor_pos.y; + (*text).used = 0; + } + ls.cursor_pos.y += csp; + (*text).pos.y += csp; + } else { + if ls.cursor_pos.x < (*text).pos.x { + (*text).pos.x = ls.cursor_pos.x; + (*text).used = 0; + } + ls.cursor_pos.x += csp; + (*text).pos.x += csp; + } + + reserve_buf(&mut *text, 2); + (*text).buf.add((*text).used).write(ch as u8 as i8); + (*text).used += 1; + (*text).buf.add((*text).used).write(0); + + 1 + } +} + +pub fn ccx_strstr_ignorespace(str1: *const c_char, str2: *const c_char) -> i32 { + unsafe { + let mut i = 0; + while *str2.add(i) != 0 { + if (*str2.add(i) as u8).is_ascii_whitespace() { + i += 1; + continue; + } + if *str2.add(i) != *str1.add(i) { + return 0; + } + i += 1; + } + 1 + } +} + +pub fn list_empty(head: &ListHead) -> bool { + head.next as *mut _ == head as *const _ as *mut _ +} + +pub fn get_text(ctx: &mut ISDBSubContext, buffer: &mut [u8], len: usize) -> usize { + unsafe { + let ls = &ctx.current_state.layout_state; + let mut text: *mut ISDBText = std::ptr::null_mut(); + let mut sb_text: *mut ISDBText = std::ptr::null_mut(); + let mut sb_temp: *mut ISDBText = std::ptr::null_mut(); + + // check no overflow for user + let mut index = 0; + + if ctx.cfg_no_rollup != 0 || (ctx.cfg_no_rollup == ctx.current_state.rollup_mode) { + if list_empty(&ctx.buffered_text) { + list_for_each_entry( + &mut ctx.text_list_head as *mut ListHead, + std::mem::offset_of!(ISDBText, list), + |text| { + sb_text = allocate_text_node(ls); + list_add_tail(&mut (*sb_text).list, &mut ctx.buffered_text); + reserve_buf(&mut *sb_text, (*text).used); + std::ptr::copy_nonoverlapping((*text).buf, (*sb_text).buf, (*text).used); + *(*sb_text).buf.offset((*text).used as isize) = 0; + (*sb_text).used = (*text).used; + }, + ); + return 0; + } + + list_for_each_entry( + &mut ctx.text_list_head as *mut ListHead, + std::mem::offset_of!(ISDBText, list), + |text| { + let mut found = false; + list_for_each_entry( + &mut ctx.buffered_text as *mut ListHead, + std::mem::offset_of!(ISDBText, list), + |sb_text| { + if !found && ccx_strstr_ignorespace((*text).buf, (*sb_text).buf) != 0 { + found = true; + if ccx_strstr_ignorespace((*sb_text).buf, (*text).buf) == 0 { + reserve_buf(&mut *sb_text, (*text).used); + std::ptr::copy_nonoverlapping( + (*text).buf, + (*sb_text).buf, + (*text).used, + ); + } + } + }, + ); + if !found { + sb_text = allocate_text_node(ls); + list_add_tail(&mut (*sb_text).list, &mut ctx.buffered_text); + reserve_buf(&mut *sb_text, (*text).used); + std::ptr::copy_nonoverlapping((*text).buf, (*sb_text).buf, (*text).used); + *(*sb_text).buf.offset((*text).used as isize) = 0; + (*sb_text).used = (*text).used; + } + }, + ); + + list_for_each_entry_safe(sb_text, sb_temp, &mut ctx.buffered_text, b"list\0".as_ptr()); + while !sb_text.is_null() { + let mut found = false; + list_for_each_entry( + &mut ctx.text_list_head as *mut ListHead, + std::mem::offset_of!(ISDBText, list), + |text| { + if ccx_strstr_ignorespace((*text).buf, (*sb_text).buf) != 0 { + found = true; + } + }, + ); + if !found && len - index > (*sb_text).used + 2 && (*sb_text).used > 0 { + std::ptr::copy_nonoverlapping( + (*sb_text).buf, + buffer[index..].as_mut_ptr() as *mut i8, + (*sb_text).used, + ); + buffer[(*sb_text).used + index] = b'\n'; + buffer[(*sb_text).used + index + 1] = b'\r'; + index += (*sb_text).used + 2; + (*sb_text).used = 0; + list_del(&mut (*sb_text).list); + free((*sb_text).buf as *mut c_void); + free(sb_text as *mut c_void); + } + sb_text = sb_temp; + sb_temp = list_entry((*sb_temp).list.next, b"list\0".as_ptr()); + } + } else { + list_for_each_entry( + &mut ctx.text_list_head as *mut ListHead, + std::mem::offset_of!(ISDBText, list), + |text| { + if len - index > (*text).used + 2 && (*text).used > 0 { + std::ptr::copy_nonoverlapping( + (*text).buf, + buffer[index..].as_mut_ptr() as *mut i8, + (*text).used, + ); + buffer[(*text).used + index] = b'\n'; + buffer[(*text).used + index + 1] = b'\r'; + index += (*text).used + 2; + (*text).used = 0; + *(*text).buf = 0; + } + }, + ); + } + index + } +} + +pub fn set_writing_format(ctx: &mut ISDBSubContext, mut arg: &[u8]) { + let ls = &mut ctx.current_state.layout_state; + + if arg[1] == 0x20 { + ls.format = match arg[0] & 0x0F { + 0 => WritingFormat::HorizontalStdDensity, + 1 => WritingFormat::VerticalStdDensity, + 2 => WritingFormat::HorizontalHighDensity, + 3 => WritingFormat::VerticalHighDensity, + 4 => WritingFormat::HorizontalWesternLang, + 5 => WritingFormat::Horizontal1920x1080, + 6 => WritingFormat::Vertical1920x1080, + 7 => WritingFormat::Horizontal960x540, + 8 => WritingFormat::Vertical960x540, + 9 => WritingFormat::Horizontal720x480, + 10 => WritingFormat::Vertical720x480, + 11 => WritingFormat::Horizontal1280x720, + 12 => WritingFormat::Vertical1280x720, + _ => WritingFormat::None, + }; + return; + } + + if arg[1] == 0x3B { + ls.format = WritingFormat::HorizontalCustom; + arg = &arg[2..]; + } + if arg[1] == 0x3B { + match arg[0] & 0x0F { + 0 => { + // ctx.font_size = FontSize::SmallFontSize; + } + 1 => { + // ctx.font_size = FontSize::MiddleFontSize; + } + 2 => { + // ctx.font_size = FontSize::StandardFontSize; + } + _ => {} + } + arg = &arg[2..]; + } + + isdb_log(format_args!("character numbers in one line in decimal:\0")); + while arg[0] != 0x3B && arg[0] != 0x20 { + ctx.nb_char = arg[0] as i32; + println!(" {}", arg[0] & 0x0F); + arg = &arg[1..]; + } + if arg[0] == 0x20 { + return; + } + arg = &arg[1..]; + isdb_log(format_args!("line numbers in decimal: ")); + while arg[0] != 0x20 { + ctx.nb_line = arg[0] as i32; + println!(" {}", arg[0] & 0x0F); + arg = &arg[1..]; + } +} + +pub fn move_penpos(ctx: &mut ISDBSubContext, col: i32, row: i32) { + let ls = &mut ctx.current_state.layout_state; + + ls.cursor_pos.x = row; + ls.cursor_pos.y = col; +} + +pub fn set_position(ctx: &mut ISDBSubContext, p1: u32, p2: u32) { + let ls = &mut ctx.current_state.layout_state; + let (cw, ch, col, row); + + if is_horizontal_layout(ls.format) { + cw = (ls.font_size + ls.cell_spacing.col) * ls.font_scale.fscx / 100; + ch = (ls.font_size + ls.cell_spacing.row) * ls.font_scale.fscy / 100; + // pen pos is bottom left + col = p2 as i32 * cw; + row = p1 as i32 * ch + ch; + } else { + cw = (ls.font_size + ls.cell_spacing.col) * ls.font_scale.fscy / 100; + ch = (ls.font_size + ls.cell_spacing.row) * ls.font_scale.fscx / 100; + // pen pos is upper center, -90deg rotated, hence mid left + col = p2 as i32 * cw; + row = p1 as i32 * ch + ch / 2; + } + move_penpos(ctx, col, row); +} + +pub fn get_csi_params(q: &[u8], p1: &mut Option, p2: &mut Option) -> Result { + let mut q_pivot = 0; + if p1.is_none() { + return Err(-1); + } + + *p1 = Some(0); + while q[q_pivot] >= 0x30 && q[q_pivot] <= 0x39 { + *p1.as_mut().unwrap() *= 10; + *p1.as_mut().unwrap() += (q[q_pivot] - 0x30) as u32; + q_pivot += 1; + } + if q[q_pivot] != 0x20 && q[q_pivot] != 0x3B { + return Err(-1); + } + q_pivot += 1; + if p2.is_none() { + return Ok(q_pivot as i32); + } + *p2 = Some(0); + while q[q_pivot] >= 0x30 && q[q_pivot] <= 0x39 { + *p2.as_mut().unwrap() *= 10; + *p2.as_mut().unwrap() += (q[q_pivot] - 0x30) as u32; + q_pivot += 1; + } + q_pivot += 1; + Ok(q_pivot as i32) +} + +pub fn isdb_command_log(_format: &str, _args: std::fmt::Arguments) { + // nothing said, nothing done +} + +pub fn parse_csi(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { + let mut arg = [0u8; 10]; + let mut i = 0; + let mut ret = 0; + let mut p1 = None; + let mut p2 = None; + let buf_pivot = buf; + let state = &mut ctx.current_state; + let ls = &mut state.layout_state; + + // cpy buf in arg + while buf[i] != 0x20 { + if i >= arg.len() { + isdb_log(format_args!("UnExpected CSI {} >= {}\n", arg.len(), i)); + break; + } + arg[i] = buf[i]; + i += 1; + } + // ignore terminating 0x20 char + arg[i] = buf[i]; + i += 1; + + match buf[i] { + // Writing Format setting + CSI_CMD_SWF => { + isdb_command_log("Command:CSI: SWF\n", format_args!("")); + set_writing_format(ctx, &arg); + } + // Composite Character compose + CSI_CMD_CCC => { + isdb_command_log("Command:CSI: CCC\n", format_args!("")); + ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); + if ret > 0 { + ls.ccc = match p1.unwrap() { + 0 => IsdbCCComposition::None, + 2 => IsdbCCComposition::And, + 3 => IsdbCCComposition::Or, + 4 => IsdbCCComposition::Xor, + _ => IsdbCCComposition::None, + }; + } + } + // Display Format setting + CSI_CMD_SDF => { + ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); + if ret > 0 { + ls.display_area.w = p1.unwrap() as i32; + ls.display_area.h = p2.unwrap() as i32; + } + isdb_command_log( + "Command:CSI: SDF (w:{}, h:{})\n", + format_args!("{}, {}", p1.unwrap(), p2.unwrap()), + ); + } + // char composition's dot designation + CSI_CMD_SSM => { + ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); + if ret > 0 { + ls.font_size = p1.unwrap() as i32; + } + isdb_command_log( + "Command:CSI: SSM (x:{}, y:{})\n", + format_args!("{}, {}", p1.unwrap(), p2.unwrap()), + ); + } + // Set Display Position + CSI_CMD_SDP => { + ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); + if ret > 0 { + ls.display_area.x = p1.unwrap() as i32; + ls.display_area.y = p2.unwrap() as i32; + } + isdb_command_log( + "Command:CSI: SDP (x:{}, y:{})\n", + format_args!("{}, {}", p1.unwrap(), p2.unwrap()), + ); + } + // Raster Colour command + CSI_CMD_RCS => { + ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); + if ret > 0 { + ctx.current_state.raster_color = DEFAULT_CLUT + [(ctx.current_state.clut_high_idx << 4 | p1.unwrap() as u8) as usize]; + } + isdb_command_log("Command:CSI: RCS ({})\n", format_args!("{}", p1.unwrap())); + } + // Set Horizontal Spacing + CSI_CMD_SHS => { + ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); + if ret > 0 { + ls.cell_spacing.col = p1.unwrap() as i32; + } + isdb_command_log("Command:CSI: SHS ({})\n", format_args!("{}", p1.unwrap())); + } + // Set Vertical Spacing + CSI_CMD_SVS => { + ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); + if ret > 0 { + ls.cell_spacing.row = p1.unwrap() as i32; + } + isdb_command_log("Command:CSI: SVS ({})\n", format_args!("{}", p1.unwrap())); + } + // Active Coordinate Position Set + CSI_CMD_ACPS => { + isdb_command_log("Command:CSI: ACPS\n", format_args!("")); + ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); + if ret > 0 { + ls.acps[0] = p1.unwrap() as i32; + ls.acps[1] = p2.unwrap() as i32; + } + } + _ => { + isdb_log(format_args!( + "Command:CSI: Unknown command 0x{:x}\n", + buf[i] + )); + } + } + i as i32 +} + +pub fn parse_command(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { + let buf_pivot = buf; + let code_lo = buf[0] & 0x0f; + let code_hi = (buf[0] & 0xf0) >> 4; + let mut ret; + let state = &mut ctx.current_state; + let ls = &mut state.layout_state; + + let mut buf = &buf[1..]; + match code_hi { + 0x00 => match code_lo { + 0x0 => isdb_command_log("Command: NUL\n", format_args!("")), + 0x7 => isdb_command_log("Command: BEL\n", format_args!("")), + 0x8 => isdb_command_log("Command: ABP\n", format_args!("")), + 0x9 => isdb_command_log("Command: APF\n", format_args!("")), + 0xA => isdb_command_log("Command: APD\n", format_args!("")), + 0xB => isdb_command_log("Command: APU\n", format_args!("")), + 0xC => isdb_command_log("Command: CS clear Screen\n", format_args!("")), + 0xD => isdb_command_log("Command: APR\n", format_args!("")), + 0xE => isdb_command_log("Command: LS1\n", format_args!("")), + 0xF => isdb_command_log("Command: LS0\n", format_args!("")), + _ => isdb_command_log("Command: Unknown\n", format_args!("")), + }, + 0x01 => match code_lo { + 0x6 => isdb_command_log("Command: PAPF\n", format_args!("")), + 0x8 => isdb_command_log("Command: CAN\n", format_args!("")), + 0x9 => isdb_command_log("Command: SS2\n", format_args!("")), + 0xB => isdb_command_log("Command: ESC\n", format_args!("")), + 0xC => { + isdb_command_log("Command: APS\n", format_args!("")); + set_position(ctx, (buf[0] & 0x3F) as u32, (buf[1] & 0x3F) as u32); + buf = &buf[2..]; + } + 0xD => isdb_command_log("Command: SS3\n", format_args!("")), + 0xE => isdb_command_log("Command: RS\n", format_args!("")), + 0xF => isdb_command_log("Command: US\n", format_args!("")), + _ => isdb_command_log("Command: Unknown\n", format_args!("")), + }, + 0x8 => match code_lo { + 0x0..=0x7 => { + isdb_command_log( + &format!( + "Command: Forground color (0x{:X})\n", + DEFAULT_CLUT[code_lo as usize] + ), + format_args!(""), + ); + state.fg_color = DEFAULT_CLUT[code_lo as usize]; + } + 0x8 => { + isdb_command_log("Command: SSZ\n", format_args!("")); + ls.font_scale.fscx = 50; + ls.font_scale.fscy = 50; + } + 0x9 => { + isdb_command_log("Command: MSZ\n", format_args!("")); + ls.font_scale.fscx = 200; + ls.font_scale.fscy = 200; + } + 0xA => { + isdb_command_log("Command: NSZ\n", format_args!("")); + ls.font_scale.fscx = 100; + ls.font_scale.fscy = 100; + } + 0xB => { + isdb_command_log("Command: SZX\n", format_args!("")); + buf = &buf[1..]; + } + _ => isdb_command_log("Command: Unknown\n", format_args!("")), + }, + 0x9 => match code_lo { + 0x0 => { + if buf[0] == 0x20 { + isdb_command_log( + &format!("Command: COL: Set Clut {}\n", (buf[0] & 0x0F)), + format_args!(""), + ); + buf = &buf[1..]; + ctx.current_state.clut_high_idx = (buf[0] & 0x0F) as u8; + } else if (buf[0] & 0xF0) == 0x40 { + isdb_command_log( + &format!( + "Command: COL: Set Forground 0x{:08X}\n", + DEFAULT_CLUT[(buf[0] & 0x0F) as usize] + ), + format_args!(""), + ); + ctx.current_state.fg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; + } else if (buf[0] & 0xF0) == 0x50 { + isdb_command_log( + &format!( + "Command: COL: Set Background 0x{:08X}\n", + DEFAULT_CLUT[(buf[0] & 0x0F) as usize] + ), + format_args!(""), + ); + ctx.current_state.bg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; + } else if (buf[0] & 0xF0) == 0x60 { + isdb_command_log( + &format!( + "Command: COL: Set half Forground 0x{:08X}\n", + DEFAULT_CLUT[(buf[0] & 0x0F) as usize] + ), + format_args!(""), + ); + ctx.current_state.hfg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; + } else if (buf[0] & 0xF0) == 0x70 { + isdb_command_log( + &format!( + "Command: COL: Set Half Background 0x{:08X}\n", + DEFAULT_CLUT[(buf[0] & 0x0F) as usize] + ), + format_args!(""), + ); + ctx.current_state.hbg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; + } + buf = &buf[1..]; + } + 0x1 => { + isdb_command_log("Command: FLC\n", format_args!("")); + buf = &buf[1..]; + } + 0x2 => { + isdb_command_log("Command: CDC\n", format_args!("")); + buf = &buf[3..]; + } + 0x3 => { + isdb_command_log("Command: POL\n", format_args!("")); + buf = &buf[1..]; + } + 0x4 => { + isdb_command_log("Command: WMM\n", format_args!("")); + buf = &buf[3..]; + } + 0x5 => { + isdb_command_log("Command: MACRO\n", format_args!("")); + buf = &buf[1..]; + } + 0x7 => { + isdb_command_log("Command: HLC\n", format_args!("")); + buf = &buf[1..]; + } + 0x8 => { + isdb_command_log("Command: RPC\n", format_args!("")); + buf = &buf[1..]; + } + 0x9 => isdb_command_log("Command: SPL\n", format_args!("")), + 0xA => isdb_command_log("Command: STL\n", format_args!("")), + 0xB => { + ret = parse_csi(ctx, buf); + buf = &buf[ret as usize..]; + } + 0xD => { + isdb_command_log("Command: TIME\n", format_args!("")); + buf = &buf[2..]; + } + _ => isdb_command_log("Command: Unknown\n", format_args!("")), + }, + _ => isdb_command_log("Command: Unknown\n", format_args!("")), + } + + (buf_pivot.len() - buf.len()) as i32 +} + +pub fn parse_caption_management_data(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { + let buf_pivot = buf; + let mut buf = buf; + + ctx.tmd = match buf[0] >> 6 { + 0 => IsdbTmd::Free, + 1 => IsdbTmd::RealTime, + 2 => IsdbTmd::OffsetTime, + _ => IsdbTmd::Free, // default case + }; + isdb_log(format_args!("CC MGMT DATA: TMD: {:?}\n", ctx.tmd)); + buf = &buf[1..]; + + match ctx.tmd { + ISDB_TMD_FREE => { + isdb_log(format_args!( + "Playback time is not restricted to synchronize to the clock.\n" + )); + } + ISDB_TMD_OFFSET_TIME => { + ctx.offset_time.hour = ((buf[0] >> 4) * 10 + (buf[0] & 0xf)) as i32; + buf = &buf[1..]; + ctx.offset_time.min = ((buf[0] >> 4) * 10 + (buf[0] & 0xf)) as i32; + buf = &buf[1..]; + ctx.offset_time.sec = ((buf[0] >> 4) * 10 + (buf[0] & 0xf)) as i32; + buf = &buf[1..]; + ctx.offset_time.milli = + ((buf[0] >> 4) * 100 + ((buf[0] & 0xf) * 10) + (buf[1] & 0xf)) as i32; + buf = &buf[2..]; + isdb_log(format_args!( + "CC MGMT DATA: OTD( h:{} m:{} s:{} millis: {}\n", + ctx.offset_time.hour, + ctx.offset_time.min, + ctx.offset_time.sec, + ctx.offset_time.milli + )); + } + _ => { + isdb_log(format_args!( + "Playback time is in accordance with the time of the clock, \ + which is calibrated by clock signal (TDT). Playback time is \ + given by PTS.\n" + )); + } + } + ctx.nb_lang = buf[0] as i32; + isdb_log(format_args!( + "CC MGMT DATA: nb languages: {}\n", + ctx.nb_lang + )); + buf = &buf[1..]; + + for _ in 0..ctx.nb_lang { + isdb_log(format_args!("CC MGMT DATA: {}\n", (buf[0] & 0x1F) >> 5)); + ctx.dmf = buf[0] & 0x0F; + isdb_log(format_args!("CC MGMT DATA: DMF 0x{:X}\n", ctx.dmf)); + buf = &buf[1..]; + if ctx.dmf == 0xC || ctx.dmf == 0xD || ctx.dmf == 0xE { + ctx.dc = buf[0]; + if ctx.dc == 0x00 { + isdb_log(format_args!("Attenuation Due to Rain\n")); + } + } + isdb_log(format_args!( + "CC MGMT DATA: languages: {}{}{}\n", + buf[0] as char, buf[1] as char, buf[2] as char + )); + buf = &buf[3..]; + isdb_log(format_args!("CC MGMT DATA: Format: 0x{:X}\n", buf[0] >> 4)); + isdb_log(format_args!( + "CC MGMT DATA: TCS: 0x{:X}\n", + (buf[0] >> 2) & 0x3 + )); + ctx.current_state.rollup_mode = if (buf[0] & 0x3) != 0 { 1 } else { 0 }; + isdb_log(format_args!( + "CC MGMT DATA: Rollup mode: 0x{:X}\n", + ctx.current_state.rollup_mode + )); + buf = &buf[1..]; + } + (buf_pivot.len() - buf.len()) as i32 +} + +// in/from ccx_common_common.c +use std::ffi::CString; +pub fn add_cc_sub_text( + sub: &mut CcSubtitle, + str: &str, + start_time: i64, + end_time: i64, + info: Option<&str>, + mode: Option<&str>, + e_type: CcxEncodingType, +) -> i32 { + if str.is_empty() { + return 0; + } + + let target_sub = if sub.nb_data > 0 { + let mut current = sub; + while !current.next.is_null() { + current = unsafe { &mut *current.next }; + } + let new_sub = Box::into_raw(Box::new(CcSubtitle { + data: ptr::null_mut(), + datatype: Subdatatype::Generic, + nb_data: 0, + type_: Subtype::Text, + enc_type: e_type, + start_time: 0, + end_time: 0, + flags: 0, + lang_index: 0, + got_output: 0, + mode: [0; 5], + info: [0; 4], + time_out: 0, + next: ptr::null_mut(), + prev: current, + })); + current.next = new_sub; + unsafe { &mut *new_sub } + } else { + sub + }; + + target_sub.type_ = Subtype::Text; + target_sub.enc_type = e_type; + target_sub.data = CString::new(str).unwrap().into_raw() as *mut c_void; + target_sub.datatype = Subdatatype::Generic; + target_sub.nb_data = str.len() as u32; + target_sub.start_time = start_time; + target_sub.end_time = end_time; + if let Some(info_str) = info { + let info_bytes = info_str.as_bytes(); + target_sub.info[..info_bytes.len()].copy_from_slice(&info_bytes[..4]); + } + if let Some(mode_str) = mode { + let mode_bytes = mode_str.as_bytes(); + target_sub.mode[..mode_bytes.len()].copy_from_slice(&mode_bytes[..4]); + } + target_sub.got_output = 1; + target_sub.next = ptr::null_mut(); + + 0 +} + +pub fn parse_statement(ctx: &mut ISDBSubContext, buf: &[u8], size: i32) -> i32 { + let buf_pivot = buf; + let mut buf = buf; + let mut ret; + + while (buf.len() - buf_pivot.len()) < size as usize { + let code = (buf[0] & 0xf0) >> 4; + let code_lo = buf[0] & 0x0f; + if code <= 0x1 { + ret = parse_command(ctx, buf); + } else if code == 0x2 && code_lo == 0x0 { + ret = append_char(ctx, buf[0] as char); + } else if code == 0x7 && code_lo == 0xF { + ret = append_char(ctx, buf[0] as char); + } else if code <= 0x7 { + ret = append_char(ctx, buf[0] as char); + } else if code <= 0x9 { + ret = parse_command(ctx, buf); + } else if code == 0xA && code_lo == 0x0 { + // still todo :) + ret = 1; + } else if code == 0x0F && code_lo == 0xF { + // still todo :) + ret = 1; + } else { + ret = append_char(ctx, buf[0] as char); + } + if ret < 0 { + break; + } + buf = &buf[ret as usize..]; + } + 0 +} + +pub fn parse_data_unit(ctx: &mut ISDBSubContext, buf: &[u8], size: i32) -> i32 { + let mut buf = buf; + // unit separator + buf = &buf[1..]; + + let unit_parameter = buf[0]; + buf = &buf[1..]; + let len = rb24(buf); + buf = &buf[3..]; + + match unit_parameter { + // statement body + 0x20 => { + parse_statement(ctx, buf, len); + } + _ => {} + } + 0 +} + +pub fn parse_caption_statement_data( + ctx: &mut ISDBSubContext, + lang_id: i32, + buf: &[u8], + size: i32, + sub: &mut CcSubtitle, +) -> i32 { + let mut buf = buf; + let tmd = buf[0] >> 6; + buf = &buf[1..]; + + // skipping timing data + if tmd == 1 || tmd == 2 { + buf = &buf[5..]; + } + + let len = rb24(buf); + buf = &buf[3..]; + let ret = parse_data_unit(ctx, buf, len); + if ret < 0 { + return -1; + } + + let mut buffer = [0u8; 1024]; + let ret = get_text(ctx, &mut buffer, 1024); + // cpy data if there in buffer + if ret < 0 { + return 0; + } + + if ret > 0 { + add_cc_sub_text( + sub, + std::str::from_utf8(&buffer).unwrap_or(""), + ctx.prev_timestamp as i64, + ctx.timestamp as i64, + Some("NA"), + Some("ISDB"), + CcxEncodingType::Utf8, + ); + if sub.start_time == sub.end_time { + sub.end_time += 2; + } + ctx.prev_timestamp = ctx.timestamp; + } + 0 +} + +fn rb24(buf: &[u8]) -> i32 { + ((buf[0] as i32) << 16) | ((buf[1] as i32) << 8) | (buf[2] as i32) +} + +pub static mut CCX_OPTIONS: CcxOptions = CcxOptions { + messages_target: CCX_MESSAGES_QUIET, +}; + +pub fn mprint(message: &str) { + unsafe { + if CCX_OPTIONS.messages_target == CCX_MESSAGES_QUIET { + return; + } + + match CCX_OPTIONS.messages_target { + CCX_MESSAGES_STDOUT => { + print!("{}", message); + io::stdout().flush().unwrap(); + } + CCX_MESSAGES_STDERR => { + eprint!("{}", message); + io::stderr().flush().unwrap(); + } + _ => (), + } + } +} + +pub fn cstr(s: &str) -> std::ffi::CString { + std::ffi::CString::new(s).unwrap() +} + +pub fn isdb_parse_data_group(codec_ctx: *mut c_void, buf: &[u8], sub: &mut CcSubtitle) -> i32 { + let ctx = unsafe { &mut *(codec_ctx as *mut ISDBSubContext) }; + let buf_pivot = buf; + let mut buf = buf; + let id = buf[0] >> 2; + let mut group_size = 0; + let mut ret = 0; + + if (id >> 4) == 0 { + isdb_log(format_args!("ISDB group A\n")); + } else if (id >> 4) == 0 { + isdb_log(format_args!("ISDB group B\n")); + } + + buf = &buf[3..]; + + group_size = rb16(buf); + buf = &buf[2..]; + isdb_log(format_args!( + "ISDB (Data group) group_size {}\n", + group_size + )); + + if ctx.prev_timestamp > ctx.timestamp { + ctx.prev_timestamp = ctx.timestamp; + } + if (id & 0x0F) == 0 { + // for caption management + ret = parse_caption_management_data(ctx, buf); + } else if (id & 0x0F) < 8 { + // caption sttmnt data + isdb_log(format_args!("ISDB {} language\n", id)); + ret = parse_caption_statement_data(ctx, (id & 0x0F) as i32, buf, group_size as i32, sub); + } else { + // coz not allowed in spec + } + if ret < 0 { + return -1; + } + buf = &buf[group_size as usize..]; + + // crc todo still :) + buf = &buf[2..]; + + (buf_pivot.len() - buf.len()) as i32 +} + +fn rb16(buf: &[u8]) -> i32 { + ((buf[0] as i32) << 8) | (buf[1] as i32) +} + +pub fn isdbsub_decode(dec_ctx: &mut LibCcDecode, buf: &[u8], sub: &mut CcSubtitle) -> i32 { + let mut buf = buf; + let mut ret = 0; + let ctx = unsafe { &mut *(dec_ctx.private_data as *mut ISDBSubContext) }; + + if buf[0] != 0x80 { + mprint("Not a Syncronised PES\n"); + return -1; + } + buf = &buf[1..]; + + // pvt data stream is 0xFF + buf = &buf[1..]; + let header_end = buf[0] & 0x0f; + buf = &buf[1..]; + + while buf.len() > header_end as usize { + // todo header spec finding :) + buf = &buf[1..]; + } + + ctx.cfg_no_rollup = dec_ctx.no_rollup; + ret = isdb_parse_data_group(ctx as *mut _ as *mut c_void, buf, sub); + if ret < 0 { + return -1; + } + + 1 +} diff --git a/src/rust/lib_ccxr/src/isdbs/mod.rs b/src/rust/lib_ccxr/src/isdbs/mod.rs new file mode 100644 index 000000000..be0869812 --- /dev/null +++ b/src/rust/lib_ccxr/src/isdbs/mod.rs @@ -0,0 +1,2 @@ +pub mod isdbd; +pub mod isdbs; diff --git a/src/rust/lib_ccxr/src/lib.rs b/src/rust/lib_ccxr/src/lib.rs index 9f32678db..d1ae32357 100644 --- a/src/rust/lib_ccxr/src/lib.rs +++ b/src/rust/lib_ccxr/src/lib.rs @@ -1,6 +1,7 @@ pub mod activity; pub mod common; pub mod hardsubx; +pub mod isdbs; pub mod subtitle; pub mod teletext; pub mod time; diff --git a/src/rust/src/libccxr_exports/isdb.rs b/src/rust/src/libccxr_exports/isdb.rs new file mode 100644 index 000000000..46a3c9cbf --- /dev/null +++ b/src/rust/src/libccxr_exports/isdb.rs @@ -0,0 +1,72 @@ +extern crate libc; + +use lib_ccxr::isdbs::isdbd::*; +use lib_ccxr::isdbs::isdbs::*; + +use std::mem; +use std::os::raw::{c_int, c_uint, c_void}; +use std::ptr; + +#[no_mangle] +pub extern "C" fn ccxr_isdb_set_global_time(dec_ctx: *mut LibCcDecode, timestamp: u64) -> c_int { + unsafe { + let ctx = (*dec_ctx).private_data as *mut ISDBSubContext; + if !ctx.is_null() { + isdb_set_global_time(&mut *ctx, timestamp); + 0 // return 0 if everything is CCX_OK + } else { + -1 // return an error code if ctx is null + } + } +} + +#[no_mangle] +pub unsafe extern "C" fn ccxr_delete_isdb_decoder(isdb_ctx: *mut *mut c_void) { + let ctx = *isdb_ctx as *mut ISDBSubContext; + delete_isdb_decoder(ctx); + freep(isdb_ctx); +} + +#[no_mangle] +pub extern "C" fn ccxr_init_isdb_decoder() -> *mut c_void { + unsafe { + let ctx = libc::malloc(mem::size_of::()) as *mut ISDBSubContext; + if ctx.is_null() { + return ptr::null_mut(); + } + + libc::memset(ctx as *mut c_void, 0, mem::size_of::()); + + init_list_head(&mut (*ctx).text_list_head); + init_list_head(&mut (*ctx).buffered_text); + (*ctx).prev_timestamp = c_uint::MAX as u64; + + (*ctx).current_state.clut_high_idx = 0; + (*ctx).current_state.rollup_mode = 0; + init_layout(&mut (*ctx).current_state.layout_state); + + ctx as *mut c_void + } +} + +#[no_mangle] +pub extern "C" fn ccxr_isdb_parse_data_group( + codec_ctx: *mut c_void, + buf: *const u8, + size: i32, + sub: *mut CcSubtitle, +) -> i32 { + let buf_slice = unsafe { std::slice::from_raw_parts(buf, size as usize) }; + isdb_parse_data_group(codec_ctx, buf_slice, unsafe { &mut *sub }) +} + +#[no_mangle] +pub extern "C" fn ccxr_isdbsub_decode( + dec_ctx: *mut LibCcDecode, + buf: *const u8, + buf_size: usize, + sub: *mut CcSubtitle, +) -> i32 { + let buf_slice = unsafe { std::slice::from_raw_parts(buf, buf_size) }; + isdbsub_decode(unsafe { &mut *dec_ctx }, buf_slice, unsafe { &mut *sub }) +} diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs index c2f64a258..016304bdc 100644 --- a/src/rust/src/libccxr_exports/mod.rs +++ b/src/rust/src/libccxr_exports/mod.rs @@ -1,5 +1,6 @@ //! Provides C-FFI functions that are direct equivalent of functions available in C. +pub mod isdb; pub mod time; use crate::ccx_options; use lib_ccxr::util::log::*; From ccdfeee37d3c435276629b79c43b42629c37114c Mon Sep 17 00:00:00 2001 From: vats004 <=> Date: Fri, 28 Mar 2025 23:02:25 +0530 Subject: [PATCH 2/4] some serious code refactoring --- src/lib_ccx/ccx_decoders_isdb.c | 31 +- src/rust/Cargo.lock | 106 +- src/rust/Cargo.toml | 10 +- src/rust/lib_ccxr/Cargo.toml | 12 +- .../lib_ccxr/src/decoder_isdb/exit_codes.rs | 56 + .../src/decoder_isdb/functions_common.rs | 173 ++ .../src/decoder_isdb/functions_isdb.rs | 1986 +++++++++++++++++ src/rust/lib_ccxr/src/decoder_isdb/mod.rs | 6 + .../src/decoder_isdb/structs_ccdecode.rs | 281 +++ .../lib_ccxr/src/decoder_isdb/structs_isdb.rs | 798 +++++++ .../lib_ccxr/src/decoder_isdb/structs_xds.rs | 392 ++++ src/rust/lib_ccxr/src/isdbs/isdbd.rs | 1250 ----------- src/rust/lib_ccxr/src/isdbs/isdbs.rs | 1204 ---------- src/rust/lib_ccxr/src/isdbs/mod.rs | 2 - src/rust/lib_ccxr/src/lib.rs | 2 +- src/rust/src/libccxr_exports/isdb.rs | 72 - src/rust/src/libccxr_exports/isdb_exports.rs | 95 + src/rust/src/libccxr_exports/mod.rs | 2 +- 18 files changed, 3820 insertions(+), 2658 deletions(-) create mode 100644 src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs create mode 100644 src/rust/lib_ccxr/src/decoder_isdb/functions_common.rs create mode 100644 src/rust/lib_ccxr/src/decoder_isdb/functions_isdb.rs create mode 100644 src/rust/lib_ccxr/src/decoder_isdb/mod.rs create mode 100644 src/rust/lib_ccxr/src/decoder_isdb/structs_ccdecode.rs create mode 100644 src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs create mode 100644 src/rust/lib_ccxr/src/decoder_isdb/structs_xds.rs delete mode 100644 src/rust/lib_ccxr/src/isdbs/isdbd.rs delete mode 100644 src/rust/lib_ccxr/src/isdbs/isdbs.rs delete mode 100644 src/rust/lib_ccxr/src/isdbs/mod.rs delete mode 100644 src/rust/src/libccxr_exports/isdb.rs create mode 100644 src/rust/src/libccxr_exports/isdb_exports.rs diff --git a/src/lib_ccx/ccx_decoders_isdb.c b/src/lib_ccx/ccx_decoders_isdb.c index af1223efd..8e87081b2 100644 --- a/src/lib_ccx/ccx_decoders_isdb.c +++ b/src/lib_ccx/ccx_decoders_isdb.c @@ -1,4 +1,3 @@ - #include "ccx_decoders_isdb.h" #include "lib_ccx.h" #include "utility.h" @@ -6,11 +5,26 @@ #ifndef DISABLE_RUST -extern int ccxr_isdb_set_global_time(struct lib_cc_decode *dec_ctx, uint64_t timestamp); -extern void ccxr_delete_isdb_decoder(void **isdb_ctx); -extern void *ccxr_init_isdb_decoder(void); -extern int ccxr_isdb_parse_data_group(void *codec_ctx, const uint8_t *buf, int size, struct cc_subtitle *sub); -extern int ccxr_isdbsub_decode(struct lib_cc_decode *dec_ctx, const uint8_t *buf, size_t buf_size, struct cc_subtitle *sub); +extern int ccxr_isdb_set_global_time( + struct lib_cc_decode *dec_ctx, + unsigned long long timestamp); + +extern void ccxr_delete_isdb_decoder( + void **isdb_ctx); + +extern struct ISDBSubContext *ccxr_init_isdb_decoder( + void); + +extern int ccxr_isdb_parse_data_group( + struct ISDBSubContext *codec_ctx, + const unsigned char *buf, + struct cc_subtitle *sub); + +extern int ccxr_isdbsub_decode( + struct lib_cc_decode *dec_ctx, + const unsigned char *buf, + size_t buf_len, + struct cc_subtitle *sub); #endif @@ -487,6 +501,7 @@ static int ccx_strstr_ignorespace(const unsigned char *str1, const unsigned char } return 1; } + /** * Copy data not more then len provided * User should check for return type to check how much data he has got @@ -1347,7 +1362,7 @@ static int parse_caption_statement_data(ISDBSubContext *ctx, int lang_id, const int isdb_parse_data_group(void *codec_ctx, const uint8_t *buf, struct cc_subtitle *sub) { #ifndef DISABLE_RUST - return ccxr_isdb_parse_data_group(codec_ctx, buf, 0, sub); + return ccxr_isdb_parse_data_group(codec_ctx, buf, sub); #else ISDBSubContext *ctx = codec_ctx; const uint8_t *buf_pivot = buf; @@ -1458,4 +1473,4 @@ int isdb_set_global_time(struct lib_cc_decode *dec_ctx, uint64_t timestamp) ctx->timestamp = timestamp; return CCX_OK; #endif -} +} \ No newline at end of file diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 75a29f7fe..93ca302ac 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -61,12 +61,6 @@ dependencies = [ "windows-sys", ] -[[package]] -name = "anyhow" -version = "1.0.97" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" - [[package]] name = "approx" version = "0.5.1" @@ -150,27 +144,12 @@ version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" -[[package]] -name = "bytes" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" - [[package]] name = "camino" version = "1.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b96ec4966b5813e2c0507c1f86115c8c5abaadc3980879c3424042a02fd1ad3" -[[package]] -name = "cc" -version = "1.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be714c154be609ec7f5dad223a33bf1482fff90472de28f7362806e6d4832b8c" -dependencies = [ - "shlex", -] - [[package]] name = "ccx_rust" version = "0.1.0" @@ -180,18 +159,12 @@ dependencies = [ "clap", "env_logger", "iconv", - "lazy_static", "leptonica-sys", "lib_ccxr", - "libc", "log", - "nanomsg", - "nanomsg-sys", "num-integer", "palette", "pkg-config", - "prost", - "prost-types", "rsmpeg", "strum 0.25.0", "strum_macros 0.25.3", @@ -266,15 +239,6 @@ version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - [[package]] name = "colorchoice" version = "1.0.3" @@ -388,12 +352,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" - [[package]] name = "glob" version = "0.3.2" @@ -652,13 +610,9 @@ dependencies = [ "bitflags 2.9.0", "crc32fast", "derive_more", - "lazy_static", - "libc", - "nanomsg", - "nanomsg-sys", + "env_logger", + "log", "num_enum", - "prost", - "prost-types", "strum 0.26.3", "strum_macros 0.26.4", "thiserror", @@ -712,28 +666,6 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" -[[package]] -name = "nanomsg" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617e0160fba522f8667df7bc79f3b4a74a0e3968d08023ebb3ce717a5f3bd3ac" -dependencies = [ - "libc", - "nanomsg-sys", -] - -[[package]] -name = "nanomsg-sys" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78aa3ccb6d007dfecb4f7070725c4b1670a87677babb6621cb0c8cce9cfdc004" -dependencies = [ - "cmake", - "gcc", - "libc", - "pkg-config", -] - [[package]] name = "nom" version = "7.1.3" @@ -920,38 +852,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "prost" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" -dependencies = [ - "bytes", - "prost-derive", -] - -[[package]] -name = "prost-derive" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" -dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn 2.0.99", -] - -[[package]] -name = "prost-types" -version = "0.13.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" -dependencies = [ - "prost", -] - [[package]] name = "quote" version = "1.0.39" @@ -1543,4 +1443,4 @@ dependencies = [ "proc-macro2", "quote", "syn 2.0.99", -] +] \ No newline at end of file diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 3825ec178..96fa7b309 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -27,14 +27,8 @@ time = "0.3.39" cfg-if = "1.0.0" num-integer = "0.1.46" lib_ccxr = { path = "lib_ccxr" } - url = "2.5.4" -libc = "0.2.169" -nanomsg = { version = "0.7.2" } -prost = "0.13.4" -prost-types = "0.13.4" -lazy_static = "1.5.0" -nanomsg-sys = "0.7.2" + [build-dependencies] bindgen = "0.64.0" @@ -49,4 +43,4 @@ hardsubx_ocr = ["rsmpeg", "tesseract-sys", "leptonica-sys"] [profile.release-with-debug] inherits = "release" -debug = true +debug = true \ No newline at end of file diff --git a/src/rust/lib_ccxr/Cargo.toml b/src/rust/lib_ccxr/Cargo.toml index 9344aef7e..3e7a78923 100644 --- a/src/rust/lib_ccxr/Cargo.toml +++ b/src/rust/lib_ccxr/Cargo.toml @@ -15,12 +15,8 @@ strum = "0.26.3" strum_macros = "0.26.4" crc32fast = "1.4.2" num_enum = "0.6.1" -libc = "0.2.169" -nanomsg = "0.7.2" -prost = "0.13.4" -prost-types = "0.13.4" -lazy_static = "1.5.0" -nanomsg-sys = "0.7.2" +log = "0.4.26" +env_logger = "0.8.4" [features] default = [ @@ -35,6 +31,4 @@ wtv_debug = [] enable_ffmpeg = [] debug_out = [] debug = [] -with_libcurl = [] - - +with_libcurl = [] \ No newline at end of file diff --git a/src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs b/src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs new file mode 100644 index 000000000..2bc1e4427 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs @@ -0,0 +1,56 @@ +// Status codes +pub const CCX_OK: i64 = 0; +pub const CCX_FALSE: i64 = 0; +pub const CCX_TRUE: i64 = 1; +pub const CCX_EAGAIN: i64 = -100; +pub const CCX_EOF: i64 = -101; +pub const CCX_EINVAL: i64 = -102; +pub const CCX_ENOSUPP: i64 = -103; +pub const CCX_ENOMEM: i64 = -104; + +pub const NUM_BYTES_PER_PACKET: i64 = 35; // Class + type (repeated for convenience) + data + zero +pub const NUM_XDS_BUFFERS: i64 = 35; // CEA recommends no more than one level of interleaving. Play it safe + +pub const MAXBFRAMES: usize = 50; +pub const SORTBUF: usize = 2 * MAXBFRAMES + 1; + +// Time at which we switched to XDS mode, -1 means it hasn't happened yet +pub const TS_START_OF_XDS: i64 = -1; + +// Exit codes +pub const EXIT_OK: i64 = 0; +pub const EXIT_NO_INPUT_FILES: i64 = 2; +pub const EXIT_TOO_MANY_INPUT_FILES: i64 = 3; +pub const EXIT_INCOMPATIBLE_PARAMETERS: i64 = 4; +pub const EXIT_UNABLE_TO_DETERMINE_FILE_SIZE: i64 = 6; +pub const EXIT_MALFORMED_PARAMETER: i64 = 7; +pub const EXIT_READ_ERROR: i64 = 8; +pub const EXIT_NO_CAPTIONS: i64 = 10; +pub const EXIT_WITH_HELP: i64 = 11; +pub const EXIT_NOT_CLASSIFIED: i64 = 300; +pub const EXIT_ERROR_IN_CAPITALIZATION_FILE: i64 = 501; +pub const EXIT_BUFFER_FULL: i64 = 502; +pub const EXIT_MISSING_ASF_HEADER: i64 = 1001; +pub const EXIT_MISSING_RCWT_HEADER: i64 = 1002; + +// Common exit codes +pub const CCX_COMMON_EXIT_FILE_CREATION_FAILED: i64 = 5; +pub const CCX_COMMON_EXIT_UNSUPPORTED: i64 = 9; +pub const EXIT_NOT_ENOUGH_MEMORY: i64 = 500; +pub const CCX_COMMON_EXIT_BUG_BUG: i64 = 1000; + +// Define max width in characters/columns on the screen +pub const CCX_DECODER_608_SCREEN_ROWS: usize = 15; +pub const CCX_DECODER_608_SCREEN_WIDTH: usize = 32; + +//isdb specific codes +pub const CCX_DTVCC_MAX_SERVICES: usize = 63; +pub const CCX_DTVCC_MAX_WINDOWS: usize = 8; +pub const CCX_DTVCC_MAX_ROWS: usize = 15; +pub const CCX_DTVCC_SCREENGRID_COLUMNS: usize = 210; +pub const CCX_DTVCC_MAX_PACKET_LENGTH: usize = 128; +pub const CCX_DTVCC_SCREENGRID_ROWS: usize = 75; + +pub const CCX_MESSAGES_QUIET: i32 = 0; +pub const CCX_MESSAGES_STDOUT: i32 = 1; +pub const CCX_MESSAGES_STDERR: i32 = 2; diff --git a/src/rust/lib_ccxr/src/decoder_isdb/functions_common.rs b/src/rust/lib_ccxr/src/decoder_isdb/functions_common.rs new file mode 100644 index 000000000..d4c55e7a0 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_isdb/functions_common.rs @@ -0,0 +1,173 @@ +use crate::decoder_isdb::structs_xds::{CcSubtitle, CcxEncodingType, SubDataType, SubType}; + +/// # Safety +/// +/// - The caller must ensure that `sub` is a valid, mutable reference to a `CcSubtitle`. +/// - The `sub` parameter must point to a properly initialized `CcSubtitle` structure. +/// - The `info` and `mode` strings must not exceed 4 bytes in length; otherwise, they will be truncated. +/// - The function uses unsafe operations to manipulate pointers, so improper usage may result in undefined behavior. +pub fn add_cc_sub_text( + sub: &mut CcSubtitle, + str: &str, + start_time: i64, + end_time: i64, + info: &str, + mode: &str, + e_type: CcxEncodingType, +) -> i32 { + if str.is_empty() { + return 0; + } + + // Traverse to the last subtitle node if there are existing subtitles + let mut current_sub = sub; + while let Some(next_sub) = unsafe { current_sub.next.as_mut() } { + current_sub = next_sub; + } + + // Allocate a new subtitle node if needed + if current_sub.nb_data > 0 { + let new_sub = Box::new(CcSubtitle { + prev: current_sub as *mut CcSubtitle, + ..Default::default() + }); + + current_sub.next = Box::into_raw(new_sub); + + current_sub = unsafe { &mut *current_sub.next }; + } + + // Populate the subtitle fields + current_sub.subtype = SubType::Text; + current_sub.enc_type = e_type; + current_sub.data = Box::into_raw(Box::new(str.to_string())) as *mut std::ffi::c_void; + current_sub.datatype = SubDataType::Generic; + current_sub.nb_data = str.len() as u32; + current_sub.start_time = start_time; + current_sub.end_time = end_time; + + if !info.is_empty() { + current_sub.info[..info.len().min(4)] + .copy_from_slice(&info.as_bytes()[..info.len().min(4)]); + } + + if !mode.is_empty() { + current_sub.mode[..mode.len().min(4)] + .copy_from_slice(&mode.as_bytes()[..mode.len().min(4)]); + } + + current_sub.got_output = true; + current_sub.next = std::ptr::null_mut(); + + 0 +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::decoder_isdb::structs_xds::{CcSubtitle, CcxEncodingType, SubDataType, SubType}; + + #[test] + fn test_add_cc_sub_text_basic() { + let mut subtitle = CcSubtitle::default(); + let result = add_cc_sub_text( + &mut subtitle, + "Hello, world!", + 1000, + 2000, + "info", + "mode", + CcxEncodingType::Utf8, + ); + + assert_eq!(result, 0); + assert_eq!(subtitle.nb_data, 13); // "Hello, world!" has 13 characters + assert_eq!(subtitle.start_time, 1000); + assert_eq!(subtitle.end_time, 2000); + assert_eq!(subtitle.subtype, SubType::Text); + assert_eq!(subtitle.enc_type, CcxEncodingType::Utf8); + assert_eq!(subtitle.datatype, SubDataType::Generic); + assert!(subtitle.got_output); + } + + #[test] + fn test_add_cc_sub_text_empty_string() { + let mut subtitle = CcSubtitle::default(); + let result = add_cc_sub_text( + &mut subtitle, + "", + 1000, + 2000, + "info", + "mode", + CcxEncodingType::Utf8, + ); + + assert_eq!(result, 0); + assert_eq!(subtitle.nb_data, 0); + assert_eq!(subtitle.start_time, 0); + assert_eq!(subtitle.end_time, 0); + assert!(!subtitle.got_output); + } + + #[test] + fn test_add_cc_sub_text_truncated_info_and_mode() { + let mut subtitle = CcSubtitle::default(); + let result = add_cc_sub_text( + &mut subtitle, + "Test", + 1000, + 2000, + "longinfo", + "longmode", + CcxEncodingType::Utf8, + ); + + assert_eq!(result, 0); + assert_eq!(subtitle.nb_data, 4); // "Test" has 4 characters + assert_eq!(subtitle.start_time, 1000); + assert_eq!(subtitle.end_time, 2000); + assert_eq!(subtitle.info, *b"long"); // Only the first 4 bytes of "longinfo" are used + assert_eq!(&subtitle.mode[..4], b"long"); // Only the first 4 bytes of "longmode" are used + assert!(subtitle.got_output); + } + + #[test] + fn test_add_cc_sub_text_multiple_nodes() { + let mut subtitle = CcSubtitle::default(); + add_cc_sub_text( + &mut subtitle, + "First", + 1000, + 2000, + "info", + "mode", + CcxEncodingType::Utf8, + ); + + let result = add_cc_sub_text( + &mut subtitle, + "Second", + 3000, + 4000, + "info2", + "mode2", + CcxEncodingType::Utf8, + ); + + assert_eq!(result, 0); + + // Verify the first node + assert_eq!(subtitle.nb_data, 5); // "First" has 5 characters + assert_eq!(subtitle.start_time, 1000); + assert_eq!(subtitle.end_time, 2000); + assert!(subtitle.next.is_null() == false); + + // Verify the second node + let second_subtitle = unsafe { &*subtitle.next }; + assert_eq!(second_subtitle.nb_data, 6); // "Second" has 6 characters + assert_eq!(second_subtitle.start_time, 3000); + assert_eq!(second_subtitle.end_time, 4000); + assert!(second_subtitle.next.is_null()); + } +} diff --git a/src/rust/lib_ccxr/src/decoder_isdb/functions_isdb.rs b/src/rust/lib_ccxr/src/decoder_isdb/functions_isdb.rs new file mode 100644 index 000000000..36a59b735 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_isdb/functions_isdb.rs @@ -0,0 +1,1986 @@ +use crate::decoder_isdb::exit_codes::*; +use crate::decoder_isdb::functions_common::*; +use crate::decoder_isdb::structs_ccdecode::*; +use crate::decoder_isdb::structs_isdb::*; +use crate::decoder_isdb::structs_xds::*; + +use std::os::raw::c_char; + +use log::info; // use info!() for isdb_log and isdb_command_log + +pub fn is_horizontal_layout(format: WritingFormat) -> bool { + matches!( + format, + WritingFormat::HorizontalStdDensity + | WritingFormat::HorizontalHighDensity + | WritingFormat::HorizontalWesternLang + | WritingFormat::Horizontal1920x1080 + | WritingFormat::Horizontal960x540 + | WritingFormat::Horizontal720x480 + | WritingFormat::Horizontal1280x720 + | WritingFormat::HorizontalCustom + ) +} + +#[no_mangle] +pub fn layout_get_width(format: WritingFormat) -> u32 { + match format { + WritingFormat::Horizontal960x540 | WritingFormat::Vertical960x540 => 960, + _ => 720, + } +} + +#[no_mangle] +pub fn layout_get_height(format: WritingFormat) -> u32 { + match format { + WritingFormat::Horizontal960x540 | WritingFormat::Vertical960x540 => 540, + _ => 480, + } +} + +/// # Safety +/// +/// - The caller must ensure that `ls` is a valid, non-null pointer to an `ISDBSubLayout`. +/// - Improper usage of the pointer may result in undefined behavior. +pub unsafe fn init_layout(ls: *mut ISDBSubLayout) { + unsafe { + (*ls).font_size = 36; + (*ls).display_area.x = 0; + (*ls).display_area.y = 0; + (*ls).font_scale.fscx = 100; + (*ls).font_scale.fscy = 100; + } +} + +/// # Safety +/// +/// - The caller must ensure that `text` is a valid, mutable reference to an `ISDBText`. +/// - The function manipulates raw pointers and performs unsafe operations, so improper usage may result in undefined behavior. +pub fn reserve_buf(text: &mut ISDBText, len: usize) -> Result<(), &'static str> { + // Check if the current buffer length is sufficient + if text.len >= text.used + len { + return Ok(()); + } + + // Calculate the new buffer length (always in multiples of 128) + let blen = ((text.used + len + 127) >> 7) << 7; + + // Attempt to resize the buffer + let mut new_buf = Vec::with_capacity(blen); + new_buf.extend_from_slice(unsafe { std::slice::from_raw_parts(text.buf, text.used) }); + + // Update the buffer and its length + text.buf = new_buf.as_mut_ptr(); + text.len = blen; + + // Log the expansion + info!("expanded ctx->text({})", blen); + + Ok(()) +} + +/// # Safety +/// +/// - The caller must ensure that `str1` and `str2` are valid, null-terminated C-style strings. +/// - Passing invalid or null pointers will result in undefined behavior. +pub unsafe fn ccx_strstr_ignorespace(str1: *const c_char, str2: *const c_char) -> i32 { + unsafe { + let mut i = 0; + while *str2.add(i) != 0 { + if (*str2.add(i) as u8).is_ascii_whitespace() { + i += 1; + continue; + } + if *str2.add(i) != *str1.add(i) { + return 0; + } + i += 1; + } + 1 + } +} + +/// # Safety +/// +/// - The caller must ensure that `isdb_ctx` is a valid, mutable reference to an `Option>`. +/// - The function performs unsafe operations to free memory, so improper usage may result in undefined behavior. +pub fn delete_isdb_decoder(isdb_ctx: &mut Option>) { + if let Some(ctx) = isdb_ctx.as_mut() { + // Iterate over `text_list_head` and free its elements + while let Some(text) = unsafe { (ctx.text_list_head.next as *mut ISDBText).as_mut() } { + unsafe { + // Remove the element from the list + (*text.list.prev).next = text.list.next; + (*text.list.next).prev = text.list.prev; + } + // Free the buffer + if !text.buf.is_null() { + unsafe { drop(Box::from_raw(text.buf)) }; + } + // Free the text structure + unsafe { drop(Box::from_raw(text)) }; + } + + // Iterate over `buffered_text` and free its elements + while let Some(text) = unsafe { (ctx.buffered_text.next as *mut ISDBText).as_mut() } { + unsafe { + // Remove the element from the list + (*text.list.prev).next = text.list.next; + (*text.list.next).prev = text.list.prev; + } + // Free the buffer + if !text.buf.is_null() { + unsafe { drop(Box::from_raw(text.buf)) }; + } + // Free the text structure + unsafe { drop(Box::from_raw(text)) }; + } + + // Free the ISDBSubContext itself + *isdb_ctx = None; + } +} + +/// # Safety +/// +/// - This function is safe as it does not perform any unsafe operations. +/// - The caller is responsible for managing the lifetime of the returned `Box`. +pub fn init_isdb_decoder() -> Option> { + // Allocate memory for ISDBSubContext + let mut ctx = Box::new(ISDBSubContext { + text_list_head: ListHead { + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + }, + buffered_text: ListHead { + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + }, + prev_timestamp: u64::MAX, + current_state: ISDBSubState { + clut_high_idx: 0, + rollup_mode: 0, + ..Default::default() + }, + ..Default::default() + }); + + // Initialize the list heads + ctx.text_list_head.next = &mut ctx.text_list_head as *mut ListHead; + ctx.text_list_head.prev = &mut ctx.text_list_head as *mut ListHead; + ctx.buffered_text.next = &mut ctx.buffered_text as *mut ListHead; + ctx.buffered_text.prev = &mut ctx.buffered_text as *mut ListHead; + + // Initialize the layout state + unsafe { + init_layout(&mut ctx.current_state.layout_state); + } + + Some(ctx) +} + +/// # Safety +/// +/// - The caller must ensure that `ls` is a valid, mutable reference to an `ISDBSubLayout`. +/// - The function manipulates raw pointers and performs unsafe operations, so improper usage may result in undefined behavior. +pub fn allocate_text_node(ls: &mut ISDBSubLayout) -> Option> { + // assign ls to uls + let _uls = ls as *mut ISDBSubLayout; // ls unused + + // Allocate memory for the ISDBText structure + let mut text = Box::new(ISDBText { + buf: std::ptr::null_mut(), + len: 128, + used: 0, + pos: ISDBPos { x: 0, y: 0 }, + txt_tail: 0, + timestamp: 0, + refcount: 0, + list: ListHead { + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + }, + }); + + // Allocate memory for the buffer + let mut buf = vec![0u8; 128]; + text.buf = buf.as_mut_ptr() as *mut c_char; + std::mem::forget(buf); // Prevent Rust from deallocating the buffer + + Some(text) +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The function manipulates raw pointers and performs unsafe operations, so improper usage may result in undefined behavior. +pub fn append_char(ctx: &mut ISDBSubContext, ch: char) -> i32 { + let ls = &mut ctx.current_state.layout_state; + let mut text: Option<&mut ISDBText> = None; + + // Current Line Position + let cur_lpos; + // Space taken by character + let csp; + + if is_horizontal_layout(ls.format) { + cur_lpos = ls.cursor_pos.x; + csp = ls.font_size * ls.font_scale.fscx / 100; + } else { + cur_lpos = ls.cursor_pos.y; + csp = ls.font_size * ls.font_scale.fscy / 100; + } + + // Iterate over the text list + let mut current = ctx.text_list_head.next; + while let Some(current_text) = unsafe { (current as *mut ISDBText).as_mut() } { + // Text Line Position + let text_lpos = if is_horizontal_layout(ls.format) { + current_text.pos.x + } else { + current_text.pos.y + }; + + match text_lpos.cmp(&cur_lpos) { + std::cmp::Ordering::Equal => { + text = Some(current_text); + break; + } + std::cmp::Ordering::Greater => { + // Allocate Text here so that list is always sorted + let mut new_text = allocate_text_node(ls).expect("Failed to allocate text node"); + new_text.pos.x = ls.cursor_pos.x; + new_text.pos.y = ls.cursor_pos.y; + + // Add the new text node before the current one + unsafe { + (*new_text.list.prev).next = &mut new_text.list as *mut ListHead; + (*new_text.list.next).prev = &mut new_text.list as *mut ListHead; + } + + text = Some(&mut *Box::leak(new_text)); + break; + } + _ => {} + } + + current = current_text.list.next; + } + + if text.is_none() { + // If no matching text node was found, allocate a new one and add it to the tail + let mut new_text = allocate_text_node(ls).expect("Failed to allocate text node"); + new_text.pos.x = ls.cursor_pos.x; + new_text.pos.y = ls.cursor_pos.y; + + // Add the new text node to the tail + unsafe { + (*ctx.text_list_head.prev).next = &mut new_text.list as *mut ListHead; + (*new_text.list.prev).prev = &mut ctx.text_list_head as *mut ListHead; + } + + text = Some(Box::leak(new_text)); + } + + let text = text.unwrap(); + + // Check position of character and if moving backward then clean text + if is_horizontal_layout(ls.format) { + if ls.cursor_pos.y < text.pos.y { + text.pos.y = ls.cursor_pos.y; + text.used = 0; + } + ls.cursor_pos.y += csp; + text.pos.y += csp; + } else { + if ls.cursor_pos.y < text.pos.y { + text.pos.y = ls.cursor_pos.y; + text.used = 0; + } + ls.cursor_pos.x += csp; + text.pos.x += csp; + } + + // Reserve buffer space for the character + reserve_buf(text, 2).expect("Failed to reserve buffer space"); // +1 for terminating '\0' + + // Append the character to the buffer + unsafe { + *text.buf.add(text.used) = ch as i8; + text.used += 1; + *text.buf.add(text.used) = b'\0' as i8; + } + + 1 +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buffer` must be a valid, mutable slice with sufficient capacity to hold the text. +/// - The function manipulates raw pointers and performs unsafe operations, so improper usage may result in undefined behavior. +pub fn get_text(ctx: &mut ISDBSubContext, buffer: &mut [u8], len: usize) -> usize { + let ls = &mut ctx.current_state.layout_state; + let mut index = 0; + + if ctx.cfg_no_rollup != 0 || ctx.cfg_no_rollup == ctx.current_state.rollup_mode { + // If the buffered_text list is empty, copy text_list_head to buffered_text + if ctx.buffered_text.next == &mut ctx.buffered_text as *mut ListHead { + let mut current = ctx.text_list_head.next; + while let Some(text) = unsafe { (current as *mut ISDBText).as_mut() } { + let mut sb_text = allocate_text_node(ls).expect("Failed to allocate text node"); + unsafe { + // Add to buffered_text + sb_text.list.next = &mut ctx.buffered_text as *mut ListHead; + sb_text.list.prev = ctx.buffered_text.prev; + (*ctx.buffered_text.prev).next = &mut sb_text.list as *mut ListHead; + ctx.buffered_text.prev = &mut sb_text.list as *mut ListHead; + } + reserve_buf(&mut sb_text, text.used).expect("Failed to reserve buffer space"); + unsafe { + std::ptr::copy_nonoverlapping(text.buf, sb_text.buf, text.used); + *sb_text.buf.add(text.used) = b'\0' as i8; + } + sb_text.used = text.used; + + current = text.list.next; + } + return 0; + } + + // Update buffered_text for new entries in text_list_head + let mut current = ctx.text_list_head.next; + while let Some(text) = unsafe { (current as *mut ISDBText).as_mut() } { + let mut found = false; + let mut sb_current = ctx.buffered_text.next; + while let Some(sb_text) = unsafe { (sb_current as *mut ISDBText).as_mut() } { + if unsafe { ccx_strstr_ignorespace(text.buf, sb_text.buf) } != 0 { + found = true; + if unsafe { ccx_strstr_ignorespace(sb_text.buf, text.buf) } == 0 { + reserve_buf(sb_text, text.used).expect("Failed to reserve buffer space"); + unsafe { + std::ptr::copy_nonoverlapping(text.buf, sb_text.buf, text.used); + } + } + break; + } + sb_current = sb_text.list.next; + } + if !found { + let mut sb_text = allocate_text_node(ls).expect("Failed to allocate text node"); + unsafe { + // Add to buffered_text + sb_text.list.next = &mut ctx.buffered_text as *mut ListHead; + sb_text.list.prev = ctx.buffered_text.prev; + (*ctx.buffered_text.prev).next = &mut sb_text.list as *mut ListHead; + ctx.buffered_text.prev = &mut sb_text.list as *mut ListHead; + } + reserve_buf(&mut sb_text, text.used).expect("Failed to reserve buffer space"); + unsafe { + std::ptr::copy_nonoverlapping(text.buf, sb_text.buf, text.used); + *sb_text.buf.add(text.used) = b'\0' as i8; + } + sb_text.used = text.used; + } + current = text.list.next; + } + + // Flush buffered_text if text is not in text_list_head + let mut sb_current = ctx.buffered_text.next; + while sb_current != &mut ctx.buffered_text as *mut ListHead { + let sb_text = unsafe { &mut *(sb_current as *mut ISDBText) }; + let mut found = false; + let mut current = ctx.text_list_head.next; + while let Some(text) = unsafe { (current as *mut ISDBText).as_mut() } { + if unsafe { ccx_strstr_ignorespace(text.buf, sb_text.buf) } != 0 { + found = true; + break; + } + current = text.list.next; + } + if !found && len - index > sb_text.used + 2 && sb_text.used > 0 { + unsafe { + std::ptr::copy_nonoverlapping( + sb_text.buf as *mut u8, + buffer.as_mut_ptr().add(index), + sb_text.used, + ); + *buffer.as_mut_ptr().add(index + sb_text.used) = b'\n'; + *buffer.as_mut_ptr().add(index + sb_text.used + 1) = b'\r'; + } + index += sb_text.used + 2; + sb_text.used = 0; + + // Remove from buffered_text + unsafe { + (*sb_text.list.prev).next = sb_text.list.next; + (*sb_text.list.next).prev = sb_text.list.prev; + } + } + sb_current = sb_text.list.next; + } + } else { + // Copy text_list_head directly to buffer + let mut current = ctx.text_list_head.next; + while let Some(text) = unsafe { (current as *mut ISDBText).as_mut() } { + if len - index > text.used + 2 && text.used > 0 { + unsafe { + std::ptr::copy_nonoverlapping( + text.buf as *mut u8, + buffer.as_mut_ptr().add(index), + text.used, + ); + *buffer.as_mut_ptr().add(index + text.used) = b'\n'; + *buffer.as_mut_ptr().add(index + text.used + 1) = b'\r'; + } + index += text.used + 2; + text.used = 0; + unsafe { + *text.buf = b'\0' as i8; + } + } + current = text.list.next; + } + } + index +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `arg` slice must be valid and properly initialized. +/// - Improper usage of the context or slice may result in undefined behavior. +pub fn set_writing_format(ctx: &mut ISDBSubContext, arg: &[u8]) { + let ls = &mut ctx.current_state.layout_state; + + // One param means its initialization + if arg.get(1) == Some(&0x20) { + ls.format = match arg[0] & 0x0F { + 0 => WritingFormat::HorizontalStdDensity, + 1 => WritingFormat::VerticalStdDensity, + 2 => WritingFormat::HorizontalHighDensity, + 3 => WritingFormat::VerticalHighDensity, + 4 => WritingFormat::HorizontalWesternLang, + 5 => WritingFormat::Horizontal1920x1080, + 6 => WritingFormat::Vertical1920x1080, + 7 => WritingFormat::Horizontal960x540, + 8 => WritingFormat::Vertical960x540, + 9 => WritingFormat::Horizontal720x480, + 10 => WritingFormat::Vertical720x480, + 11 => WritingFormat::Horizontal1280x720, + 12 => WritingFormat::Vertical1280x720, + _ => WritingFormat::None, + }; + return; + } + + // P1 I1 P2 I2 P31 ~ P3i I3 P41 ~ P4j I4 F + if arg.get(1) == Some(&0x3B) { + ls.format = WritingFormat::HorizontalCustom; + let mut arg_iter = arg.iter().skip(2); + + if let Some(&value) = arg_iter.next() { + match value & 0x0F { + 0 => { + // ctx.font_size = SMALL_FONT_SIZE; + } + 1 => { + // ctx.font_size = MIDDLE_FONT_SIZE; + } + 2 => { + // ctx.font_size = STANDARD_FONT_SIZE; + } + _ => {} + } + } + + // P3 + info!("character numbers in one line in decimal:"); + for &value in arg_iter.by_ref() { + if value == 0x3B || value == 0x20 { + break; + } + ctx.nb_char = value as i32; + print!(" {:x}", value & 0x0F); + } + + if arg_iter.clone().next() == Some(&0x20) { + return; + } + + // P4 + info!("line numbers in decimal:"); + for &value in arg_iter { + if value == 0x20 { + break; + } + ctx.nb_line = value as i32; + print!(" {:x}", value & 0x0F); + } + } +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - Improper usage of the context may result in undefined behavior. +pub fn move_penpos(ctx: &mut ISDBSubContext, col: i32, row: i32) { + let ls = &mut ctx.current_state.layout_state; + + ls.cursor_pos.x = row; + ls.cursor_pos.y = col; +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - Improper usage of the context may result in undefined behavior. +pub fn set_position(ctx: &mut ISDBSubContext, p1: i32, p2: i32) { + let ls = &mut ctx.current_state.layout_state; + let (cw, ch, col, row); + + if is_horizontal_layout(ls.format) { + cw = (ls.font_size + ls.cell_spacing.col) * ls.font_scale.fscx / 100; + ch = (ls.font_size + ls.cell_spacing.row) * ls.font_scale.fscy / 100; + // Pen position is at bottom left + col = p2 * cw; + row = p1 * ch + ch; + } else { + cw = (ls.font_size + ls.cell_spacing.col) * ls.font_scale.fscy / 100; + ch = (ls.font_size + ls.cell_spacing.row) * ls.font_scale.fscx / 100; + // Pen position is at upper center, + // but in -90deg rotated coordinates, it is at middle left. + col = p2 * cw; + row = p1 * ch + ch / 2; + } + + move_penpos(ctx, col, row); +} + +/// # Safety +/// +/// - The caller must ensure that `q` is a valid slice of bytes. +/// - If `p1` or `p2` are provided, they must be valid, mutable references to `u32`. +/// - Improper usage of the slice or references may result in undefined behavior. +pub fn get_csi_params(q: &[u8], p1: Option<&mut u32>, p2: Option<&mut u32>) -> i32 { + let mut q_iter = q.iter(); + let mut q_pivot = 0; + + if let Some(p1_ref) = p1 { + *p1_ref = 0; + for &byte in q_iter.by_ref() { + if !(0x30..=0x39).contains(&byte) { + break; + } + *p1_ref *= 10; + *p1_ref += (byte - 0x30) as u32; + q_pivot += 1; + } + if let Some(&byte) = q_iter.next() { + q_pivot += 1; + if byte != 0x20 && byte != 0x3B { + return -1; + } + } else { + return -1; + } + } + + if let Some(p2_ref) = p2 { + *p2_ref = 0; + for &byte in q_iter { + if !(0x30..=0x39).contains(&byte) { + break; + } + *p2_ref *= 10; + *p2_ref += (byte - 0x30) as u32; + q_pivot += 1; + } + q_pivot += 1; // Account for the final increment after the loop + } + + q_pivot +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buf` slice must be valid and properly initialized. +/// - Improper usage of the context or slice may result in undefined behavior. +pub fn parse_csi(ctx: &mut ISDBSubContext, buf: &[u8]) -> usize { + let mut arg = [0u8; 10]; + let mut i = 0; + let ret; // should be mut and init to 0 + let mut p1 = 0; + let mut p2 = 0; + let _buf_pivot = buf; // Unused variable + let state = &mut ctx.current_state; + let ls = &mut state.layout_state; + + // Copy buf into arg + let mut buf_iter = buf.iter(); + for &byte in buf_iter.by_ref() { + if byte == 0x20 { + break; + } + if i >= arg.len() { + info!("Unexpected CSI {} >= {}", arg.len(), i); + break; + } + arg[i] = byte; + i += 1; + } + + // Ignore terminating 0x20 character + if let Some(&byte) = buf_iter.next() { + arg[i] = byte; + } + + // Process the CSI command + if let Some(&command) = buf_iter.next() { + match command { + command if command == CsiCommand::Swf as u8 => { + info!("Command: CSI: SWF"); + set_writing_format(ctx, &arg[..=i]); + } + command if command == CsiCommand::Ccc as u8 => { + info!("Command: CSI: CCC"); + ret = get_csi_params(&arg[..=i], Some(&mut p1), None); + if ret > 0 { + ls.ccc = + IsdbCCComposition::try_from(p1 as u8).unwrap_or(IsdbCCComposition::None); + } + } + command if command == CsiCommand::Sdf as u8 => { + ret = get_csi_params(&arg[..=i], Some(&mut p1), Some(&mut p2)); + if ret > 0 { + ls.display_area.w = p1 as i32; + ls.display_area.h = p2 as i32; + } + info!("Command: CSI: SDF (w: {}, h: {})", p1, p2); + } + command if command == CsiCommand::Ssm as u8 => { + ret = get_csi_params(&arg[..=i], Some(&mut p1), Some(&mut p2)); + if ret > 0 { + ls.font_size = p1 as i32; + } + info!("Command: CSI: SSM (x: {}, y: {})", p1, p2); + } + command if command == CsiCommand::Sdp as u8 => { + ret = get_csi_params(&arg[..=i], Some(&mut p1), Some(&mut p2)); + if ret > 0 { + ls.display_area.x = p1 as i32; + ls.display_area.y = p2 as i32; + } + info!("Command: CSI: SDP (x: {}, y: {})", p1, p2); + } + command if command == CsiCommand::Rcs as u8 => { + ret = get_csi_params(&arg[..=i], Some(&mut p1), None); + if ret > 0 { + ctx.current_state.raster_color = DEFAULT_CLUT + [(((ctx.current_state.clut_high_idx as u32) << 4) | p1) as usize]; + } + info!("Command: CSI: RCS ({})", p1); + } + command if command == CsiCommand::Shs as u8 => { + ret = get_csi_params(&arg[..=i], Some(&mut p1), None); + if ret > 0 { + ls.cell_spacing.col = p1 as i32; + } + info!("Command: CSI: SHS ({})", p1); + } + command if command == CsiCommand::Svs as u8 => { + ret = get_csi_params(&arg[..=i], Some(&mut p1), None); + if ret > 0 { + ls.cell_spacing.row = p1 as i32; + } + info!("Command: CSI: SVS ({})", p1); + } + command if command == CsiCommand::Acps as u8 => { + info!("Command: CSI: ACPS"); + ret = get_csi_params(&arg[..=i], Some(&mut p1), Some(&mut p2)); + if ret > 0 { + ls.acps[0] = p1 as i32; + ls.acps[1] = p2 as i32; + } + } + _ => { + info!("Command: CSI: Unknown command 0x{:x}", command); + } + } + } + + // Return the number of bytes processed + buf.len() - buf_iter.as_slice().len() +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buf` slice must be valid and properly initialized. +/// - Improper usage of the context or slice may result in undefined behavior. +pub fn parse_command(ctx: &mut ISDBSubContext, buf: &[u8]) -> usize { + let _buf_pivot = buf; // Unused variable + let mut buf_iter = buf.iter(); + let ret; // should be mut and init to 0 + + let code_lo = buf[0] & 0x0F; + let code_hi = (buf[0] & 0xF0) >> 4; + + let state = &mut ctx.current_state; + let ls = &mut state.layout_state; + + buf_iter.next(); // Move to the next byte + + match code_hi { + 0x00 => match code_lo { + 0x0 => info!("Command: NUL"), + 0x7 => info!("Command: BEL"), + 0x8 => info!("Command: ABP"), + 0x9 => info!("Command: APF"), + 0xA => info!("Command: APD"), + 0xB => info!("Command: APU"), + 0xC => info!("Command: CS clear Screen"), + 0xD => info!("Command: APR"), + 0xE => info!("Command: LS1"), + 0xF => info!("Command: LS0"), + _ => info!("Command: Unknown"), + }, + 0x01 => match code_lo { + 0x6 => info!("Command: PAPF"), + 0x8 => info!("Command: CAN"), + 0x9 => info!("Command: SS2"), + 0xB => info!("Command: ESC"), + 0xC => { + info!("Command: APS"); + if let (Some(&p1), Some(&p2)) = (buf_iter.next(), buf_iter.next()) { + set_position(ctx, (p1 & 0x3F) as i32, (p2 & 0x3F) as i32); + } + } + 0xD => info!("Command: SS3"), + 0xE => info!("Command: RS"), + 0xF => info!("Command: US"), + _ => info!("Command: Unknown"), + }, + 0x08 => match code_lo { + 0x0..=0x7 => { + info!( + "Command: Forground color (0x{:X})", + DEFAULT_CLUT[code_lo as usize] + ); + state.fg_color = DEFAULT_CLUT[code_lo as usize]; + } + 0x8 => { + info!("Command: SSZ"); + ls.font_scale.fscx = 50; + ls.font_scale.fscy = 50; + } + 0x9 => { + info!("Command: MSZ"); + ls.font_scale.fscx = 200; + ls.font_scale.fscy = 200; + } + 0xA => { + info!("Command: NSZ"); + ls.font_scale.fscx = 100; + ls.font_scale.fscy = 100; + } + 0xB => { + info!("Command: SZX"); + buf_iter.next(); + } + _ => info!("Command: Unknown"), + }, + 0x09 => match code_lo { + 0x0 => { + if let Some(&byte) = buf_iter.next() { + match byte & 0xF0 { + 0x20 => { + info!("Command: COL: Set Clut {}", byte & 0x0F); + ctx.current_state.clut_high_idx = byte & 0x0F; + } + 0x40 => { + info!( + "Command: COL: Set Forground 0x{:08X}", + DEFAULT_CLUT[(byte & 0x0F) as usize] + ); + ctx.current_state.fg_color = DEFAULT_CLUT[(byte & 0x0F) as usize]; + } + 0x50 => { + info!( + "Command: COL: Set Background 0x{:08X}", + DEFAULT_CLUT[(byte & 0x0F) as usize] + ); + ctx.current_state.bg_color = DEFAULT_CLUT[(byte & 0x0F) as usize]; + } + 0x60 => { + info!( + "Command: COL: Set half Forground 0x{:08X}", + DEFAULT_CLUT[(byte & 0x0F) as usize] + ); + ctx.current_state.hfg_color = DEFAULT_CLUT[(byte & 0x0F) as usize]; + } + 0x70 => { + info!( + "Command: COL: Set Half Background 0x{:08X}", + DEFAULT_CLUT[(byte & 0x0F) as usize] + ); + ctx.current_state.hbg_color = DEFAULT_CLUT[(byte & 0x0F) as usize]; + } + _ => {} + } + } + } + 0x1 => { + info!("Command: FLC"); + buf_iter.next(); + } + 0x2 => { + info!("Command: CDC"); + buf_iter.nth(2); // Skip 3 bytes + } + 0x3 => { + info!("Command: POL"); + buf_iter.next(); + } + 0x4 => { + info!("Command: WMM"); + buf_iter.nth(2); // Skip 3 bytes + } + 0x5 => { + info!("Command: MACRO"); + buf_iter.next(); + } + 0x7 => { + info!("Command: HLC"); + buf_iter.next(); + } + 0x8 => { + info!("Command: RPC"); + buf_iter.next(); + } + 0x9 => info!("Command: SPL"), + 0xA => info!("Command: STL"), + 0xB => { + ret = parse_csi(ctx, buf_iter.as_slice()); + buf_iter.nth(ret - 1); // Skip the parsed bytes + } + 0xD => { + info!("Command: TIME"); + buf_iter.nth(1); // Skip 2 bytes + } + _ => info!("Command: Unknown"), + }, + _ => {} + } + + buf.len() - buf_iter.as_slice().len() +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buf` slice must be valid and properly initialized. +/// - Improper usage of the context or slice may result in undefined behavior. +pub fn parse_caption_management_data(ctx: &mut ISDBSubContext, buf: &[u8]) -> usize { + let _buf_pivot = buf; // Unused variable + let mut buf_iter = buf.iter(); + + // Parse TMD + if let Some(&byte) = buf_iter.next() { + ctx.tmd = match byte >> 6 { + 0 => IsdbTmd::Free, + 1 => IsdbTmd::RealTime, + 2 => IsdbTmd::OffsetTime, + _ => IsdbTmd::Free, + }; + info!("CC MGMT DATA: TMD: {:?}", ctx.tmd); + + match ctx.tmd { + IsdbTmd::Free => { + info!("Playback time is not restricted to synchronize to the clock."); + } + IsdbTmd::OffsetTime => { + // + // This 36-bit field indicates offset time to add to the playback time when the + // clock control mode is in offset time mode. Offset time is coded in the + // order of hour, minute, second and millisecond, using nine 4-bit binary + // coded decimals (BCD). + // + // +-----------+-----------+---------+--------------+ + // | hour | minute | sec | millisecond | + // +-----------+-----------+---------+--------------+ + // | 2 (4bit) | 2 (4bit) | 2 (4bit)| 3 (4bit) | + // +-----------+-----------+---------+--------------+ + + // Parse offset time + if let ( + Some(&hour_byte), + Some(&min_byte), + Some(&sec_byte), + Some(&milli_byte1), + Some(&milli_byte2), + ) = ( + buf_iter.next(), + buf_iter.next(), + buf_iter.next(), + buf_iter.next(), + buf_iter.next(), + ) { + ctx.offset_time.hour = ((hour_byte >> 4) * 10 + (hour_byte & 0xF)) as i32; + ctx.offset_time.min = ((min_byte >> 4) * 10 + (min_byte & 0xF)) as i32; + ctx.offset_time.sec = ((sec_byte >> 4) * 10 + (sec_byte & 0xF)) as i32; + ctx.offset_time.milli = ((milli_byte1 >> 4) * 100 + + ((milli_byte1 & 0xF) * 10) + + (milli_byte2 & 0xF)) as i32; + + info!( + "CC MGMT DATA: OTD( h:{} m:{} s:{} millis:{} )", + ctx.offset_time.hour, + ctx.offset_time.min, + ctx.offset_time.sec, + ctx.offset_time.milli + ); + } + } + _ => { + info!( + "Playback time is in accordance with the time of the clock, \ + which is calibrated by clock signal (TDT). Playback time is \ + given by PTS." + ); + } + } + } + + // Parse number of languages + if let Some(&nb_lang) = buf_iter.next() { + ctx.nb_lang = nb_lang as i32; + info!("CC MGMT DATA: nb languages: {}", ctx.nb_lang); + } + + // Parse language data + for _ in 0..ctx.nb_lang { + if let Some(&lang_byte) = buf_iter.next() { + info!("CC MGMT DATA: {}", (lang_byte & 0x1F) >> 5); + ctx.dmf = lang_byte & 0x0F; + info!("CC MGMT DATA: DMF 0x{:X}", ctx.dmf); + + if ctx.dmf == 0xC || ctx.dmf == 0xD || ctx.dmf == 0xE { + if let Some(&dc_byte) = buf_iter.next() { + ctx.dc = dc_byte; + if ctx.dc == 0x00 { + info!("Attenuation Due to Rain"); + } + } + } + + if let (Some(&lang1), Some(&lang2), Some(&lang3)) = + (buf_iter.next(), buf_iter.next(), buf_iter.next()) + { + info!( + "CC MGMT DATA: languages: {}{}{}", + lang1 as char, lang2 as char, lang3 as char + ); + } + + if let Some(&format_byte) = buf_iter.next() { + info!("CC MGMT DATA: Format: 0x{:X}", format_byte >> 4); + info!("CC MGMT DATA: TCS: 0x{:X}", (format_byte >> 2) & 0x3); + ctx.current_state.rollup_mode = ((format_byte & 0x3) != 0) as i32; + info!( + "CC MGMT DATA: Rollup mode: 0x{:X}", + ctx.current_state.rollup_mode + ); + } + } + } + + buf.len() - buf_iter.as_slice().len() +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buf` slice must be valid and properly initialized. +/// - Improper usage of the context or slice may result in undefined behavior. +pub fn parse_statement(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { + let _buf_pivot = buf; // Unused variable + let mut buf_iter = buf.iter(); + let mut ret; + + while buf.len() - buf_iter.as_slice().len() < buf.len() { + if let Some(&byte) = buf_iter.next() { + let code = (byte & 0xF0) >> 4; + let code_lo = byte & 0x0F; + + if code <= 0x1 { + ret = parse_command(ctx, buf_iter.as_slice()); + } + // Special case *1(SP) + else if code == 0x2 && code_lo == 0x0 { + ret = append_char(ctx, byte as char) as usize; + } + // Special case *3(DEL) + else if code == 0x7 && code_lo == 0xF { + // TODO: DEL should have block in fg color + ret = append_char(ctx, byte as char) as usize; + } else if code <= 0x7 { + ret = append_char(ctx, byte as char) as usize; + } else if code <= 0x9 { + ret = parse_command(ctx, buf_iter.as_slice()); + } + // Special case *2(10/0) + else if (code == 0xA && code_lo == 0x0) || (code == 0x0F && code_lo == 0xF) { + // TODO: handle + ret = 1; // Placeholder for handling + } else { + ret = append_char(ctx, byte as char) as usize; + } + + // No need to check for `ret < 0` as `ret` is of type `usize` + // if ret < 0 { + // break; + // } + + buf_iter.nth(ret - 1); // Move the iterator forward by `ret` bytes + } + } + + 0 +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buf` slice must be valid and properly initialized. +/// - Improper usage of the context or slice may result in undefined behavior. +pub fn parse_data_unit(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { + let mut buf_iter = buf.iter(); + + // Skip the unit separator + buf_iter.next(); + + // Parse unit parameter + let unit_parameter = if let Some(&byte) = buf_iter.next() { + byte + } else { + return 0; // Return early if no data + }; + + // Parse length (RB24 equivalent) + if let (Some(&b1), Some(&b2), Some(&b3)) = (buf_iter.next(), buf_iter.next(), buf_iter.next()) { + let _len = ((b1 as u32) << 16) | ((b2 as u32) << 8) | (b3 as u32); + // Use `_len` if needed or keep it as a placeholder to suppress the warning + } else { + return 0; // Return early if length cannot be parsed + } + + // Process based on unit parameter + match unit_parameter { + 0x20 => { + parse_statement(ctx, buf_iter.as_slice()); + } + _ => { + // Handle other cases if needed + } + } + + 0 +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buf` slice must be valid and properly initialized. +/// - The `sub` parameter must be a valid, mutable reference to a `CcSubtitle`. +/// - Improper usage of the context, slice, or subtitle may result in undefined behavior. +pub fn parse_caption_statement_data( + ctx: &mut ISDBSubContext, + lang_id: i32, + buf: &[u8], + sub: &mut CcSubtitle, +) -> i32 { + // assinn land_id to ulang_id + let _ulang_id = lang_id; // Unused variable + + let mut buf_iter = buf.iter(); + let mut buffer = [0u8; 1024]; + + // Parse TMD + let tmd = if let Some(&byte) = buf_iter.next() { + byte >> 6 + } else { + return -1; + }; + + // Skip timing data if TMD is 1 or 2 + if tmd == 1 || tmd == 2 { + buf_iter.nth(4); // Skip 5 bytes + } + + // Parse length (RB24 equivalent) + let _len = if let (Some(&b1), Some(&b2), Some(&b3)) = // len unused + (buf_iter.next(), buf_iter.next(), buf_iter.next()) + { + ((b1 as u32) << 16) | ((b2 as u32) << 8) | (b3 as u32) + } else { + return -1; + }; + + // Parse data unit + let ret = parse_data_unit(ctx, buf_iter.as_slice()); + if ret < 0 { + return -1; + } + + // Get text from the context + let ret = get_text(ctx, &mut buffer, 1024); + + // no need to check for `ret < 0` as `ret` is of type `usize` + // if ret < 0 { + // return CCX_OK as i32; + // } + + // Copy data if present in the buffer + if ret > 0 { + add_cc_sub_text( + sub, + std::str::from_utf8(&buffer[..ret]).unwrap_or(""), + ctx.prev_timestamp as i64, + ctx.timestamp as i64, + "NA", + "ISDB", + CcxEncodingType::Utf8, + ); + if sub.start_time == sub.end_time { + sub.end_time += 2; + } + ctx.prev_timestamp = ctx.timestamp; + } + + 0 +} + +/// # Safety +/// +/// - The caller must ensure that `codec_ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - The `buf` slice must be valid and properly initialized. +/// - The `sub` parameter must be a valid, mutable reference to a `CcSubtitle`. +/// - Improper usage of the context, slice, or subtitle may result in undefined behavior. +pub fn isdb_parse_data_group( + codec_ctx: &mut ISDBSubContext, + buf: &[u8], + sub: &mut CcSubtitle, +) -> i32 { + let _buf_pivot = buf; // Unused variable + let mut buf_iter = buf.iter(); + + // Parse ID + let id = if let Some(&byte) = buf_iter.next() { + byte >> 2 + } else { + return -1; + }; + + // Debug variables (unused in release mode) + #[cfg(debug_assertions)] + let _version = id & 2; + + // Skip unused bytes + buf_iter.nth(2); + + // Parse group size (RB16 equivalent) + let group_size = if let (Some(&b1), Some(&b2)) = (buf_iter.next(), buf_iter.next()) { + u16::from_be_bytes([b1, b2]) as usize + } else { + return -1; + }; + + info!("ISDB (Data group) group_size {}", group_size); + + // Check and update timestamps + if codec_ctx.prev_timestamp > codec_ctx.timestamp { + codec_ctx.prev_timestamp = codec_ctx.timestamp; + } + + // Process based on ID + let _ret = match id & 0x0F { + // ret is unused variable + 0 => { + // Caption management + parse_caption_management_data(codec_ctx, buf_iter.as_slice()) + } + 1..=7 => { + // Caption statement data + info!("ISDB {} language", id & 0x0F); + parse_caption_statement_data(codec_ctx, (id & 0x0F) as i32, buf_iter.as_slice(), sub) + as usize + } + _ => { + // Not allowed in spec + info!("ISDB: Invalid ID in data group"); + 0 + } + }; + + // no need to check for `ret < 0` as `ret` is of type `usize` + // if ret < 0 { + // return -1; + // } + + // Skip processed group size + buf_iter.nth(group_size - 1); + + // Skip CRC (2 bytes) + buf_iter.nth(1); + + (buf.len() - buf_iter.as_slice().len()) as i32 +} + +/// # Safety +/// +/// - The caller must ensure that `dec_ctx` is a valid, mutable reference to a `LibCcDecode`. +/// - The `buf` slice must be valid and properly initialized. +/// - The `sub` parameter must be a valid, mutable reference to a `CcSubtitle`. +/// - Improper usage of the context, slice, or subtitle may result in undefined behavior. +pub fn isdbsub_decode(dec_ctx: &mut LibCcDecode, buf: &[u8], sub: &mut CcSubtitle) -> i32 { + let mut buf_iter = buf.iter(); + + // Check for synchronization byte + if let Some(&byte) = buf_iter.next() { + if byte != 0x80 { + info!("\nNot a Synchronized PES\n"); + return -1; + } + } else { + return -1; + } + + // Skip private data stream (0xFF) + buf_iter.next(); + + // Parse header end + let header_end = if let Some(&byte) = buf_iter.next() { + buf_iter.as_slice().len() - (byte & 0x0F) as usize + } else { + return -1; + }; + + buf_iter.next(); // Skip one more byte + + // Skip header bytes + while buf_iter.as_slice().len() > header_end { + buf_iter.next(); + } + + // Set rollup configuration + if let Some(ctx) = unsafe { (dec_ctx.private_data as *mut ISDBSubContext).as_mut() } { + ctx.cfg_no_rollup = dec_ctx.no_rollup; + + // Parse data group + let ret = isdb_parse_data_group(ctx, buf_iter.as_slice(), sub); + if ret < 0 { + return -1; + } + } + + 1 +} + +/// # Safety +/// +/// - The caller must ensure that `ctx` is a valid, mutable reference to an `ISDBSubContext`. +/// - Improper usage of the context may result in undefined behavior. +pub fn isdb_set_global_time(ctx: &mut ISDBSubContext, timestamp: u64) -> i32 { + ctx.timestamp = timestamp; + CCX_OK as i32 +} + +#[cfg(test)] +mod tests { + use super::*; + + use std::ffi::c_char; + use std::ffi::CString; + + #[test] + fn test_is_horizontal_layout() { + assert!(is_horizontal_layout(WritingFormat::HorizontalStdDensity)); + assert!(is_horizontal_layout(WritingFormat::HorizontalHighDensity)); + assert!(is_horizontal_layout(WritingFormat::HorizontalWesternLang)); + assert!(is_horizontal_layout(WritingFormat::Horizontal1920x1080)); + assert!(!is_horizontal_layout(WritingFormat::Vertical960x540)); + assert!(!is_horizontal_layout(WritingFormat::Vertical720x480)); + } + + #[test] + fn test_layout_get_width() { + assert_eq!(layout_get_width(WritingFormat::Horizontal960x540), 960); + assert_eq!(layout_get_width(WritingFormat::Vertical960x540), 960); + assert_eq!(layout_get_width(WritingFormat::Horizontal720x480), 720); + assert_eq!(layout_get_width(WritingFormat::Vertical720x480), 720); + } + + #[test] + fn test_layout_get_height() { + assert_eq!(layout_get_height(WritingFormat::Horizontal960x540), 540); + assert_eq!(layout_get_height(WritingFormat::Vertical960x540), 540); + assert_eq!(layout_get_height(WritingFormat::Horizontal720x480), 480); + assert_eq!(layout_get_height(WritingFormat::Vertical720x480), 480); + } + + #[test] + fn test_init_layout() { + let mut layout = ISDBSubLayout { + font_size: 0, + display_area: Default::default(), + font_scale: Default::default(), + cursor_pos: Default::default(), + format: WritingFormat::None, + cell_spacing: Default::default(), + ccc: Default::default(), + acps: [0; 2], + }; + + unsafe { + init_layout(&mut layout); + } + + assert_eq!(layout.font_size, 36); + assert_eq!(layout.display_area.x, 0); + assert_eq!(layout.display_area.y, 0); + assert_eq!(layout.font_scale.fscx, 100); + assert_eq!(layout.font_scale.fscy, 100); + } + + #[test] + fn test_ccx_strstr_ignorespace_exact_match() { + let str1 = CString::new("hello").unwrap(); + let str2 = CString::new("hello").unwrap(); + + unsafe { + assert_eq!(ccx_strstr_ignorespace(str1.as_ptr(), str2.as_ptr()), 1); + } + } + + #[test] + fn test_ccx_strstr_ignorespace_no_match() { + let str1 = CString::new("hello").unwrap(); + let str2 = CString::new("world").unwrap(); + + unsafe { + assert_eq!(ccx_strstr_ignorespace(str1.as_ptr(), str2.as_ptr()), 0); + } + } + + #[test] + fn test_ccx_strstr_ignorespace_empty_strings() { + let str1 = CString::new("").unwrap(); + let str2 = CString::new("").unwrap(); + + unsafe { + assert_eq!(ccx_strstr_ignorespace(str1.as_ptr(), str2.as_ptr()), 1); + } + } + + #[test] + fn test_ccx_strstr_ignorespace_spaces_only() { + let str1 = CString::new(" ").unwrap(); + let str2 = CString::new("").unwrap(); + + unsafe { + assert_eq!(ccx_strstr_ignorespace(str1.as_ptr(), str2.as_ptr()), 1); + } + } + + #[test] + fn test_ccx_strstr_ignorespace_mismatched_lengths() { + let str1 = CString::new("hello").unwrap(); + let str2 = CString::new("hello world").unwrap(); + + unsafe { + assert_eq!(ccx_strstr_ignorespace(str1.as_ptr(), str2.as_ptr()), 0); + } + } + + #[test] + fn test_init_isdb_decoder_success() { + // Call the function to initialize the decoder + let isdb_ctx = init_isdb_decoder(); + + // Ensure the context is initialized + assert!(isdb_ctx.is_some()); + + // Verify the initialized values + let ctx = isdb_ctx.unwrap(); + assert_eq!(ctx.prev_timestamp, u64::MAX); + assert_eq!(ctx.current_state.clut_high_idx, 0); + assert_eq!(ctx.current_state.rollup_mode, 0); + + // Verify the list heads are properly initialized + assert_eq!( + ctx.text_list_head.next, + &ctx.text_list_head as *const _ as *mut _ + ); + assert_eq!( + ctx.text_list_head.prev, + &ctx.text_list_head as *const _ as *mut _ + ); + assert_eq!( + ctx.buffered_text.next, + &ctx.buffered_text as *const _ as *mut _ + ); + assert_eq!( + ctx.buffered_text.prev, + &ctx.buffered_text as *const _ as *mut _ + ); + + // Verify the layout state is initialized + let layout = &ctx.current_state.layout_state; + assert_eq!(layout.font_size, 36); + assert_eq!(layout.display_area.x, 0); + assert_eq!(layout.display_area.y, 0); + assert_eq!(layout.font_scale.fscx, 100); + assert_eq!(layout.font_scale.fscy, 100); + } + + #[test] + fn test_init_isdb_decoder_no_crash() { + // Ensure the function does not crash when called multiple times + let _ctx1 = init_isdb_decoder(); + let _ctx2 = init_isdb_decoder(); + } + + #[test] + fn test_allocate_text_node_buffer_allocation() { + // Create a mock ISDBSubLayout + let mut layout = ISDBSubLayout { + font_size: 36, + display_area: DispArea { + x: 0, + y: 0, + w: 0, + h: 0, + }, + font_scale: FScale { + fscx: 100, + fscy: 100, + }, + cursor_pos: ISDBPos { x: 0, y: 0 }, + format: WritingFormat::HorizontalStdDensity, + cell_spacing: Spacing { col: 0, row: 0 }, + ccc: IsdbCCComposition::None, + acps: [0; 2], + }; + + // Call the function to allocate a text node + let mut text_node = allocate_text_node(&mut layout).unwrap(); + + // Allocate memory for the buffer + let mut buf = vec![0u8; 128]; + text_node.buf = buf.as_mut_ptr() as *mut c_char; + std::mem::forget(buf); // Prevent Rust from deallocating the buffer + + // Verify the buffer is allocated + assert!(!text_node.buf.is_null()); + } + + #[test] + fn test_set_writing_format_invalid_format() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + format: WritingFormat::None, + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Call set_writing_format with an invalid format argument + set_writing_format(&mut ctx, &[0xFF, 0x20]); + + // Verify the writing format remains None + assert_eq!(ctx.current_state.layout_state.format, WritingFormat::None); + } + + #[test] + fn test_move_penpos_horizontal_layout() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + cursor_pos: ISDBPos { x: 0, y: 0 }, + format: WritingFormat::HorizontalStdDensity, + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Move the pen position + move_penpos(&mut ctx, 10, 20); + + // Verify the pen position is updated correctly + assert_eq!(ctx.current_state.layout_state.cursor_pos.x, 20); + assert_eq!(ctx.current_state.layout_state.cursor_pos.y, 10); + } + + #[test] + fn test_move_penpos_vertical_layout() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + cursor_pos: ISDBPos { x: 0, y: 0 }, + format: WritingFormat::Vertical960x540, + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Move the pen position + move_penpos(&mut ctx, 15, 25); + + // Verify the pen position is updated correctly + assert_eq!(ctx.current_state.layout_state.cursor_pos.x, 25); + assert_eq!(ctx.current_state.layout_state.cursor_pos.y, 15); + } + + #[test] + fn test_move_penpos_no_change() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + cursor_pos: ISDBPos { x: 5, y: 5 }, + format: WritingFormat::HorizontalStdDensity, + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Move the pen position to the same coordinates + move_penpos(&mut ctx, 5, 5); + + // Verify the pen position remains unchanged + assert_eq!(ctx.current_state.layout_state.cursor_pos.x, 5); + assert_eq!(ctx.current_state.layout_state.cursor_pos.y, 5); + } + + #[test] + fn test_get_csi_params_invalid_input() { + let input = b"123x"; + let mut p1 = 0; + + let result = get_csi_params(input, Some(&mut p1), None); + + // Verify the result + assert_eq!(result, -1); // Invalid character 'x' + } + + #[test] + fn test_get_csi_params_partial_input() { + let input = b"123;"; + let mut p1 = 0; + let mut p2 = 0; + + let result = get_csi_params(input, Some(&mut p1), Some(&mut p2)); + + // Verify the result + assert_eq!(result, -1); // Missing second parameter + } + + #[test] + fn test_parse_command_nul() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Input buffer for the NUL command + let buf = b"\x00"; + + // Call parse_command + let result = parse_command(&mut ctx, buf); + + // Verify the command is processed + assert_eq!(result, 1); // NUL command processes 1 byte + } + + #[test] + fn test_parse_command_apf() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + cursor_pos: ISDBPos { x: 0, y: 0 }, + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Input buffer for the APF (Advance Pen Forward) command + let buf = b"\x09"; + + // Call parse_command + let result = parse_command(&mut ctx, buf); + + // Verify the command is processed + assert_eq!(result, 1); // APF command processes 1 byte + } + + #[test] + fn test_parse_command_ssz() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + font_scale: FScale { + fscx: 100, + fscy: 100, + }, + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Input buffer for the SSZ (Small Size) command + let buf = b"\x88"; + + // Call parse_command + let result = parse_command(&mut ctx, buf); + + // Verify the font scale is updated + assert_eq!(result, 1); // SSZ command processes 1 byte + assert_eq!(ctx.current_state.layout_state.font_scale.fscx, 50); + assert_eq!(ctx.current_state.layout_state.font_scale.fscy, 50); + } + + #[test] + fn test_parse_command_unknown() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Input buffer for an unknown command + let buf = b"\xFF"; + + // Call parse_command + let result = parse_command(&mut ctx, buf); + + // Verify the command is processed but does not modify the context + assert_eq!(result, 1); // Unknown command processes 1 byte + } + + #[test] + fn test_parse_caption_management_data_free_mode() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + tmd: IsdbTmd::Free, + nb_lang: 0, + ..Default::default() + }; + + // Input buffer for free mode + let buf = b"\x00\x01"; + + // Call parse_caption_management_data + let result = parse_caption_management_data(&mut ctx, buf); + + // Verify the TMD and number of languages + assert_eq!(result, buf.len()); + assert_eq!(ctx.tmd, IsdbTmd::Free); + assert_eq!(ctx.nb_lang, 1); + } + + #[test] + fn test_parse_caption_management_data_languages() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + nb_lang: 0, + dmf: 0, + dc: 0, + ..Default::default() + }; + + // Input buffer with language data + let buf = b"\x00\x02\xC1\x45\x4E\x47\x10"; + + // Call parse_caption_management_data + let result = parse_caption_management_data(&mut ctx, buf); + + // Verify the number of languages and language details + assert_eq!(result, buf.len()); + assert_eq!(ctx.nb_lang, 2); + assert_eq!(ctx.dmf, 0x01); + assert_eq!(ctx.dc, 0); + } + + #[test] + fn test_parse_caption_management_data_invalid_buffer() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + tmd: IsdbTmd::Free, + nb_lang: 0, + ..Default::default() + }; + + // Input buffer with insufficient data + let buf = b"\x80"; + + // Call parse_caption_management_data + let result = parse_caption_management_data(&mut ctx, buf); + + // Verify that the function processes only the available data + assert_eq!(result, buf.len()); + assert_eq!(ctx.tmd, IsdbTmd::OffsetTime); + assert_eq!(ctx.nb_lang, 0); // No languages parsed + } + + #[test] + fn test_parse_statement_unknown_code() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + current_state: ISDBSubState { + layout_state: ISDBSubLayout { + ..Default::default() + }, + ..Default::default() + }, + ..Default::default() + }; + + // Input buffer for an unknown code + let buf = b"\xFF"; + + // Call parse_statement + let result = parse_statement(&mut ctx, buf); + + // Verify the unknown code is processed without errors + assert_eq!(result, 0); // `parse_statement` returns 0 on success + } + + #[test] + fn test_parse_data_unit_empty_buffer() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + ..Default::default() + }; + + // Input buffer with no data + let buf = b""; + + // Call parse_data_unit + let result = parse_data_unit(&mut ctx, buf); + + // Verify the function handles an empty buffer gracefully + assert_eq!(result, 0); // No data to process + } + + #[test] + fn test_parse_data_unit_invalid_length() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + ..Default::default() + }; + + // Input buffer with an invalid length (less than 3 bytes for RB24) + let buf = b"\x1F\x20\x00"; + + // Call parse_data_unit + let result = parse_data_unit(&mut ctx, buf); + + // Verify the function handles invalid length gracefully + assert_eq!(result, 0); // No valid data to process + } + + #[test] + fn test_parse_data_unit_unknown_unit_parameter() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + ..Default::default() + }; + + // Input buffer with an unknown unit parameter + let buf = b"\x1F\xFF\x00\x00\x00"; + + // Call parse_data_unit + let result = parse_data_unit(&mut ctx, buf); + + // Verify the function processes the unknown unit parameter without errors + assert_eq!(result, 0); // Unknown unit parameter is ignored + } + + #[test] + fn test_parse_caption_statement_data_invalid_buffer() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + tmd: IsdbTmd::Free, + ..Default::default() + }; + + // Create a mock CcSubtitle + let mut sub = CcSubtitle { + ..Default::default() + }; + + // Input buffer with insufficient data + let buf = b"\x80"; + + // Call parse_caption_statement_data + let result = parse_caption_statement_data(&mut ctx, 1, buf, &mut sub); + + // Verify the function handles the invalid buffer gracefully + assert_eq!(result, -1); + } + + #[test] + fn test_isdb_parse_data_group_insufficient_data() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + ..Default::default() + }; + + // Create a mock CcSubtitle + let mut sub = CcSubtitle { + ..Default::default() + }; + + // Input buffer with insufficient data + let buf = b"\x00"; + + // Call isdb_parse_data_group + let result = isdb_parse_data_group(&mut ctx, buf, &mut sub); + + // Verify the function handles insufficient data gracefully + assert_eq!(result, -1); + } + + #[test] + fn test_isdbsub_decode_invalid_sync_byte() { + // Create a mock LibCcDecode + let mut dec_ctx = LibCcDecode { + private_data: Box::into_raw(Box::new(ISDBSubContext { + cfg_no_rollup: 0, + ..Default::default() + })) as *mut _, + no_rollup: 1, + ..Default::default() + }; + + // Create a mock CcSubtitle + let mut sub = CcSubtitle { + ..Default::default() + }; + + // Input buffer with an invalid synchronization byte + let buf = b"\x00\xFF\x00\x00\x00\x00\x00\x00"; + + // Call isdbsub_decode + let result = isdbsub_decode(&mut dec_ctx, buf, &mut sub); + + // Verify the function returns an error + assert_eq!(result, -1); + } + + #[test] + fn test_isdbsub_decode_empty_buffer() { + // Create a mock LibCcDecode + let mut dec_ctx = LibCcDecode { + private_data: Box::into_raw(Box::new(ISDBSubContext { + cfg_no_rollup: 0, + ..Default::default() + })) as *mut _, + no_rollup: 1, + ..Default::default() + }; + + // Create a mock CcSubtitle + let mut sub = CcSubtitle { + ..Default::default() + }; + + // Input buffer with no data + let buf = b""; + + // Call isdbsub_decode + let result = isdbsub_decode(&mut dec_ctx, buf, &mut sub); + + // Verify the function returns an error + assert_eq!(result, -1); + } + + #[test] + fn test_isdb_set_global_time_valid_timestamp() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + timestamp: 0, + ..Default::default() + }; + + // Set a valid timestamp + let result = isdb_set_global_time(&mut ctx, 123456789); + + // Verify the timestamp is updated correctly + assert_eq!(result, CCX_OK as i32); + assert_eq!(ctx.timestamp, 123456789); + } + + #[test] + fn test_isdb_set_global_time_zero_timestamp() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + timestamp: 987654321, + ..Default::default() + }; + + // Set the timestamp to zero + let result = isdb_set_global_time(&mut ctx, 0); + + // Verify the timestamp is updated correctly + assert_eq!(result, CCX_OK as i32); + assert_eq!(ctx.timestamp, 0); + } + + #[test] + fn test_isdb_set_global_time_large_timestamp() { + // Create a mock ISDBSubContext + let mut ctx = ISDBSubContext { + timestamp: 0, + ..Default::default() + }; + + // Set a large timestamp + let result = isdb_set_global_time(&mut ctx, u64::MAX); + + // Verify the timestamp is updated correctly + assert_eq!(result, CCX_OK as i32); + assert_eq!(ctx.timestamp, u64::MAX); + } +} diff --git a/src/rust/lib_ccxr/src/decoder_isdb/mod.rs b/src/rust/lib_ccxr/src/decoder_isdb/mod.rs new file mode 100644 index 000000000..70b098088 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_isdb/mod.rs @@ -0,0 +1,6 @@ +pub mod exit_codes; +pub mod functions_common; +pub mod functions_isdb; +pub mod structs_ccdecode; +pub mod structs_isdb; +pub mod structs_xds; diff --git a/src/rust/lib_ccxr/src/decoder_isdb/structs_ccdecode.rs b/src/rust/lib_ccxr/src/decoder_isdb/structs_ccdecode.rs new file mode 100644 index 000000000..807b1ff15 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_isdb/structs_ccdecode.rs @@ -0,0 +1,281 @@ +use crate::decoder_isdb::exit_codes::*; +use crate::decoder_isdb::structs_xds::XdsBuffer; + +use crate::time::Timestamp; + +use std::ptr::null_mut; + +pub struct LibCcDecode { + pub cc_stats: [i32; 4], + pub saw_caption_block: i32, + pub processed_enough: i32, // If 1, we have enough lines, time, etc. + + /* 608 contexts - note that this shouldn't be global, they should be + * per program */ + pub context_cc608_field_1: *mut std::ffi::c_void, + pub context_cc608_field_2: *mut std::ffi::c_void, + + pub no_rollup: i32, // If 1, write one line at a time + pub noscte20: i32, + pub fix_padding: i32, // Replace 0000 with 8080 in HDTV (needed for some cards) + pub write_format: crate::common::OutputFormat, // 0 = Raw, 1 = srt, 2 = SMI + pub extraction_start: Option, + pub extraction_end: Option, // Segment we actually process + pub subs_delay: i64, // ms to delay (or advance) subs + pub extract: i32, // Extract 1st, 2nd or both fields + pub fullbin: i32, // Disable pruning of padding cc blocks + // TODO when cc_subtitle completed + // pub dec_sub: cc_subtitle, + pub in_bufferdatatype: crate::common::BufferdataType, + pub hauppauge_mode: u32, // If 1, use PID=1003, process specially and so on + + pub frames_since_last_gop: i32, + /* GOP-based timing */ + pub saw_gop_header: i32, + /* Time info for timed-transcript */ + pub max_gop_length: i32, // (Maximum) length of a group of pictures + pub last_gop_length: i32, // Length of the previous group of pictures + pub total_pulldownfields: u32, + pub total_pulldownframes: u32, + pub program_number: i32, + pub list: HList, + pub timing: *mut crate::time::TimingContext, + pub codec: crate::common::Codec, // Can also be SelectCodec + + // Set to true if data is buffered + pub has_ccdata_buffered: i32, + pub is_alloc: i32, + + pub avc_ctx: *mut AvcCtx, + pub private_data: *mut std::ffi::c_void, + + /* General video information */ + pub current_hor_size: u32, + pub current_vert_size: u32, + pub current_aspect_ratio: u32, + pub current_frame_rate: u32, // Assume standard fps, 29.97 + + /* Required in es_function.c */ + pub no_bitstream_error: i32, + pub saw_seqgoppic: i32, + pub in_pic_data: i32, + + pub current_progressive_sequence: u32, + pub current_pulldownfields: u32, + + pub temporal_reference: i32, + pub picture_coding_type: crate::common::FrameType, + pub num_key_frames: u32, + pub picture_structure: u32, + pub repeat_first_field: u32, + pub progressive_frame: u32, + pub pulldownfields: u32, + + /* Required in es_function.c and es_userdata.c */ + pub top_field_first: u32, // Needs to be global + + /* Stats. Modified in es_userdata.c */ + pub stat_numuserheaders: i32, + pub stat_dvdccheaders: i32, + pub stat_scte20ccheaders: i32, + pub stat_replay5000headers: i32, + pub stat_replay4000headers: i32, + pub stat_dishheaders: i32, + pub stat_hdtv: i32, + pub stat_divicom: i32, + pub false_pict_header: i32, + // TODO when 708 completed + // pub dtvcc: *mut DtvccCtx, + pub current_field: i32, + + // Analyse/use the picture information + pub maxtref: i32, // Use to remember the temporal reference number + + pub cc_data_count: [i32; SORTBUF], + // Store fts; + pub cc_fts: [i64; SORTBUF], + // Store HD CC packets + pub cc_data_pkts: [[u8; 10 * 31 * 3 + 1]; SORTBUF], // *10, because MP4 seems to have different limits + + // The sequence number of the current anchor frame. All currently read + // B-Frames belong to this I- or P-frame. + pub anchor_seq_number: i32, + pub xds_ctx: *mut XdsContext, + // TODO when vbi completed + // pub vbi_decoder: *mut CcxDecoderVbiCtx, + + // TODO when cc_subtitle completed + // pub writedata: Option< + // extern "C" fn( + // data: *const u8, + // length: i32, + // private_data: *mut std::ffi::c_void, + // sub: *mut cc_subtitle, + // ) -> i32, + // >, + + // DVB subtitle related + pub ocr_quantmode: i32, + pub prev: *mut LibCcDecode, +} +impl Default for LibCcDecode { + fn default() -> Self { + LibCcDecode { + cc_stats: [0; 4], + saw_caption_block: 0, + processed_enough: 0, + context_cc608_field_1: std::ptr::null_mut(), + context_cc608_field_2: std::ptr::null_mut(), + no_rollup: 0, + noscte20: 0, + fix_padding: 0, + write_format: crate::common::OutputFormat::Raw, + extraction_start: None, + extraction_end: None, + subs_delay: 0, + extract: 0, + fullbin: 0, + in_bufferdatatype: crate::common::BufferdataType::Unknown, + hauppauge_mode: 0, + frames_since_last_gop: 0, + saw_gop_header: 0, + max_gop_length: 0, + last_gop_length: 0, + total_pulldownfields: 0, + total_pulldownframes: 0, + program_number: 0, + list: HList::default(), + timing: std::ptr::null_mut(), + codec: crate::common::Codec::Dvb, + has_ccdata_buffered: 0, + is_alloc: 0, + avc_ctx: std::ptr::null_mut(), + private_data: std::ptr::null_mut(), + current_hor_size: 0, + current_vert_size: 0, + current_aspect_ratio: 0, + current_frame_rate: 0, + no_bitstream_error: 0, + saw_seqgoppic: 0, + in_pic_data: 0, + current_progressive_sequence: 0, + current_pulldownfields: 0, + temporal_reference: 0, + picture_coding_type: crate::common::FrameType::ResetOrUnknown, + num_key_frames: 0, + picture_structure: 0, + repeat_first_field: 0, + progressive_frame: 0, + pulldownfields: 0, + top_field_first: 0, + stat_numuserheaders: 0, + stat_dvdccheaders: 0, + stat_scte20ccheaders: 0, + stat_replay5000headers: 0, + stat_replay4000headers: 0, + stat_dishheaders: 0, + stat_hdtv: 0, + stat_divicom: 0, + false_pict_header: 0, + current_field: 0, + maxtref: 0, + cc_data_count: [0; SORTBUF], + cc_fts: [0; SORTBUF], + cc_data_pkts: [[0; 10 * 31 * 3 + 1]; SORTBUF], + anchor_seq_number: 0, + xds_ctx: std::ptr::null_mut(), + ocr_quantmode: 0, + prev: std::ptr::null_mut(), + } + } +} + +// HList (Hyperlinked List) +#[derive(Debug)] +pub struct HList { + // A lot of the HList struct is not implemented yet + pub next: *mut HList, + pub prev: *mut HList, +} +impl Default for HList { + fn default() -> Self { + HList { + next: null_mut(), + prev: null_mut(), + } + } +} + +pub struct AvcCtx { + pub cc_count: u8, // Number of closed caption blocks + pub cc_data: *mut u8, // Pointer to buffer holding CC data + pub cc_databufsize: i64, // Buffer size for CC data + pub cc_buffer_saved: i32, // Was the CC buffer saved after the last update? + + pub got_seq_para: i32, // Flag indicating if sequence parameters were received + pub nal_ref_idc: u32, // NAL reference ID + pub seq_parameter_set_id: i64, // Sequence parameter set ID + pub log2_max_frame_num: i32, // Log2 of max frame number + pub pic_order_cnt_type: i32, // Picture order count type + pub log2_max_pic_order_cnt_lsb: i32, // Log2 of max picture order count LSB + pub frame_mbs_only_flag: i32, // Flag indicating if only frame MBs are used + + // Use and throw stats for debugging (TODO: clean up later) + pub num_nal_unit_type_7: i64, // Number of NAL units of type 7 + pub num_vcl_hrd: i64, // Number of VCL HRD parameters encountered + pub num_nal_hrd: i64, // Number of NAL HRD parameters encountered + pub num_jump_in_frames: i64, // Number of frame jumps detected + pub num_unexpected_sei_length: i64, // Number of unexpected SEI lengths + + pub ccblocks_in_avc_total: i32, // Total CC blocks in AVC stream + pub ccblocks_in_avc_lost: i32, // Lost CC blocks in AVC stream + + pub frame_num: i64, // Current frame number + pub lastframe_num: i64, // Last processed frame number + pub currref: i32, // Current reference index + pub maxidx: i32, // Maximum index value for ordering + pub lastmaxidx: i32, // Last max index + + // Used to find tref zero in PTS mode + pub minidx: i32, // Minimum reference index + pub lastminidx: i32, // Last minimum reference index + + // Used to remember the max temporal reference number (POC mode) + pub maxtref: i32, // Max temporal reference + pub last_gop_maxtref: i32, // Last GOP max temporal reference + + // Used for PTS ordering of CC blocks + pub currefpts: i64, // Current reference PTS + pub last_pic_order_cnt_lsb: i64, // Last picture order count LSB + pub last_slice_pts: i64, // Last slice PTS +} + +pub struct XdsContext { + // Program Identification Number (Start Time) for current program + pub current_xds_min: i32, + pub current_xds_hour: i32, + pub current_xds_date: i32, + pub current_xds_month: i32, + pub current_program_type_reported: i32, // No. + pub xds_start_time_shown: i32, + pub xds_program_length_shown: i32, + pub xds_program_description: [[char; 33]; 8], // Program descriptions (8 entries of 33 characters each) + + pub current_xds_network_name: [char; 33], // Network name + pub current_xds_program_name: [char; 33], // Program name + pub current_xds_call_letters: [char; 7], // Call letters + pub current_xds_program_type: [char; 33], // Program type + + pub xds_buffers: [XdsBuffer; NUM_XDS_BUFFERS as usize], // Array of XDS buffers + pub cur_xds_buffer_idx: i32, // Current XDS buffer index + pub cur_xds_packet_class: i32, // Current XDS packet class + pub cur_xds_payload: *mut u8, // Pointer to the current XDS payload + pub cur_xds_payload_length: i32, // Length of the current XDS payload + pub cur_xds_packet_type: i32, // Current XDS packet type + pub timing: *mut crate::time::TimingContext, // Pointer to timing context + + pub current_ar_start: u32, // Current AR start time + pub current_ar_end: u32, // Current AR end time + + pub xds_write_to_file: i32, // Set to 1 if XDS data is to be written to a file +} diff --git a/src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs b/src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs new file mode 100644 index 000000000..584d4370a --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs @@ -0,0 +1,798 @@ +use crate::decoder_isdb::exit_codes::*; +use crate::decoder_isdb::structs_xds::{CcxDecoder608ColorCode, CcxEncodingType}; + +use crate::common::OutputFormat; // ccxoutputformat + +use crate::common::StreamMode; // //CcxStreamModeEnum + +use crate::common::Codec; // ccxcodetype - 2 options{Codec, SelectCodec} - use appropriately + +use crate::time::TimestampFormat; // ccxoutputdateformat + +use crate::time::TimingContext; // ccxcommontimingctx + +use crate::time::Timestamp; // ccx_boundary_time + +use std::os::raw::{c_char, c_int, c_uchar, c_uint, c_ulonglong, c_void}; + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ListHead { + pub next: *mut ListHead, + pub prev: *mut ListHead, +} + +#[repr(C)] +pub struct DtvccCtx { + pub is_active: c_int, + pub active_services_count: c_int, + pub services_active: [c_int; CCX_DTVCC_MAX_SERVICES], // 0 - inactive, 1 - active + pub report_enabled: c_int, + pub report: *mut CcxDecoderDtvccReport, + pub decoders: [DtvccServiceDecoder; CCX_DTVCC_MAX_SERVICES], + pub current_packet: [c_uchar; CCX_DTVCC_MAX_PACKET_LENGTH], + pub current_packet_length: c_int, + pub is_current_packet_header_parsed: c_int, + pub last_sequence: c_int, + pub encoder: *mut c_void, // we can't include header, so keeping it this way + pub no_rollup: c_int, + pub timing: *mut TimingContext, +} + +#[repr(C)] +pub struct CcxDecoderDtvccReport { + pub reset_count: c_int, + pub services: [c_uint; CCX_DTVCC_MAX_SERVICES], +} + +#[repr(C)] +pub struct DtvccServiceDecoder { + pub windows: [DtvccWindow; CCX_DTVCC_MAX_WINDOWS], + pub current_window: c_int, + pub tv: *mut DtvccTvScreen, + pub cc_count: c_int, +} + +#[repr(C)] +pub struct DtvccWindow { + pub is_defined: c_int, + pub number: c_int, + pub priority: c_int, + pub col_lock: c_int, + pub row_lock: c_int, + pub visible: c_int, + pub anchor_vertical: c_int, + pub relative_pos: c_int, + pub anchor_horizontal: c_int, + pub row_count: c_int, + pub anchor_point: c_int, + pub col_count: c_int, + pub pen_style: c_int, + pub win_style: c_int, + pub commands: [c_uchar; 6], // Commands used to create this window + pub attribs: DtvccWindowAttribs, + pub pen_row: c_int, + pub pen_column: c_int, + pub rows: [*mut DtvccSymbol; CCX_DTVCC_MAX_ROWS], + pub pen_colors: [[DtvccPenColor; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_MAX_ROWS], + pub pen_attribs: [[DtvccPenAttribs; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_MAX_ROWS], + pub pen_color_pattern: DtvccPenColor, + pub pen_attribs_pattern: DtvccPenAttribs, + pub memory_reserved: c_int, + pub is_empty: c_int, + pub time_ms_show: i64, // Assuming LLONG is equivalent to i64 + pub time_ms_hide: i64, +} + +#[repr(C)] +pub struct DtvccWindowAttribs { + pub justify: c_int, + pub print_direction: c_int, + pub scroll_direction: c_int, + pub word_wrap: c_int, + pub display_effect: c_int, + pub effect_direction: c_int, + pub effect_speed: c_int, + pub fill_color: c_int, + pub fill_opacity: c_int, + pub border_type: c_int, + pub border_color: c_int, +} + +#[repr(C)] +pub struct DtvccSymbol { + pub sym: u16, // symbol itself, at least 16 bit + pub init: c_uchar, // initialized or not. could be 0 or 1 +} + +#[repr(C)] +pub struct DtvccPenColor { + pub fg_color: c_int, + pub fg_opacity: c_int, + pub bg_color: c_int, + pub bg_opacity: c_int, + pub edge_color: c_int, +} + +#[repr(C)] +pub struct DtvccPenAttribs { + pub pen_size: c_int, + pub offset: c_int, + pub text_tag: c_int, + pub font_tag: c_int, + pub edge_type: c_int, + pub underline: c_int, + pub italic: c_int, +} + +#[repr(C)] +pub struct DtvccTvScreen { + pub chars: [[DtvccSymbol; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], + pub pen_colors: [[DtvccPenColor; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], + pub pen_attribs: [[DtvccPenAttribs; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], + pub time_ms_show: i64, + pub time_ms_hide: i64, + pub cc_count: c_uint, + pub service_number: c_int, + pub old_cc_time_end: c_int, +} + +#[repr(C)] +pub struct CcxDecoderVbiCtx { + pub vbi_decoder_inited: c_int, + pub zvbi_decoder: VbiRawDecoder, + // vbi3_raw_decoder zvbi_decoder; + // #ifdef VBI_DEBUG + // pub vbi_debug_dump: *mut FILE, + // #endif +} + +#[repr(C)] +pub struct VbiRawDecoder { + /* Sampling parameters */ + /** + * Either 525 (M/NTSC, M/PAL) or 625 (PAL, SECAM), describing the + * scan line system all line numbers refer to. + */ + pub scanning: c_int, + /** + * Format of the raw vbi data. + */ + pub sampling_format: VbiPixfmt, + /** + * Sampling rate in Hz, the number of samples or pixels + * captured per second. + */ + pub sampling_rate: c_int, // Hz + /** + * Number of samples or pixels captured per scan line, + * in bytes. This determines the raw vbi image width and you + * want it large enough to cover all data transmitted in the line (with + * headroom). + */ + pub bytes_per_line: c_int, + /** + * The distance from 0H (leading edge hsync, half amplitude point) + * to the first sample (pixel) captured, in samples (pixels). You want + * an offset small enough not to miss the start of the data + * transmitted. + */ + pub offset: c_int, // 0H, samples + /** + * First scan line to be captured, first and second field + * respectively, according to the ITU-R line numbering scheme + * (see vbi_sliced). Set to zero if the exact line number isn't + * known. + */ + pub start: [c_int; 2], // ITU-R numbering + /** + * Number of scan lines captured, first and second + * field respectively. This can be zero if only data from one + * field is required. The sum @a count[0] + @a count[1] determines the + * raw vbi image height. + */ + pub count: [c_int; 2], // field lines + /** + * In the raw vbi image, normally all lines of the second + * field are supposed to follow all lines of the first field. When + * this flag is set, the scan lines of first and second field + * will be interleaved in memory. This implies @a count[0] and @a count[1] + * are equal. + */ + pub interlaced: c_int, + /** + * Fields must be stored in temporal order, i. e. as the + * lines have been captured. It is assumed that the first field is + * also stored first in memory, however if the hardware cannot reliable + * distinguish fields this flag shall be cleared, which disables + * decoding of data services depending on the field number. + */ + pub synchronous: c_int, + + pub services: c_uint, + pub num_jobs: c_int, + + pub pattern: *mut i8, + pub jobs: [VbiRawDecoderJob; 8], +} + +#[repr(C)] +pub struct VbiRawDecoderJob { + pub id: c_uint, + pub offset: c_int, + pub slicer: VbiBitSlicer, +} + +#[repr(C)] +pub enum VbiPixfmt { + Yuv420 = 1, + Yuyv, + Yvyu, + Uyvy, + Vyuy, + Pal8, + Rgba32Le = 32, + Rgba32Be, + Bgra32Le, + Bgra32Be, + Abgr32Be, /* = 32, // synonyms */ + Abgr32Le, + Argb32Be, + Argb32Le, + Rgb24, + Bgr24, + Rgb16Le, + Rgb16Be, + Bgr16Le, + Bgr16Be, + Rgba15Le, + Rgba15Be, + Bgra15Le, + Bgra15Be, + Argb15Le, + Argb15Be, + Abgr15Le, + Abgr15Be, +} + +#[repr(C)] +pub struct VbiBitSlicer { + pub func: Option< + extern "C" fn(slicer: *mut VbiBitSlicer, raw: *mut c_uchar, buf: *mut c_uchar) -> c_int, + >, + pub cri: c_uint, + pub cri_mask: c_uint, + pub thresh: c_int, + pub cri_bytes: c_int, + pub cri_rate: c_int, + pub oversampling_rate: c_int, + pub phase_shift: c_int, + pub step: c_int, + pub frc: c_uint, + pub frc_bits: c_int, + pub payload: c_int, + pub endian: c_int, + pub skip: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ISDBSubContext { + pub nb_char: c_int, + pub nb_line: c_int, + pub timestamp: u64, + pub prev_timestamp: u64, + pub text_list_head: ListHead, + pub buffered_text: ListHead, + pub current_state: ISDBSubState, + pub tmd: IsdbTmd, + pub nb_lang: c_int, + pub offset_time: OffsetTime, + pub dmf: c_uchar, + pub dc: c_uchar, + pub cfg_no_rollup: c_int, +} + +#[derive(Debug, Clone, Default)] +#[repr(C)] +pub struct ISDBSubState { + pub auto_display: c_int, // bool + pub rollup_mode: c_int, // bool + pub need_init: c_uchar, // bool + pub clut_high_idx: c_uchar, + pub fg_color: c_uint, + pub bg_color: c_uint, + pub hfg_color: c_uint, + pub hbg_color: c_uint, + pub mat_color: c_uint, + pub raster_color: c_uint, + pub layout_state: ISDBSubLayout, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct ISDBSubLayout { + pub format: WritingFormat, + pub display_area: DispArea, + pub font_size: c_int, + pub font_scale: FScale, + pub cell_spacing: Spacing, + pub cursor_pos: ISDBPos, + pub ccc: IsdbCCComposition, + pub acps: [c_int; 2], +} + +#[derive(Debug, Clone, Default)] +#[repr(C)] +pub struct DispArea { + pub x: c_int, + pub y: c_int, + pub w: c_int, + pub h: c_int, +} + +#[derive(Debug, Clone)] +#[repr(C)] +pub struct FScale { + pub fscx: c_int, + pub fscy: c_int, +} + +#[derive(Debug, Clone, Default)] +#[repr(C)] +pub struct Spacing { + pub col: c_int, + pub row: c_int, +} + +#[derive(Debug, Clone, Default)] +#[repr(C)] +pub struct ISDBPos { + pub x: c_int, + pub y: c_int, +} + +#[derive(Debug, Clone, Default)] +#[repr(C)] +pub struct OffsetTime { + pub hour: c_int, + pub min: c_int, + pub sec: c_int, + pub milli: c_int, +} + +#[repr(C)] +#[derive(Copy, Clone, Debug, Default, PartialEq)] +pub enum WritingFormat { + #[default] + HorizontalStdDensity = 0, + VerticalStdDensity = 1, + HorizontalHighDensity = 2, + VerticalHighDensity = 3, + HorizontalWesternLang = 4, + Horizontal1920x1080 = 5, + Vertical1920x1080 = 6, + Horizontal960x540 = 7, + Vertical960x540 = 8, + Horizontal720x480 = 9, + Vertical720x480 = 10, + Horizontal1280x720 = 11, + Vertical1280x720 = 12, + HorizontalCustom = 100, + None, +} + +#[derive(Debug, Clone, Default)] +#[repr(C)] +pub enum IsdbCCComposition { + #[default] + None = 0, + And = 2, + Or = 3, + Xor = 4, +} + +use std::convert::TryFrom; + +impl TryFrom for IsdbCCComposition { + type Error = (); + + fn try_from(value: u8) -> Result { + match value { + 0 => Ok(IsdbCCComposition::None), + 2 => Ok(IsdbCCComposition::And), + 3 => Ok(IsdbCCComposition::Or), + 4 => Ok(IsdbCCComposition::Xor), + _ => Err(()), + } + } +} + +#[derive(Debug, Clone, Copy, Default, PartialEq)] +#[repr(C)] +pub enum IsdbTmd { + #[default] + Free = 0, + RealTime = 0x1, + OffsetTime = 0x2, +} + +#[repr(C)] +pub enum FontSize { + SmallFontSize, + MiddleFontSize, + StandardFontSize, +} + +#[repr(C)] +pub enum CsiCommand { + Gsm = 0x42, + Swf = 0x53, + Ccc = 0x54, + Sdf = 0x56, + Ssm = 0x57, + Shs = 0x58, + Svs = 0x59, + Pld = 0x5B, + Plu = 0x5C, + Gaa = 0x5D, + Src = 0x5E, + Sdp = 0x5F, + Acps = 0x61, + Tcc = 0x62, + Orn = 0x63, + Mdf = 0x64, + Cfs = 0x65, + Xcs = 0x66, + Pra = 0x68, + Acs = 0x69, + Rcs = 0x6E, + Scs = 0x6F, +} + +#[repr(C)] +pub enum Color { + CcxIsdbBlack, + FiRed, + FiGreen, + FiYellow, + FiBlue, + FiMagenta, + FiCyan, + FiWhite, + CcxIsdbTransparent, + HiRed, + HiGreen, + HiYellow, + HiBlue, + HiMagenta, + HiCyan, + HiWhite, +} + +#[no_mangle] +pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> u32 { + ((255 - a as u32) << 24) | ((b as u32) << 16) | ((g as u32) << 8) | (r as u32) +} + +type Rgba = u32; +pub const ZATA: u32 = 0; +pub const DEFAULT_CLUT: [Rgba; 128] = [ + // 0-7 + rgba(0, 0, 0, 255), + rgba(255, 0, 0, 255), + rgba(0, 255, 0, 255), + rgba(255, 255, 0, 255), + rgba(0, 0, 255, 255), + rgba(255, 0, 255, 255), + rgba(0, 255, 255, 255), + rgba(255, 255, 255, 255), + // 8-15 + rgba(0, 0, 0, 0), + rgba(170, 0, 0, 255), + rgba(0, 170, 0, 255), + rgba(170, 170, 0, 255), + rgba(0, 0, 170, 255), + rgba(170, 0, 170, 255), + rgba(0, 170, 170, 255), + rgba(170, 170, 170, 255), + // 16-23 + rgba(0, 0, 85, 255), + rgba(0, 85, 0, 255), + rgba(0, 85, 85, 255), + rgba(0, 85, 170, 255), + rgba(0, 85, 255, 255), + rgba(0, 170, 85, 255), + rgba(0, 170, 255, 255), + rgba(0, 255, 85, 255), + // 24-31 + rgba(0, 255, 170, 255), + rgba(85, 0, 0, 255), + rgba(85, 0, 85, 255), + rgba(85, 0, 170, 255), + rgba(85, 0, 255, 255), + rgba(85, 85, 0, 255), + rgba(85, 85, 85, 255), + rgba(85, 85, 170, 255), + // 32-39 + rgba(85, 85, 255, 255), + rgba(85, 170, 0, 255), + rgba(85, 170, 85, 255), + rgba(85, 170, 170, 255), + rgba(85, 170, 255, 255), + rgba(85, 255, 0, 255), + rgba(85, 255, 85, 255), + rgba(85, 255, 170, 255), + // 40-47 + rgba(85, 255, 255, 255), + rgba(170, 0, 85, 255), + rgba(170, 0, 255, 255), + rgba(170, 85, 0, 255), + rgba(170, 85, 85, 255), + rgba(170, 85, 170, 255), + rgba(170, 85, 255, 255), + rgba(170, 170, 85, 255), + // 48-55 + rgba(170, 170, 255, 255), + rgba(170, 255, 0, 255), + rgba(170, 255, 85, 255), + rgba(170, 255, 170, 255), + rgba(170, 255, 255, 255), + rgba(255, 0, 85, 255), + rgba(255, 0, 170, 255), + rgba(255, 85, 0, 255), + // 56-63 + rgba(255, 85, 85, 255), + rgba(255, 85, 170, 255), + rgba(255, 85, 255, 255), + rgba(255, 170, 0, 255), + rgba(255, 170, 85, 255), + rgba(255, 170, 170, 255), + rgba(255, 170, 255, 255), + rgba(255, 255, 85, 255), + // 64 + rgba(255, 255, 170, 255), + // 65-127 are calculated later. + // Initialize remaining elements to 0 + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, + ZATA, +]; + +#[repr(C)] +pub struct ISDBText { + pub buf: *mut c_char, + pub len: usize, + pub used: usize, + pub pos: ISDBPos, + pub txt_tail: usize, // tail of the text, excluding trailing control sequences. + pub timestamp: c_ulonglong, // Time Stamp when first string is received + pub refcount: c_int, + pub list: ListHead, +} + +pub struct CcxEncodersTranscriptFormat { + pub show_start_time: i32, // Show start and/or end time. + pub show_end_time: i32, // Show start and/or end time. + pub show_mode: i32, // Show which mode if available (E.G.: POP, RU1, ...) + pub show_cc: i32, // Show which CC channel has been captured. + pub relative_timestamp: i32, // Timestamps relative to start of sample or in UTC? + pub xds: i32, // Show XDS or not + pub use_colors: i32, // Add colors or no colors + pub is_final: i32, // Used to determine if these parameters should be changed afterwards. +} + +#[repr(i32)] +pub enum CcxDataSource { + File = 0, + Stdin = 1, + Network = 2, + Tcp = 3, +} + +#[repr(C)] +pub struct CcxDecoder608Settings { + pub direct_rollup: i32, + pub force_rollup: i32, + pub no_rollup: i32, + pub default_color: CcxDecoder608ColorCode, + pub screens_to_process: i32, + pub report: *mut CcxDecoder608Report, +} + +#[repr(C)] +pub struct CcxDecoder608Report { + pub xds: u8, + pub cc_channels: [u8; 4], +} + +#[repr(C)] +pub struct CcxDecoderDtvccSettings { + pub enabled: i32, + pub print_file_reports: i32, + pub no_rollup: i32, + pub report: *mut CcxDecoderDtvccReport, + pub active_services_count: i32, + pub services_enabled: [i32; CCX_DTVCC_MAX_SERVICES], + pub timing: *mut TimingContext, +} + +#[repr(C)] +pub struct DemuxerCfg { + pub m2ts: i32, + pub auto_stream: StreamMode, + pub codec: Codec, + pub nocodec: Codec, + pub ts_autoprogram: u32, + pub ts_allprogram: u32, + pub ts_cappids: [u32; 128], + pub nb_ts_cappid: i32, + pub ts_forced_cappid: u32, + pub ts_forced_program: i32, + pub ts_forced_program_selected: u32, + pub ts_datastreamtype: i32, + pub ts_forced_streamtype: u32, +} + +#[repr(C)] +pub struct EncoderCfg { + pub extract: i32, + pub dtvcc_extract: i32, + pub gui_mode_reports: i32, + pub output_filename: *mut c_char, + pub write_format: OutputFormat, + pub keep_output_closed: i32, + pub force_flush: i32, + pub append_mode: i32, + pub ucla: i32, + pub encoding: CcxEncodingType, + pub date_format: TimestampFormat, + pub millis_separator: c_char, + pub autodash: i32, + pub trim_subs: i32, + pub sentence_cap: i32, + pub splitbysentence: i32, + pub filter_profanity: i32, + pub with_semaphore: i32, + pub start_credits_text: *mut c_char, + pub end_credits_text: *mut c_char, + pub startcreditsnotbefore: Timestamp, + pub startcreditsnotafter: Timestamp, + pub startcreditsforatleast: Timestamp, + pub startcreditsforatmost: Timestamp, + pub endcreditsforatleast: Timestamp, + pub endcreditsforatmost: Timestamp, + pub transcript_settings: CcxEncodersTranscriptFormat, + pub send_to_srv: u32, + pub no_bom: i32, + pub first_input_file: *mut c_char, + pub multiple_files: i32, + pub no_font_color: i32, + pub no_type_setting: i32, + pub cc_to_stdout: i32, + pub line_terminator_lf: i32, + pub subs_delay: i64, + pub program_number: i32, + pub in_format: u8, + pub nospupngocr: i32, + pub force_dropframe: i32, + pub render_font: *mut c_char, + pub render_font_italics: *mut c_char, + pub services_enabled: [i32; CCX_DTVCC_MAX_SERVICES], + pub services_charsets: *mut *mut c_char, + pub all_services_charset: *mut c_char, + pub extract_only_708: i32, +} + +impl Default for ISDBSubLayout { + fn default() -> Self { + Self { + format: WritingFormat::None, + display_area: Default::default(), + font_size: 0, + font_scale: Default::default(), + cell_spacing: Default::default(), + cursor_pos: Default::default(), + ccc: IsdbCCComposition::None, + acps: [0; 2], + } + } +} + +impl Default for FScale { + fn default() -> Self { + Self { + fscx: 100, + fscy: 100, + } + } +} + +impl Default for ISDBSubContext { + fn default() -> Self { + Self { + nb_char: 0, + nb_line: 0, + timestamp: 0, + prev_timestamp: 0, + text_list_head: Default::default(), + buffered_text: Default::default(), + current_state: Default::default(), + tmd: IsdbTmd::Free, + nb_lang: 0, + offset_time: Default::default(), + dmf: 0, + dc: 0, + cfg_no_rollup: 0, + } + } +} + +impl Default for ListHead { + fn default() -> Self { + Self { + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + } + } +} diff --git a/src/rust/lib_ccxr/src/decoder_isdb/structs_xds.rs b/src/rust/lib_ccxr/src/decoder_isdb/structs_xds.rs new file mode 100644 index 000000000..33332545c --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_isdb/structs_xds.rs @@ -0,0 +1,392 @@ +use crate::decoder_isdb::exit_codes::*; + +use crate::time::timing::*; + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CcxEia608Format { + SformatCcScreen, + SformatCcLine, + SformatXds, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CcxDecoder608ColorCode { + ColWhite = 0, + ColGreen = 1, + ColBlue = 2, + ColCyan = 3, + ColRed = 4, + ColYellow = 5, + ColMagenta = 6, + ColUserDefined = 7, + ColBlack = 8, + ColTransparent = 9, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum FontBits { + FontRegular = 0, + FontItalics = 1, + FontUnderlined = 2, + FontUnderlinedItalics = 3, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq)] +pub enum CcModes { + ModePopOn = 0, + ModeRollUp2 = 1, + ModeRollUp3 = 2, + ModeRollUp4 = 3, + ModeText = 4, + ModePaintOn = 5, + ModeFakeRollUp1 = 100, // Fake modes to emulate stuff +} + +// The `Eia608Screen` structure +#[derive(Debug)] +pub struct Eia608Screen { + pub format: CcxEia608Format, // Format of data inside this structure + pub characters: [[u8; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], // Characters + pub colors: + [[CcxDecoder608ColorCode; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], // Colors + pub fonts: [[FontBits; CCX_DECODER_608_SCREEN_WIDTH + 1]; CCX_DECODER_608_SCREEN_ROWS], // Fonts + pub row_used: [i64; CCX_DECODER_608_SCREEN_ROWS], // Any data in row? + pub empty: i64, // Buffer completely empty? + pub start_time: i64, // Start time of this CC buffer + pub end_time: i64, // End time of this CC buffer + pub mode: CcModes, // Mode + pub channel: i64, // Currently selected channel + pub my_field: i64, // Used for sanity checks + pub xds_str: *const u8, // Pointer to XDS string + pub xds_len: usize, // Length of XDS string + pub cur_xds_packet_class: i64, // Class of XDS string +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +pub enum SubDataType { + #[default] + Generic = 0, + Dvb = 1, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +pub enum SubType { + #[default] + Bitmap, + Cc608, + Text, + Raw, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, Default)] +pub enum CcxEncodingType { + #[default] + Unicode = 0, + Latin1 = 1, + Utf8 = 2, + Ascii = 3, +} + +#[derive(Debug)] +pub struct CcSubtitle { + /** + * A generic data which contains data according to the decoder. + * @warn Decoder can't output multiple types of data. + */ + pub data: *mut std::ffi::c_void, // Pointer to generic data + pub datatype: SubDataType, // Data type (e.g., Generic, DVB) + + /** Number of data */ + pub nb_data: u32, + + /** Type of subtitle */ + pub subtype: SubType, + + /** Encoding type of text, ignored for bitmap or cc_screen subtypes */ + pub enc_type: CcxEncodingType, + + /* Set only when all the data is to be displayed at the same time. + * Unit of time is milliseconds. + */ + pub start_time: i64, + pub end_time: i64, + + /* Flags */ + pub flags: i32, + + /* Index of language table */ + pub lang_index: i32, + + /** Flag to tell that the decoder has given output */ + pub got_output: bool, + + pub mode: [u8; 5], // Mode as a fixed-size array of 5 bytes + pub info: [u8; 4], // Info as a fixed-size array of 4 bytes + + /** Used for DVB end time in milliseconds */ + pub time_out: i32, + + pub next: *mut CcSubtitle, // Pointer to the next subtitle + pub prev: *mut CcSubtitle, // Pointer to the previous subtitle +} + +// XDS classes +pub const XDS_CLASSES: [&str; 8] = [ + "Current", + "Future", + "Channel", + "Miscellaneous", + "Public service", + "Reserved", + "Private data", + "End", +]; + +// XDS program types +pub const XDS_PROGRAM_TYPES: [&str; 96] = [ + "Education", + "Entertainment", + "Movie", + "News", + "Religious", + "Sports", + "Other", + "Action", + "Advertisement", + "Animated", + "Anthology", + "Automobile", + "Awards", + "Baseball", + "Basketball", + "Bulletin", + "Business", + "Classical", + "College", + "Combat", + "Comedy", + "Commentary", + "Concert", + "Consumer", + "Contemporary", + "Crime", + "Dance", + "Documentary", + "Drama", + "Elementary", + "Erotica", + "Exercise", + "Fantasy", + "Farm", + "Fashion", + "Fiction", + "Food", + "Football", + "Foreign", + "Fund-Raiser", + "Game/Quiz", + "Garden", + "Golf", + "Government", + "Health", + "High_School", + "History", + "Hobby", + "Hockey", + "Home", + "Horror", + "Information", + "Instruction", + "International", + "Interview", + "Language", + "Legal", + "Live", + "Local", + "Math", + "Medical", + "Meeting", + "Military", + "Mini-Series", + "Music", + "Mystery", + "National", + "Nature", + "Police", + "Politics", + "Premiere", + "Pre-Recorded", + "Product", + "Professional", + "Public", + "Racing", + "Reading", + "Repair", + "Repeat", + "Review", + "Romance", + "Science", + "Series", + "Service", + "Shopping", + "Soap_Opera", + "Special", + "Suspense", + "Talk", + "Technical", + "Tennis", + "Travel", + "Variety", + "Video", + "Weather", + "Western", +]; + +// XDS class constants +pub const XDS_CLASS_CURRENT: u8 = 0; +pub const XDS_CLASS_FUTURE: u8 = 1; +pub const XDS_CLASS_CHANNEL: u8 = 2; +pub const XDS_CLASS_MISC: u8 = 3; +pub const XDS_CLASS_PUBLIC: u8 = 4; +pub const XDS_CLASS_RESERVED: u8 = 5; +pub const XDS_CLASS_PRIVATE: u8 = 6; +pub const XDS_CLASS_END: u8 = 7; +pub const XDS_CLASS_OUT_OF_BAND: u8 = 0x40; // Not a real class, a marker for packets for out-of-band data + +// Types for the classes current and future +pub const XDS_TYPE_PIN_START_TIME: u8 = 1; +pub const XDS_TYPE_LENGTH_AND_CURRENT_TIME: u8 = 2; +pub const XDS_TYPE_PROGRAM_NAME: u8 = 3; +pub const XDS_TYPE_PROGRAM_TYPE: u8 = 4; +pub const XDS_TYPE_CONTENT_ADVISORY: u8 = 5; +pub const XDS_TYPE_AUDIO_SERVICES: u8 = 6; +pub const XDS_TYPE_CGMS: u8 = 8; // Copy Generation Management System +pub const XDS_TYPE_ASPECT_RATIO_INFO: u8 = 9; // Appears in CEA-608-B but in E it's been removed as is "reserved" +pub const XDS_TYPE_PROGRAM_DESC_1: u8 = 0x10; +pub const XDS_TYPE_PROGRAM_DESC_2: u8 = 0x11; +pub const XDS_TYPE_PROGRAM_DESC_3: u8 = 0x12; +pub const XDS_TYPE_PROGRAM_DESC_4: u8 = 0x13; +pub const XDS_TYPE_PROGRAM_DESC_5: u8 = 0x14; +pub const XDS_TYPE_PROGRAM_DESC_6: u8 = 0x15; +pub const XDS_TYPE_PROGRAM_DESC_7: u8 = 0x16; +pub const XDS_TYPE_PROGRAM_DESC_8: u8 = 0x17; + +// Types for the class channel +pub const XDS_TYPE_NETWORK_NAME: u8 = 1; +pub const XDS_TYPE_CALL_LETTERS_AND_CHANNEL: u8 = 2; +pub const XDS_TYPE_TSID: u8 = 4; // Transmission Signal Identifier + +// Types for miscellaneous packets +pub const XDS_TYPE_TIME_OF_DAY: u8 = 1; +pub const XDS_TYPE_LOCAL_TIME_ZONE: u8 = 4; +pub const XDS_TYPE_OUT_OF_BAND_CHANNEL_NUMBER: u8 = 0x40; + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct XdsBuffer { + pub in_use: i64, // Whether the buffer is in use + pub xds_class: i64, // XDS class (e.g., XDS_CLASS_CURRENT, etc.) + pub xds_type: i64, // XDS type (e.g., XDS_TYPE_PROGRAM_NAME, etc.) + pub bytes: [u8; NUM_BYTES_PER_PACKET as usize], // Data bytes (size defined by NUM_BYTES_PER_PACKET) + pub used_bytes: i64, // Number of bytes used in the buffer +} + +#[repr(C)] +pub struct CcxDecodersXdsContext { + // Program Identification Number (Start Time) for current program + pub current_xds_min: i64, + pub current_xds_hour: i64, + pub current_xds_date: i64, + pub current_xds_month: i64, + pub current_program_type_reported: i64, // No. + pub xds_start_time_shown: i64, + pub xds_program_length_shown: i64, + pub xds_program_description: [[u8; 33]; 8], // 8 strings of 33 bytes each + + pub current_xds_network_name: [u8; 33], // String of 33 bytes + pub current_xds_program_name: [u8; 33], // String of 33 bytes + pub current_xds_call_letters: [u8; 7], // String of 7 bytes + pub current_xds_program_type: [u8; 33], // String of 33 bytes + + pub xds_buffers: [XdsBuffer; NUM_XDS_BUFFERS as usize], // Array of XdsBuffer + pub cur_xds_buffer_idx: i64, + pub cur_xds_packet_class: i64, + pub cur_xds_payload: *mut u8, // Pointer to payload + pub cur_xds_payload_length: i64, + pub cur_xds_packet_type: i64, + pub timing: TimingContext, // Replacing ccx_common_timing_ctx with TimingContext + + pub current_ar_start: i64, + pub current_ar_end: i64, + + pub xds_write_to_file: i64, // Set to 1 if XDS data is to be written to file +} + +impl Default for CcxDecodersXdsContext { + fn default() -> Self { + Self { + current_xds_min: -1, + current_xds_hour: -1, + current_xds_date: -1, + current_xds_month: -1, + current_program_type_reported: 0, + xds_start_time_shown: 0, + xds_program_length_shown: 0, + xds_program_description: [[0; 33]; 8], + current_xds_network_name: [0; 33], + current_xds_program_name: [0; 33], + current_xds_call_letters: [0; 7], + current_xds_program_type: [0; 33], + xds_buffers: [XdsBuffer { + in_use: 0, + xds_class: -1, + xds_type: -1, + bytes: [0; NUM_BYTES_PER_PACKET as usize], + used_bytes: 0, + }; NUM_XDS_BUFFERS as usize], + cur_xds_buffer_idx: -1, + cur_xds_packet_class: -1, + cur_xds_payload: std::ptr::null_mut(), + cur_xds_payload_length: 0, + cur_xds_packet_type: 0, + timing: TimingContext::default(), + current_ar_start: 0, + current_ar_end: 0, + xds_write_to_file: 0, + } + } +} + +impl Default for XdsBuffer { + fn default() -> Self { + Self { + in_use: 0, + xds_class: -1, + xds_type: -1, + bytes: [0; NUM_BYTES_PER_PACKET as usize], + used_bytes: 0, + } + } +} + +impl Default for CcSubtitle { + fn default() -> Self { + Self { + data: std::ptr::null_mut(), + datatype: SubDataType::Generic, + nb_data: 0, + subtype: SubType::Cc608, + enc_type: CcxEncodingType::Utf8, + start_time: 0, + end_time: 0, + flags: 0, + lang_index: 0, + got_output: false, + mode: [0; 5], + info: [0; 4], + time_out: 0, + next: std::ptr::null_mut(), + prev: std::ptr::null_mut(), + } + } +} diff --git a/src/rust/lib_ccxr/src/isdbs/isdbd.rs b/src/rust/lib_ccxr/src/isdbs/isdbd.rs deleted file mode 100644 index 926dee158..000000000 --- a/src/rust/lib_ccxr/src/isdbs/isdbd.rs +++ /dev/null @@ -1,1250 +0,0 @@ -// command-line bindgen used - -use std::os::raw::{c_char, c_int, c_long, c_uchar, c_uint, c_ulonglong, c_void}; - -#[allow(dead_code)] - -const CCX_DTVCC_MAX_SERVICES: usize = 63; -const CCX_DTVCC_MAX_WINDOWS: usize = 8; -const CCX_DTVCC_MAX_ROWS: usize = 15; -const CCX_DTVCC_SCREENGRID_COLUMNS: usize = 210; -const CCX_DTVCC_MAX_PACKET_LENGTH: usize = 128; -const CCX_DTVCC_SCREENGRID_ROWS: usize = 75; - -pub const CCX_MESSAGES_QUIET: i32 = 0; -pub const CCX_MESSAGES_STDOUT: i32 = 1; -pub const CCX_MESSAGES_STDERR: i32 = 2; - -const SORTBUF: usize = 2 * MAXBFRAMES + 1; -const MAXBFRAMES: usize = 50; - -#[repr(C)] -pub struct LibCcDecode { - pub cc_stats: [c_int; 4], - pub saw_caption_block: c_int, - pub processed_enough: c_int, // If 1, we have enough lines, time, etc. - - /* 608 contexts - note that this shouldn't be global, they should be - per program */ - pub context_cc608_field_1: *mut c_void, - pub context_cc608_field_2: *mut c_void, - - pub no_rollup: c_int, // If 1, write one line at a time - pub noscte20: c_int, - pub fix_padding: c_int, // Replace 0000 with 8080 in HDTV (needed for some cards) - pub write_format: CcxOutputFormat, // 0 = Raw, 1 = srt, 2 = SMI - pub extraction_start: CcxBoundaryTime, - pub extraction_end: CcxBoundaryTime, // Segment we actually process - pub subs_delay: i64, // ms to delay (or advance) subs - pub extract: c_int, // Extract 1st, 2nd or both fields - pub fullbin: c_int, // Disable pruning of padding cc blocks - pub dec_sub: CcSubtitle, - pub in_bufferdatatype: CcxBufferdataType, - pub hauppauge_mode: c_uint, // If 1, use PID=1003, process specially and so on - - pub frames_since_last_gop: c_int, - /* GOP-based timing */ - pub saw_gop_header: c_int, - /* Time info for timed-transcript */ - pub max_gop_length: c_int, // (Maximum) length of a group of pictures - pub last_gop_length: c_int, // Length of the previous group of pictures - pub total_pulldownfields: c_uint, - pub total_pulldownframes: c_uint, - pub program_number: c_int, - pub list: ListHead, - pub timing: *mut CcxCommonTimingCtx, - pub codec: CcxCodeType, - // Set to true if data is buffered - pub has_ccdata_buffered: c_int, - pub is_alloc: c_int, - - pub avc_ctx: *mut AvcCtx, - pub private_data: *mut c_void, - - /* General video information */ - pub current_hor_size: c_uint, - pub current_vert_size: c_uint, - pub current_aspect_ratio: c_uint, - pub current_frame_rate: c_uint, // Assume standard fps, 29.97 - - /* Required in es_function.c */ - pub no_bitstream_error: c_int, - pub saw_seqgoppic: c_int, - pub in_pic_data: c_int, - - pub current_progressive_sequence: c_uint, - pub current_pulldownfields: c_uint, - - pub temporal_reference: c_int, - pub picture_coding_type: CcxFrameType, - pub num_key_frames: c_uint, - pub picture_structure: c_uint, - pub repeat_first_field: c_uint, - pub progressive_frame: c_uint, - pub pulldownfields: c_uint, - /* Required in es_function.c and es_userdata.c */ - pub top_field_first: c_uint, // Needs to be global - - /* Stats. Modified in es_userdata.c */ - pub stat_numuserheaders: c_int, - pub stat_dvdccheaders: c_int, - pub stat_scte20ccheaders: c_int, - pub stat_replay5000headers: c_int, - pub stat_replay4000headers: c_int, - pub stat_dishheaders: c_int, - pub stat_hdtv: c_int, - pub stat_divicom: c_int, - pub false_pict_header: c_int, - - pub dtvcc: *mut DtvccCtx, - pub current_field: c_int, - // Analyse/use the picture information - pub maxtref: c_int, // Use to remember the temporal reference number - - pub cc_data_count: [c_int; SORTBUF], - // Store fts; - pub cc_fts: [i64; SORTBUF], - // Store HD CC packets - pub cc_data_pkts: [[u8; 10 * 31 * 3 + 1]; SORTBUF], // *10, because MP4 seems to have different limits - - // The sequence number of the current anchor frame. All currently read - // B-Frames belong to this I- or P-frame. - pub anchor_seq_number: c_int, - pub xds_ctx: *mut CcxDecodersXdsContext, - pub vbi_decoder: *mut CcxDecoderVbiCtx, - - pub writedata: Option< - extern "C" fn( - data: *const u8, - length: c_int, - private_data: *mut c_void, - sub: *mut CcSubtitle, - ) -> c_int, - >, - - // dvb subtitle related - pub ocr_quantmode: c_int, - pub prev: *mut LibCcDecode, -} - -// Define the necessary enums and structs -#[repr(C)] -pub enum CcxOutputFormat { - Raw = 0, - Srt = 1, - Sami = 2, - Transcript = 3, - Rcwt = 4, - Null = 5, - Smptett = 6, - Spupng = 7, - DvdRaw = 8, - Webvtt = 9, - SimpleXml = 10, - G608 = 11, - Curl = 12, - Ssa = 13, - Mcc = 14, - Scc = 15, - Ccd = 16, -} - -#[repr(C)] -pub struct CcxBoundaryTime { - pub hh: i32, - pub mm: i32, - pub ss: i32, - pub time_in_ms: i64, - pub set: i32, -} - -#[repr(C)] -pub struct CcSubtitle { - /** - * A generic data which contain data according to decoder - * @warn decoder cant output multiple types of data - */ - pub data: *mut c_void, - pub datatype: Subdatatype, - - /** number of data */ - pub nb_data: c_uint, - - /** type of subtitle */ - pub type_: Subtype, - - /** Encoding type of Text, must be ignored in case of subtype as bitmap or cc_screen*/ - pub enc_type: CcxEncodingType, - - /* set only when all the data is to be displayed at same time - * Unit is of time is ms - */ - pub start_time: i64, // Assuming LLONG is equivalent to i64 - pub end_time: i64, - - /* flags */ - pub flags: c_int, - - /* index of language table */ - pub lang_index: c_int, - - /** flag to tell that decoder has given output */ - pub got_output: c_int, - - pub mode: [u8; 5], - pub info: [u8; 4], - - /** Used for DVB end time in ms */ - pub time_out: c_int, - - pub next: *mut CcSubtitle, - pub prev: *mut CcSubtitle, -} - -#[repr(C)] -pub enum Subdatatype { - Generic = 0, - Dvb = 1, -} - -#[repr(C)] -pub enum Subtype { - Bitmap, - Cc608, - Text, - Raw, -} - -#[repr(C)] -#[derive(Copy, Clone)] -pub enum CcxEncodingType { - Unicode = 0, - Latin1 = 1, - Utf8 = 2, - Ascii = 3, -} - -#[repr(C)] -pub enum CcxBufferdataType { - Unknown = 0, - Pes = 1, - Raw = 2, - H264 = 3, - Hauppage = 4, - Teletext = 5, - PrivateMpeg2Cc = 6, - DvbSubtitle = 7, - IsdbSubtitle = 8, - RawType = 9, // Buffer where cc data contain 3 byte cc_valid ccdata 1 ccdata 2 - DvdSubtitle = 10, -} - -#[repr(C)] -pub enum CcxFrameType { - ResetOrUnknown = 0, - IFrame = 1, - PFrame = 2, - BFrame = 3, - DFrame = 4, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct ListHead { - pub next: *mut ListHead, - pub prev: *mut ListHead, -} - -#[repr(C)] -pub struct CcxCommonTimingCtx { - pub pts_set: i32, // 0 = No, 1 = received, 2 = min_pts set - pub min_pts_adjusted: i32, // 0 = No, 1=Yes (don't adjust again) - pub current_pts: i64, // Assuming LLONG is equivalent to i64 - pub current_picture_coding_type: CcxFrameType, - pub current_tref: i32, // Store temporal reference of current frame - pub min_pts: i64, - pub max_pts: i64, - pub sync_pts: i64, - pub minimum_fts: i64, // No screen should start before this FTS - pub fts_now: i64, // Time stamp of current file (w/ fts_offset, w/o fts_global) - pub fts_offset: i64, // Time before first sync_pts - pub fts_fc_offset: i64, // Time before first GOP - pub fts_max: i64, // Remember the maximum fts that we saw in current file - pub fts_global: i64, // Duration of previous files (-ve mode) - pub sync_pts2fts_set: i32, // 0 = No, 1 = Yes - pub sync_pts2fts_fts: i64, - pub sync_pts2fts_pts: i64, - pub pts_reset: i32, // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev -} - -#[repr(C)] -pub enum CcxCodeType { - Any = 0, - Teletext = 1, - Dvb = 2, - IsdbCc = 3, - AtscCc = 4, - None = 5, -} - -#[repr(C)] -pub struct AvcCtx { - pub cc_count: c_uchar, - // buffer to hold cc data - pub cc_data: *mut c_uchar, - pub cc_databufsize: c_long, - pub cc_buffer_saved: c_int, // Was the CC buffer saved after it was last updated? - - pub got_seq_para: c_int, - pub nal_ref_idc: c_uint, - pub seq_parameter_set_id: i64, // Assuming LLONG is equivalent to i64 - pub log2_max_frame_num: c_int, - pub pic_order_cnt_type: c_int, - pub log2_max_pic_order_cnt_lsb: c_int, - pub frame_mbs_only_flag: c_int, - - // Use and throw stats for debug, remove this ugliness soon - pub num_nal_unit_type_7: c_long, - pub num_vcl_hrd: c_long, - pub num_nal_hrd: c_long, - pub num_jump_in_frames: c_long, - pub num_unexpected_sei_length: c_long, - - pub ccblocks_in_avc_total: c_int, - pub ccblocks_in_avc_lost: c_int, - - pub frame_num: i64, - pub lastframe_num: i64, - pub currref: c_int, - pub maxidx: c_int, - pub lastmaxidx: c_int, - - // Used to find tref zero in PTS mode - pub minidx: c_int, - pub lastminidx: c_int, - - // Used to remember the max temporal reference number (poc mode) - pub maxtref: c_int, - pub last_gop_maxtref: c_int, - - // Used for PTS ordering of CC blocks - pub currefpts: i64, - pub last_pic_order_cnt_lsb: i64, - pub last_slice_pts: i64, -} - -#[repr(C)] -pub struct DtvccCtx { - pub is_active: c_int, - pub active_services_count: c_int, - pub services_active: [c_int; CCX_DTVCC_MAX_SERVICES], // 0 - inactive, 1 - active - pub report_enabled: c_int, - pub report: *mut CcxDecoderDtvccReport, - pub decoders: [DtvccServiceDecoder; CCX_DTVCC_MAX_SERVICES], - pub current_packet: [c_uchar; CCX_DTVCC_MAX_PACKET_LENGTH], - pub current_packet_length: c_int, - pub is_current_packet_header_parsed: c_int, - pub last_sequence: c_int, - pub encoder: *mut c_void, // we can't include header, so keeping it this way - pub no_rollup: c_int, - pub timing: *mut CcxCommonTimingCtx, -} - -#[repr(C)] -pub struct CcxDecoderDtvccReport { - pub reset_count: c_int, - pub services: [c_uint; CCX_DTVCC_MAX_SERVICES], -} - -#[repr(C)] -pub struct DtvccServiceDecoder { - pub windows: [DtvccWindow; CCX_DTVCC_MAX_WINDOWS], - pub current_window: c_int, - pub tv: *mut DtvccTvScreen, - pub cc_count: c_int, -} - -#[repr(C)] -pub struct DtvccWindow { - pub is_defined: c_int, - pub number: c_int, - pub priority: c_int, - pub col_lock: c_int, - pub row_lock: c_int, - pub visible: c_int, - pub anchor_vertical: c_int, - pub relative_pos: c_int, - pub anchor_horizontal: c_int, - pub row_count: c_int, - pub anchor_point: c_int, - pub col_count: c_int, - pub pen_style: c_int, - pub win_style: c_int, - pub commands: [c_uchar; 6], // Commands used to create this window - pub attribs: DtvccWindowAttribs, - pub pen_row: c_int, - pub pen_column: c_int, - pub rows: [*mut DtvccSymbol; CCX_DTVCC_MAX_ROWS], - pub pen_colors: [[DtvccPenColor; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_MAX_ROWS], - pub pen_attribs: [[DtvccPenAttribs; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_MAX_ROWS], - pub pen_color_pattern: DtvccPenColor, - pub pen_attribs_pattern: DtvccPenAttribs, - pub memory_reserved: c_int, - pub is_empty: c_int, - pub time_ms_show: i64, // Assuming LLONG is equivalent to i64 - pub time_ms_hide: i64, -} - -#[repr(C)] -pub struct DtvccWindowAttribs { - pub justify: c_int, - pub print_direction: c_int, - pub scroll_direction: c_int, - pub word_wrap: c_int, - pub display_effect: c_int, - pub effect_direction: c_int, - pub effect_speed: c_int, - pub fill_color: c_int, - pub fill_opacity: c_int, - pub border_type: c_int, - pub border_color: c_int, -} - -#[repr(C)] -pub struct DtvccSymbol { - pub sym: u16, // symbol itself, at least 16 bit - pub init: c_uchar, // initialized or not. could be 0 or 1 -} - -#[repr(C)] -pub struct DtvccPenColor { - pub fg_color: c_int, - pub fg_opacity: c_int, - pub bg_color: c_int, - pub bg_opacity: c_int, - pub edge_color: c_int, -} - -#[repr(C)] -pub struct DtvccPenAttribs { - pub pen_size: c_int, - pub offset: c_int, - pub text_tag: c_int, - pub font_tag: c_int, - pub edge_type: c_int, - pub underline: c_int, - pub italic: c_int, -} - -#[repr(C)] -pub struct DtvccTvScreen { - pub chars: [[DtvccSymbol; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], - pub pen_colors: [[DtvccPenColor; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], - pub pen_attribs: [[DtvccPenAttribs; CCX_DTVCC_SCREENGRID_COLUMNS]; CCX_DTVCC_SCREENGRID_ROWS], - pub time_ms_show: i64, - pub time_ms_hide: i64, - pub cc_count: c_uint, - pub service_number: c_int, - pub old_cc_time_end: c_int, -} - -const NUM_BYTES_PER_PACKET: usize = 35; -const NUM_XDS_BUFFERS: usize = 9; - -#[repr(C)] -pub struct CcxDecodersXdsContext { - // Program Identification Number (Start Time) for current program - pub current_xds_min: c_int, - pub current_xds_hour: c_int, - pub current_xds_date: c_int, - pub current_xds_month: c_int, - pub current_program_type_reported: c_int, // No. - pub xds_start_time_shown: c_int, - pub xds_program_length_shown: c_int, - pub xds_program_description: [[c_uchar; 33]; 8], - - pub current_xds_network_name: [c_uchar; 33], - pub current_xds_program_name: [c_uchar; 33], - pub current_xds_call_letters: [c_uchar; 7], - pub current_xds_program_type: [c_uchar; 33], - - pub xds_buffers: [XdsBuffer; NUM_XDS_BUFFERS], - pub cur_xds_buffer_idx: c_int, - pub cur_xds_packet_class: c_int, - pub cur_xds_payload: *mut c_uchar, - pub cur_xds_payload_length: c_int, - pub cur_xds_packet_type: c_int, - pub timing: *mut CcxCommonTimingCtx, - - pub current_ar_start: c_uint, - pub current_ar_end: c_uint, - - pub xds_write_to_file: c_int, // Set to 1 if XDS data is to be written to file -} - -#[repr(C)] -pub struct XdsBuffer { - pub in_use: c_uint, - pub xds_class: c_int, - pub xds_type: c_int, - pub bytes: [c_uchar; NUM_BYTES_PER_PACKET], // Class + type (repeated for convenience) + data + zero - pub used_bytes: c_uchar, -} - -#[repr(C)] -pub struct CcxDecoderVbiCtx { - pub vbi_decoder_inited: c_int, - pub zvbi_decoder: VbiRawDecoder, - // vbi3_raw_decoder zvbi_decoder; - // #ifdef VBI_DEBUG - // pub vbi_debug_dump: *mut FILE, - // #endif -} - -#[repr(C)] -pub struct VbiRawDecoder { - /* Sampling parameters */ - /** - * Either 525 (M/NTSC, M/PAL) or 625 (PAL, SECAM), describing the - * scan line system all line numbers refer to. - */ - pub scanning: c_int, - /** - * Format of the raw vbi data. - */ - pub sampling_format: VbiPixfmt, - /** - * Sampling rate in Hz, the number of samples or pixels - * captured per second. - */ - pub sampling_rate: c_int, // Hz - /** - * Number of samples or pixels captured per scan line, - * in bytes. This determines the raw vbi image width and you - * want it large enough to cover all data transmitted in the line (with - * headroom). - */ - pub bytes_per_line: c_int, - /** - * The distance from 0H (leading edge hsync, half amplitude point) - * to the first sample (pixel) captured, in samples (pixels). You want - * an offset small enough not to miss the start of the data - * transmitted. - */ - pub offset: c_int, // 0H, samples - /** - * First scan line to be captured, first and second field - * respectively, according to the ITU-R line numbering scheme - * (see vbi_sliced). Set to zero if the exact line number isn't - * known. - */ - pub start: [c_int; 2], // ITU-R numbering - /** - * Number of scan lines captured, first and second - * field respectively. This can be zero if only data from one - * field is required. The sum @a count[0] + @a count[1] determines the - * raw vbi image height. - */ - pub count: [c_int; 2], // field lines - /** - * In the raw vbi image, normally all lines of the second - * field are supposed to follow all lines of the first field. When - * this flag is set, the scan lines of first and second field - * will be interleaved in memory. This implies @a count[0] and @a count[1] - * are equal. - */ - pub interlaced: c_int, - /** - * Fields must be stored in temporal order, i. e. as the - * lines have been captured. It is assumed that the first field is - * also stored first in memory, however if the hardware cannot reliable - * distinguish fields this flag shall be cleared, which disables - * decoding of data services depending on the field number. - */ - pub synchronous: c_int, - - pub services: c_uint, - pub num_jobs: c_int, - - pub pattern: *mut i8, - pub jobs: [VbiRawDecoderJob; 8], -} - -#[repr(C)] -pub struct VbiRawDecoderJob { - pub id: c_uint, - pub offset: c_int, - pub slicer: VbiBitSlicer, -} - -#[repr(C)] -pub enum VbiPixfmt { - Yuv420 = 1, - Yuyv, - Yvyu, - Uyvy, - Vyuy, - Pal8, - Rgba32Le = 32, - Rgba32Be, - Bgra32Le, - Bgra32Be, - Abgr32Be, /* = 32, // synonyms */ - Abgr32Le, - Argb32Be, - Argb32Le, - Rgb24, - Bgr24, - Rgb16Le, - Rgb16Be, - Bgr16Le, - Bgr16Be, - Rgba15Le, - Rgba15Be, - Bgra15Le, - Bgra15Be, - Argb15Le, - Argb15Be, - Abgr15Le, - Abgr15Be, -} - -#[repr(C)] -pub struct VbiBitSlicer { - pub func: Option< - extern "C" fn(slicer: *mut VbiBitSlicer, raw: *mut c_uchar, buf: *mut c_uchar) -> c_int, - >, - pub cri: c_uint, - pub cri_mask: c_uint, - pub thresh: c_int, - pub cri_bytes: c_int, - pub cri_rate: c_int, - pub oversampling_rate: c_int, - pub phase_shift: c_int, - pub step: c_int, - pub frc: c_uint, - pub frc_bits: c_int, - pub payload: c_int, - pub endian: c_int, - pub skip: c_int, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct ISDBSubContext { - pub nb_char: c_int, - pub nb_line: c_int, - pub timestamp: u64, - pub prev_timestamp: u64, - pub text_list_head: ListHead, - pub buffered_text: ListHead, - pub current_state: ISDBSubState, - pub tmd: IsdbTmd, - pub nb_lang: c_int, - pub offset_time: OffsetTime, - pub dmf: c_uchar, - pub dc: c_uchar, - pub cfg_no_rollup: c_int, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct ISDBSubState { - pub auto_display: c_int, // bool - pub rollup_mode: c_int, // bool - pub need_init: c_uchar, // bool - pub clut_high_idx: c_uchar, - pub fg_color: c_uint, - pub bg_color: c_uint, - pub hfg_color: c_uint, - pub hbg_color: c_uint, - pub mat_color: c_uint, - pub raster_color: c_uint, - pub layout_state: ISDBSubLayout, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct ISDBSubLayout { - pub format: WritingFormat, - pub display_area: DispArea, - pub font_size: c_int, - pub font_scale: FScale, - pub cell_spacing: Spacing, - pub cursor_pos: ISDBPos, - pub ccc: IsdbCCComposition, - pub acps: [c_int; 2], -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct DispArea { - pub x: c_int, - pub y: c_int, - pub w: c_int, - pub h: c_int, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct FScale { - pub fscx: c_int, - pub fscy: c_int, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct Spacing { - pub col: c_int, - pub row: c_int, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct ISDBPos { - pub x: c_int, - pub y: c_int, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub struct OffsetTime { - pub hour: c_int, - pub min: c_int, - pub sec: c_int, - pub milli: c_int, -} - -#[repr(C)] -#[derive(Copy, Clone, Debug)] -pub enum WritingFormat { - HorizontalStdDensity = 0, - VerticalStdDensity = 1, - HorizontalHighDensity = 2, - VerticalHighDensity = 3, - HorizontalWesternLang = 4, - Horizontal1920x1080 = 5, - Vertical1920x1080 = 6, - Horizontal960x540 = 7, - Vertical960x540 = 8, - Horizontal720x480 = 9, - Vertical720x480 = 10, - Horizontal1280x720 = 11, - Vertical1280x720 = 12, - HorizontalCustom = 100, - None, -} - -#[derive(Debug, Clone)] -#[repr(C)] -pub enum IsdbCCComposition { - None = 0, - And = 2, - Or = 3, - Xor = 4, -} - -#[derive(Debug, Clone, Copy)] -#[repr(C)] -pub enum IsdbTmd { - Free = 0, - RealTime = 0x1, - OffsetTime = 0x2, -} - -#[repr(C)] -pub enum FontSize { - SmallFontSize, - MiddleFontSize, - StandardFontSize, -} - -#[repr(C)] -pub enum CsiCommand { - Gsm = 0x42, - Swf = 0x53, - Ccc = 0x54, - Sdf = 0x56, - Ssm = 0x57, - Shs = 0x58, - Svs = 0x59, - Pld = 0x5B, - Plu = 0x5C, - Gaa = 0x5D, - Src = 0x5E, - Sdp = 0x5F, - Acps = 0x61, - Tcc = 0x62, - Orn = 0x63, - Mdf = 0x64, - Cfs = 0x65, - Xcs = 0x66, - Pra = 0x68, - Acs = 0x69, - Rcs = 0x6E, - Scs = 0x6F, -} - -#[repr(C)] -pub enum Color { - CcxIsdbBlack, - FiRed, - FiGreen, - FiYellow, - FiBlue, - FiMagenta, - FiCyan, - FiWhite, - CcxIsdbTransparent, - HiRed, - HiGreen, - HiYellow, - HiBlue, - HiMagenta, - HiCyan, - HiWhite, -} - -#[no_mangle] -pub const fn rgba(r: u8, g: u8, b: u8, a: u8) -> u32 { - ((255 - a as u32) << 24) | ((b as u32) << 16) | ((g as u32) << 8) | (r as u32) -} - -type Rgba = u32; -pub const ZATA: u32 = 0; -pub const DEFAULT_CLUT: [Rgba; 128] = [ - // 0-7 - rgba(0, 0, 0, 255), - rgba(255, 0, 0, 255), - rgba(0, 255, 0, 255), - rgba(255, 255, 0, 255), - rgba(0, 0, 255, 255), - rgba(255, 0, 255, 255), - rgba(0, 255, 255, 255), - rgba(255, 255, 255, 255), - // 8-15 - rgba(0, 0, 0, 0), - rgba(170, 0, 0, 255), - rgba(0, 170, 0, 255), - rgba(170, 170, 0, 255), - rgba(0, 0, 170, 255), - rgba(170, 0, 170, 255), - rgba(0, 170, 170, 255), - rgba(170, 170, 170, 255), - // 16-23 - rgba(0, 0, 85, 255), - rgba(0, 85, 0, 255), - rgba(0, 85, 85, 255), - rgba(0, 85, 170, 255), - rgba(0, 85, 255, 255), - rgba(0, 170, 85, 255), - rgba(0, 170, 255, 255), - rgba(0, 255, 85, 255), - // 24-31 - rgba(0, 255, 170, 255), - rgba(85, 0, 0, 255), - rgba(85, 0, 85, 255), - rgba(85, 0, 170, 255), - rgba(85, 0, 255, 255), - rgba(85, 85, 0, 255), - rgba(85, 85, 85, 255), - rgba(85, 85, 170, 255), - // 32-39 - rgba(85, 85, 255, 255), - rgba(85, 170, 0, 255), - rgba(85, 170, 85, 255), - rgba(85, 170, 170, 255), - rgba(85, 170, 255, 255), - rgba(85, 255, 0, 255), - rgba(85, 255, 85, 255), - rgba(85, 255, 170, 255), - // 40-47 - rgba(85, 255, 255, 255), - rgba(170, 0, 85, 255), - rgba(170, 0, 255, 255), - rgba(170, 85, 0, 255), - rgba(170, 85, 85, 255), - rgba(170, 85, 170, 255), - rgba(170, 85, 255, 255), - rgba(170, 170, 85, 255), - // 48-55 - rgba(170, 170, 255, 255), - rgba(170, 255, 0, 255), - rgba(170, 255, 85, 255), - rgba(170, 255, 170, 255), - rgba(170, 255, 255, 255), - rgba(255, 0, 85, 255), - rgba(255, 0, 170, 255), - rgba(255, 85, 0, 255), - // 56-63 - rgba(255, 85, 85, 255), - rgba(255, 85, 170, 255), - rgba(255, 85, 255, 255), - rgba(255, 170, 0, 255), - rgba(255, 170, 85, 255), - rgba(255, 170, 170, 255), - rgba(255, 170, 255, 255), - rgba(255, 255, 85, 255), - // 64 - rgba(255, 255, 170, 255), - // 65-127 are calculated later. - // Initialize remaining elements to 0 - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, - ZATA, -]; - -#[repr(C)] -pub struct ISDBText { - pub buf: *mut c_char, - pub len: usize, - pub used: usize, - pub pos: ISDBPos, - pub txt_tail: usize, // tail of the text, excluding trailing control sequences. - pub timestamp: c_ulonglong, // Time Stamp when first string is received - pub refcount: c_int, - pub list: ListHead, -} - -#[repr(i32)] -pub enum CcxStreamMode { - ElementaryOrNotFound = 0, - Transport = 1, - Program = 2, - Asf = 3, - McPoodlesRaw = 4, - Rcwt = 5, // Raw Captions With Time, not used yet. - Myth = 6, // Use the myth loop - Mp4 = 7, // MP4, ISO- - Wtv = 9, - Gxf = 11, - Mkv = 12, - Mxf = 13, - Autodetect = 16, -} - -#[repr(i32)] -pub enum CcxOutputDateFormat { - None = 0, - Hhmmss = 1, - Seconds = 2, - Date = 3, - Hhmmssms = 4, // HH:MM:SS,MILIS (.srt style) -} - -pub struct CcxEncodersTranscriptFormat { - show_start_time: i32, // Show start and/or end time. - show_end_time: i32, // Show start and/or end time. - show_mode: i32, // Show which mode if available (E.G.: POP, RU1, ...) - show_cc: i32, // Show which CC channel has been captured. - relative_timestamp: i32, // Timestamps relative to start of sample or in UTC? - xds: i32, // Show XDS or not - use_colors: i32, // Add colors or no colors - is_final: i32, // Used to determine if these parameters should be changed afterwards. -} - -#[repr(i32)] -pub enum CcxDataSource { - File = 0, - Stdin = 1, - Network = 2, - Tcp = 3, -} - -#[repr(C)] -pub struct CcxSOptions { - pub extract: i32, - pub no_rollup: i32, - pub noscte20: i32, - pub webvtt_create_css: i32, - pub cc_channel: i32, - pub buffer_input: i32, - pub nofontcolor: i32, - pub nohtmlescape: i32, - pub notypesetting: i32, - pub extraction_start: CcxBoundaryTime, - pub extraction_end: CcxBoundaryTime, - pub print_file_reports: i32, - pub settings_608: CcxDecoder608Settings, - pub settings_dtvcc: CcxDecoderDtvccSettings, - pub is_608_enabled: i32, - pub is_708_enabled: i32, - pub millis_separator: c_char, - pub binary_concat: i32, - pub use_gop_as_pts: i32, - pub fix_padding: i32, - pub gui_mode_reports: i32, - pub no_progress_bar: i32, - pub sentence_cap_file: *mut c_char, - pub live_stream: i32, - pub filter_profanity_file: *mut c_char, - pub messages_target: i32, - pub timestamp_map: i32, - pub dolevdist: i32, - pub levdistmincnt: i32, - pub levdistmaxpct: i32, - pub investigate_packets: i32, - pub fullbin: i32, - pub nosync: i32, - pub hauppauge_mode: u32, - pub wtvconvertfix: i32, - pub wtvmpeg2: i32, - pub auto_myth: i32, - pub mp4vidtrack: u32, - pub extract_chapters: i32, - pub usepicorder: i32, - pub xmltv: i32, - pub xmltvliveinterval: i32, - pub xmltvoutputinterval: i32, - pub xmltvonlycurrent: i32, - pub keep_output_closed: i32, - pub force_flush: i32, - pub append_mode: i32, - pub ucla: i32, - pub tickertext: i32, - pub hardsubx: i32, - pub hardsubx_and_common: i32, - pub dvblang: *mut c_char, - pub ocrlang: *const c_char, - pub ocr_oem: i32, - pub psm: i32, - pub ocr_quantmode: i32, - pub mkvlang: *mut c_char, - pub analyze_video_stream: i32, - pub hardsubx_ocr_mode: i32, - pub hardsubx_subcolor: i32, - pub hardsubx_min_sub_duration: f32, - pub hardsubx_detect_italics: i32, - pub hardsubx_conf_thresh: f32, - pub hardsubx_hue: f32, - pub hardsubx_lum_thresh: f32, - pub transcript_settings: CcxEncodersTranscriptFormat, - pub date_format: CcxOutputDateFormat, - pub send_to_srv: u32, - pub write_format: CcxOutputFormat, - pub write_format_rewritten: i32, - pub use_ass_instead_of_ssa: i32, - pub use_webvtt_styling: i32, - pub debug_mask: i64, - pub debug_mask_on_debug: i64, - pub udpsrc: *mut c_char, - pub udpaddr: *mut c_char, - pub udpport: u32, - pub tcpport: *mut c_char, - pub tcp_password: *mut c_char, - pub tcp_desc: *mut c_char, - pub srv_addr: *mut c_char, - pub srv_port: *mut c_char, - pub noautotimeref: i32, - pub input_source: CcxDatasource, - pub output_filename: *mut c_char, - pub inputfile: *mut *mut c_char, - pub num_input_files: i32, - pub demux_cfg: DemuxerCfg, - pub enc_cfg: EncoderCfg, - pub subs_delay: i64, - pub cc_to_stdout: i32, - pub pes_header_to_stdout: i32, - pub ignore_pts_jumps: i32, - pub multiprogram: i32, - pub out_interval: i32, - pub segment_on_key_frames_only: i32, -} - -#[repr(C)] -pub struct CcxDecoder608Settings { - pub direct_rollup: i32, - pub force_rollup: i32, - pub no_rollup: i32, - pub default_color: CcxDecoder608ColorCode, - pub screens_to_process: i32, - pub report: *mut CcxDecoder608Report, -} - -#[repr(C)] -pub enum CcxDecoder608ColorCode { - ColWhite = 0, - ColGreen = 1, - ColBlue = 2, - ColCyan = 3, - ColRed = 4, - ColYellow = 5, - ColMagenta = 6, - ColUserdefined = 7, - ColBlack = 8, - ColTransparent = 9, - ColMax, -} - -#[repr(C)] -pub struct CcxDecoder608Report { - pub xds: u8, - pub cc_channels: [u8; 4], -} - -#[repr(C)] -pub struct CcxDecoderDtvccSettings { - pub enabled: i32, - pub print_file_reports: i32, - pub no_rollup: i32, - pub report: *mut CcxDecoderDtvccReport, - pub active_services_count: i32, - pub services_enabled: [i32; CCX_DTVCC_MAX_SERVICES], - pub timing: *mut CcxCommonTimingCtx, -} - -#[repr(C)] -pub enum CcxDatasource { - File = 0, - Stdin = 1, - Network = 2, - Tcp = 3, -} - -#[repr(C)] -pub struct DemuxerCfg { - pub m2ts: i32, - pub auto_stream: CcxStreamModeEnum, - pub codec: CcxCodeType, - pub nocodec: CcxCodeType, - pub ts_autoprogram: u32, - pub ts_allprogram: u32, - pub ts_cappids: [u32; 128], - pub nb_ts_cappid: i32, - pub ts_forced_cappid: u32, - pub ts_forced_program: i32, - pub ts_forced_program_selected: u32, - pub ts_datastreamtype: i32, - pub ts_forced_streamtype: u32, -} - -#[repr(C)] -pub struct EncoderCfg { - pub extract: i32, - pub dtvcc_extract: i32, - pub gui_mode_reports: i32, - pub output_filename: *mut c_char, - pub write_format: CcxOutputFormat, - pub keep_output_closed: i32, - pub force_flush: i32, - pub append_mode: i32, - pub ucla: i32, - pub encoding: CcxEncodingType, - pub date_format: CcxOutputDateFormat, - pub millis_separator: c_char, - pub autodash: i32, - pub trim_subs: i32, - pub sentence_cap: i32, - pub splitbysentence: i32, - pub filter_profanity: i32, - pub with_semaphore: i32, - pub start_credits_text: *mut c_char, - pub end_credits_text: *mut c_char, - pub startcreditsnotbefore: CcxBoundaryTime, - pub startcreditsnotafter: CcxBoundaryTime, - pub startcreditsforatleast: CcxBoundaryTime, - pub startcreditsforatmost: CcxBoundaryTime, - pub endcreditsforatleast: CcxBoundaryTime, - pub endcreditsforatmost: CcxBoundaryTime, - pub transcript_settings: CcxEncodersTranscriptFormat, - pub send_to_srv: u32, - pub no_bom: i32, - pub first_input_file: *mut c_char, - pub multiple_files: i32, - pub no_font_color: i32, - pub no_type_setting: i32, - pub cc_to_stdout: i32, - pub line_terminator_lf: i32, - pub subs_delay: i64, - pub program_number: i32, - pub in_format: u8, - pub nospupngocr: i32, - pub force_dropframe: i32, - pub render_font: *mut c_char, - pub render_font_italics: *mut c_char, - pub services_enabled: [i32; CCX_DTVCC_MAX_SERVICES], - pub services_charsets: *mut *mut c_char, - pub all_services_charset: *mut c_char, - pub extract_only_708: i32, -} - -#[repr(C)] -pub enum CcxStreamModeEnum { - ElementaryOrNotFound = 0, - Transport = 1, - Program = 2, - Asf = 3, - McPoodlesRaw = 4, - Rcwt = 5, - Myth = 6, - Mp4 = 7, - Wtv = 9, - Gxf = 11, - Mkv = 12, - Mxf = 13, - Autodetect = 16, -} - -pub struct CcxOptions { - pub messages_target: i32, -} diff --git a/src/rust/lib_ccxr/src/isdbs/isdbs.rs b/src/rust/lib_ccxr/src/isdbs/isdbs.rs deleted file mode 100644 index 14791bd94..000000000 --- a/src/rust/lib_ccxr/src/isdbs/isdbs.rs +++ /dev/null @@ -1,1204 +0,0 @@ -extern crate libc; -use crate::isdbs::*; -use isdbd::*; - -use std::alloc::{alloc, realloc, Layout}; - -use std::io::{self, Write}; -use std::os::raw::{c_char, c_void}; -use std::ptr; - -#[allow(unused_assignments)] -#[allow(dead_code)] -#[allow(unused_comparisons)] -#[no_mangle] -pub fn is_horizontal_layout(format: WritingFormat) -> bool { - matches!( - format, - WritingFormat::HorizontalStdDensity - | WritingFormat::HorizontalHighDensity - | WritingFormat::HorizontalWesternLang - | WritingFormat::Horizontal1920x1080 - | WritingFormat::Horizontal960x540 - | WritingFormat::Horizontal720x480 - | WritingFormat::Horizontal1280x720 - | WritingFormat::HorizontalCustom - ) -} - -#[no_mangle] -pub fn layout_get_width(format: WritingFormat) -> u32 { - match format { - WritingFormat::Horizontal960x540 | WritingFormat::Vertical960x540 => 960, - _ => 720, - } -} - -#[no_mangle] -pub fn layout_get_height(format: WritingFormat) -> u32 { - match format { - WritingFormat::Horizontal960x540 | WritingFormat::Vertical960x540 => 540, - _ => 480, - } -} - -pub fn isdb_set_global_time(ctx: &mut ISDBSubContext, timestamp: u64) { - ctx.timestamp = timestamp; -} - -pub fn init_layout(ls: *mut ISDBSubLayout) { - unsafe { - (*ls).font_size = 36; - (*ls).display_area.x = 0; - (*ls).display_area.y = 0; - (*ls).font_scale.fscx = 100; - (*ls).font_scale.fscy = 100; - } -} - -pub fn init_list_head(ptr: *mut ListHead) { - unsafe { - (*ptr).next = ptr; - (*ptr).prev = ptr; - } -} - -extern "C" { - pub fn free(ptr: *mut c_void); -} - -pub unsafe fn freep(arg: *mut *mut c_void) { - if !arg.is_null() && !(*arg).is_null() { - free(*arg); - *arg = ptr::null_mut(); - } -} - -pub const LIST_POISON1: *mut ListHead = 0x001 as *mut ListHead; -pub const LIST_POISON2: *mut ListHead = 0x002 as *mut ListHead; - -#[no_mangle] -pub unsafe fn __list_del(prev: *mut ListHead, next: *mut ListHead) { - (*next).prev = prev; - (*prev).next = next; -} - -#[no_mangle] -pub unsafe fn list_del(entry: *mut ListHead) { - __list_del((*entry).prev, (*entry).next); - (*entry).next = LIST_POISON1; - (*entry).prev = LIST_POISON2; -} - -pub unsafe fn list_for_each_entry_safe( - mut pos: *mut ISDBText, - mut n: *mut ISDBText, - head: *mut ListHead, - member: *const u8, -) { - pos = list_entry((*head).next, member); - n = list_entry((*pos).list.next, member); - while &(*pos).list as *const ListHead != head { - // Your code to process each entry goes here - pos = n; - n = list_entry((*n).list.next, member); - } -} - -pub unsafe fn list_entry(ptr: *mut ListHead, member: *const u8) -> *mut ISDBText { - container_of(ptr, member) -} - -pub unsafe fn container_of(ptr: *mut ListHead, member: *const u8) -> *mut ISDBText { - (ptr as *mut u8).offset(-(ccx_offsetof(member) as isize)) as *mut ISDBText -} - -pub unsafe fn ccx_offsetof(_member: *const u8) -> usize { - let offset = std::mem::offset_of!(ISDBText, list); - offset -} - -pub fn delete_isdb_decoder(ctx: *mut ISDBSubContext) { - unsafe { - let mut text: *mut ISDBText = ptr::null_mut(); - let mut text1: *mut ISDBText = ptr::null_mut(); - - list_for_each_entry_safe(text, text1, &mut (*ctx).text_list_head, b"list\0".as_ptr()); - while !text.is_null() { - list_del(&mut (*text).list); - free((*text).buf as *mut c_void); - free(text as *mut c_void); - list_for_each_entry_safe(text, text1, &mut (*ctx).text_list_head, b"list\0".as_ptr()); - } - - list_for_each_entry_safe(text, text1, &mut (*ctx).buffered_text, b"list\0".as_ptr()); - while !text.is_null() { - list_del(&mut (*text).list); - free((*text).buf as *mut c_void); - free(text as *mut c_void); - list_for_each_entry_safe(text, text1, &mut (*ctx).buffered_text, b"list\0".as_ptr()); - } - } -} - -// this is in case DEBUG is not defined, -// which is the case in the C code -#[no_mangle] -pub fn isdb_log(args: std::fmt::Arguments) { - // nothing said, nothing done -} - -#[no_mangle] -pub fn allocate_text_node(_ls: &ISDBSubLayout) -> *mut ISDBText { - unsafe { - let text_layout = Layout::new::(); - let text_ptr = alloc(text_layout) as *mut ISDBText; - if text_ptr.is_null() { - return ptr::null_mut(); - } - - (*text_ptr).used = 0; - let buf_layout = Layout::from_size_align(128, 1).unwrap(); - let buf_ptr = alloc(buf_layout) as *mut u8; - if buf_ptr.is_null() { - return ptr::null_mut(); - } - - (*text_ptr).buf = buf_ptr as *mut i8; - (*text_ptr).len = 128; - *(*text_ptr).buf = 0; - text_ptr - } -} - -pub fn reserve_buf(text: &mut ISDBText, len: usize) -> i32 { - const CCX_OK: i32 = 0; - const CCX_ENOMEM: i32 = 1; - - if text.len >= text.used + len { - return CCX_OK; - } - - // alloc always in 128 ka multiple - let blen = ((text.used + len + 127) >> 7) << 7; - unsafe { - let layout = Layout::from_size_align(text.len, 1).unwrap(); - let new_ptr = realloc(text.buf as *mut u8, layout, blen) as *mut u8; - if new_ptr.is_null() { - isdb_log(format_args!("ISDB: out of memory for ctx->text.buf\n")); - return CCX_ENOMEM; - } - text.buf = new_ptr as *mut i8; - } - text.len = blen; - isdb_log(format_args!("expanded ctx->text({})\n", blen)); - CCX_OK -} - -#[no_mangle] -pub unsafe fn __list_add(elem: *mut ListHead, prev: *mut ListHead, next: *mut ListHead) { - (*next).prev = elem; - (*elem).next = next; - (*elem).prev = prev; - (*prev).next = elem; -} - -#[no_mangle] -pub unsafe fn list_add_tail(elem: *mut ListHead, head: *mut ListHead) { - __list_add(elem, (*head).prev, head); -} - -pub fn list_for_each_entry(head: *mut ListHead, member: usize, mut func: F) -where - F: FnMut(*mut ISDBText), -{ - unsafe { - let member_ptr = member.to_string().as_ptr(); - let mut pos = list_entry((*head).next, member_ptr); - while &(*pos).list as *const _ != head { - func(pos); - pos = list_entry((*pos).list.next, member_ptr); - } - } -} - -pub fn list_add(elem: &mut ListHead, head: &mut ListHead) { - unsafe { - __list_add(elem, head, (*head).next); - } -} - -pub fn append_char(ctx: &mut ISDBSubContext, ch: char) -> i32 { - let ls = &mut ctx.current_state.layout_state; - let mut text: *mut ISDBText = std::ptr::null_mut(); - let cur_lpos; - let csp; - - if is_horizontal_layout(ls.format) { - cur_lpos = ls.cursor_pos.x; - csp = ls.font_size * ls.font_scale.fscx / 100; - } else { - cur_lpos = ls.cursor_pos.y; - csp = ls.font_size * ls.font_scale.fscy / 100; - } - - unsafe { - list_for_each_entry( - &mut ctx.text_list_head as *mut ListHead, - std::mem::offset_of!(ISDBText, list), - |t| { - text = t; - let text_lpos = if is_horizontal_layout(ls.format) { - (*text).pos.x - } else { - (*text).pos.y - }; - - if text_lpos == cur_lpos { - return; - } else if text_lpos > cur_lpos { - let text1 = allocate_text_node(ls); - (*text1).pos.x = ls.cursor_pos.x; - (*text1).pos.y = ls.cursor_pos.y; - list_add(&mut (*text1).list, &mut *(*text).list.prev); - text = text1; - return; - } - }, - ); - - if &(*text).list as *const ListHead == &ctx.text_list_head as *const ListHead { - text = allocate_text_node(ls); - (*text).pos.x = ls.cursor_pos.x; - (*text).pos.y = ls.cursor_pos.y; - list_add_tail(&mut (*text).list, &mut ctx.text_list_head); - } - - if is_horizontal_layout(ls.format) { - if ls.cursor_pos.y < (*text).pos.y { - (*text).pos.y = ls.cursor_pos.y; - (*text).used = 0; - } - ls.cursor_pos.y += csp; - (*text).pos.y += csp; - } else { - if ls.cursor_pos.x < (*text).pos.x { - (*text).pos.x = ls.cursor_pos.x; - (*text).used = 0; - } - ls.cursor_pos.x += csp; - (*text).pos.x += csp; - } - - reserve_buf(&mut *text, 2); - (*text).buf.add((*text).used).write(ch as u8 as i8); - (*text).used += 1; - (*text).buf.add((*text).used).write(0); - - 1 - } -} - -pub fn ccx_strstr_ignorespace(str1: *const c_char, str2: *const c_char) -> i32 { - unsafe { - let mut i = 0; - while *str2.add(i) != 0 { - if (*str2.add(i) as u8).is_ascii_whitespace() { - i += 1; - continue; - } - if *str2.add(i) != *str1.add(i) { - return 0; - } - i += 1; - } - 1 - } -} - -pub fn list_empty(head: &ListHead) -> bool { - head.next as *mut _ == head as *const _ as *mut _ -} - -pub fn get_text(ctx: &mut ISDBSubContext, buffer: &mut [u8], len: usize) -> usize { - unsafe { - let ls = &ctx.current_state.layout_state; - let mut text: *mut ISDBText = std::ptr::null_mut(); - let mut sb_text: *mut ISDBText = std::ptr::null_mut(); - let mut sb_temp: *mut ISDBText = std::ptr::null_mut(); - - // check no overflow for user - let mut index = 0; - - if ctx.cfg_no_rollup != 0 || (ctx.cfg_no_rollup == ctx.current_state.rollup_mode) { - if list_empty(&ctx.buffered_text) { - list_for_each_entry( - &mut ctx.text_list_head as *mut ListHead, - std::mem::offset_of!(ISDBText, list), - |text| { - sb_text = allocate_text_node(ls); - list_add_tail(&mut (*sb_text).list, &mut ctx.buffered_text); - reserve_buf(&mut *sb_text, (*text).used); - std::ptr::copy_nonoverlapping((*text).buf, (*sb_text).buf, (*text).used); - *(*sb_text).buf.offset((*text).used as isize) = 0; - (*sb_text).used = (*text).used; - }, - ); - return 0; - } - - list_for_each_entry( - &mut ctx.text_list_head as *mut ListHead, - std::mem::offset_of!(ISDBText, list), - |text| { - let mut found = false; - list_for_each_entry( - &mut ctx.buffered_text as *mut ListHead, - std::mem::offset_of!(ISDBText, list), - |sb_text| { - if !found && ccx_strstr_ignorespace((*text).buf, (*sb_text).buf) != 0 { - found = true; - if ccx_strstr_ignorespace((*sb_text).buf, (*text).buf) == 0 { - reserve_buf(&mut *sb_text, (*text).used); - std::ptr::copy_nonoverlapping( - (*text).buf, - (*sb_text).buf, - (*text).used, - ); - } - } - }, - ); - if !found { - sb_text = allocate_text_node(ls); - list_add_tail(&mut (*sb_text).list, &mut ctx.buffered_text); - reserve_buf(&mut *sb_text, (*text).used); - std::ptr::copy_nonoverlapping((*text).buf, (*sb_text).buf, (*text).used); - *(*sb_text).buf.offset((*text).used as isize) = 0; - (*sb_text).used = (*text).used; - } - }, - ); - - list_for_each_entry_safe(sb_text, sb_temp, &mut ctx.buffered_text, b"list\0".as_ptr()); - while !sb_text.is_null() { - let mut found = false; - list_for_each_entry( - &mut ctx.text_list_head as *mut ListHead, - std::mem::offset_of!(ISDBText, list), - |text| { - if ccx_strstr_ignorespace((*text).buf, (*sb_text).buf) != 0 { - found = true; - } - }, - ); - if !found && len - index > (*sb_text).used + 2 && (*sb_text).used > 0 { - std::ptr::copy_nonoverlapping( - (*sb_text).buf, - buffer[index..].as_mut_ptr() as *mut i8, - (*sb_text).used, - ); - buffer[(*sb_text).used + index] = b'\n'; - buffer[(*sb_text).used + index + 1] = b'\r'; - index += (*sb_text).used + 2; - (*sb_text).used = 0; - list_del(&mut (*sb_text).list); - free((*sb_text).buf as *mut c_void); - free(sb_text as *mut c_void); - } - sb_text = sb_temp; - sb_temp = list_entry((*sb_temp).list.next, b"list\0".as_ptr()); - } - } else { - list_for_each_entry( - &mut ctx.text_list_head as *mut ListHead, - std::mem::offset_of!(ISDBText, list), - |text| { - if len - index > (*text).used + 2 && (*text).used > 0 { - std::ptr::copy_nonoverlapping( - (*text).buf, - buffer[index..].as_mut_ptr() as *mut i8, - (*text).used, - ); - buffer[(*text).used + index] = b'\n'; - buffer[(*text).used + index + 1] = b'\r'; - index += (*text).used + 2; - (*text).used = 0; - *(*text).buf = 0; - } - }, - ); - } - index - } -} - -pub fn set_writing_format(ctx: &mut ISDBSubContext, mut arg: &[u8]) { - let ls = &mut ctx.current_state.layout_state; - - if arg[1] == 0x20 { - ls.format = match arg[0] & 0x0F { - 0 => WritingFormat::HorizontalStdDensity, - 1 => WritingFormat::VerticalStdDensity, - 2 => WritingFormat::HorizontalHighDensity, - 3 => WritingFormat::VerticalHighDensity, - 4 => WritingFormat::HorizontalWesternLang, - 5 => WritingFormat::Horizontal1920x1080, - 6 => WritingFormat::Vertical1920x1080, - 7 => WritingFormat::Horizontal960x540, - 8 => WritingFormat::Vertical960x540, - 9 => WritingFormat::Horizontal720x480, - 10 => WritingFormat::Vertical720x480, - 11 => WritingFormat::Horizontal1280x720, - 12 => WritingFormat::Vertical1280x720, - _ => WritingFormat::None, - }; - return; - } - - if arg[1] == 0x3B { - ls.format = WritingFormat::HorizontalCustom; - arg = &arg[2..]; - } - if arg[1] == 0x3B { - match arg[0] & 0x0F { - 0 => { - // ctx.font_size = FontSize::SmallFontSize; - } - 1 => { - // ctx.font_size = FontSize::MiddleFontSize; - } - 2 => { - // ctx.font_size = FontSize::StandardFontSize; - } - _ => {} - } - arg = &arg[2..]; - } - - isdb_log(format_args!("character numbers in one line in decimal:\0")); - while arg[0] != 0x3B && arg[0] != 0x20 { - ctx.nb_char = arg[0] as i32; - println!(" {}", arg[0] & 0x0F); - arg = &arg[1..]; - } - if arg[0] == 0x20 { - return; - } - arg = &arg[1..]; - isdb_log(format_args!("line numbers in decimal: ")); - while arg[0] != 0x20 { - ctx.nb_line = arg[0] as i32; - println!(" {}", arg[0] & 0x0F); - arg = &arg[1..]; - } -} - -pub fn move_penpos(ctx: &mut ISDBSubContext, col: i32, row: i32) { - let ls = &mut ctx.current_state.layout_state; - - ls.cursor_pos.x = row; - ls.cursor_pos.y = col; -} - -pub fn set_position(ctx: &mut ISDBSubContext, p1: u32, p2: u32) { - let ls = &mut ctx.current_state.layout_state; - let (cw, ch, col, row); - - if is_horizontal_layout(ls.format) { - cw = (ls.font_size + ls.cell_spacing.col) * ls.font_scale.fscx / 100; - ch = (ls.font_size + ls.cell_spacing.row) * ls.font_scale.fscy / 100; - // pen pos is bottom left - col = p2 as i32 * cw; - row = p1 as i32 * ch + ch; - } else { - cw = (ls.font_size + ls.cell_spacing.col) * ls.font_scale.fscy / 100; - ch = (ls.font_size + ls.cell_spacing.row) * ls.font_scale.fscx / 100; - // pen pos is upper center, -90deg rotated, hence mid left - col = p2 as i32 * cw; - row = p1 as i32 * ch + ch / 2; - } - move_penpos(ctx, col, row); -} - -pub fn get_csi_params(q: &[u8], p1: &mut Option, p2: &mut Option) -> Result { - let mut q_pivot = 0; - if p1.is_none() { - return Err(-1); - } - - *p1 = Some(0); - while q[q_pivot] >= 0x30 && q[q_pivot] <= 0x39 { - *p1.as_mut().unwrap() *= 10; - *p1.as_mut().unwrap() += (q[q_pivot] - 0x30) as u32; - q_pivot += 1; - } - if q[q_pivot] != 0x20 && q[q_pivot] != 0x3B { - return Err(-1); - } - q_pivot += 1; - if p2.is_none() { - return Ok(q_pivot as i32); - } - *p2 = Some(0); - while q[q_pivot] >= 0x30 && q[q_pivot] <= 0x39 { - *p2.as_mut().unwrap() *= 10; - *p2.as_mut().unwrap() += (q[q_pivot] - 0x30) as u32; - q_pivot += 1; - } - q_pivot += 1; - Ok(q_pivot as i32) -} - -pub fn isdb_command_log(_format: &str, _args: std::fmt::Arguments) { - // nothing said, nothing done -} - -pub fn parse_csi(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { - let mut arg = [0u8; 10]; - let mut i = 0; - let mut ret = 0; - let mut p1 = None; - let mut p2 = None; - let buf_pivot = buf; - let state = &mut ctx.current_state; - let ls = &mut state.layout_state; - - // cpy buf in arg - while buf[i] != 0x20 { - if i >= arg.len() { - isdb_log(format_args!("UnExpected CSI {} >= {}\n", arg.len(), i)); - break; - } - arg[i] = buf[i]; - i += 1; - } - // ignore terminating 0x20 char - arg[i] = buf[i]; - i += 1; - - match buf[i] { - // Writing Format setting - CSI_CMD_SWF => { - isdb_command_log("Command:CSI: SWF\n", format_args!("")); - set_writing_format(ctx, &arg); - } - // Composite Character compose - CSI_CMD_CCC => { - isdb_command_log("Command:CSI: CCC\n", format_args!("")); - ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); - if ret > 0 { - ls.ccc = match p1.unwrap() { - 0 => IsdbCCComposition::None, - 2 => IsdbCCComposition::And, - 3 => IsdbCCComposition::Or, - 4 => IsdbCCComposition::Xor, - _ => IsdbCCComposition::None, - }; - } - } - // Display Format setting - CSI_CMD_SDF => { - ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); - if ret > 0 { - ls.display_area.w = p1.unwrap() as i32; - ls.display_area.h = p2.unwrap() as i32; - } - isdb_command_log( - "Command:CSI: SDF (w:{}, h:{})\n", - format_args!("{}, {}", p1.unwrap(), p2.unwrap()), - ); - } - // char composition's dot designation - CSI_CMD_SSM => { - ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); - if ret > 0 { - ls.font_size = p1.unwrap() as i32; - } - isdb_command_log( - "Command:CSI: SSM (x:{}, y:{})\n", - format_args!("{}, {}", p1.unwrap(), p2.unwrap()), - ); - } - // Set Display Position - CSI_CMD_SDP => { - ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); - if ret > 0 { - ls.display_area.x = p1.unwrap() as i32; - ls.display_area.y = p2.unwrap() as i32; - } - isdb_command_log( - "Command:CSI: SDP (x:{}, y:{})\n", - format_args!("{}, {}", p1.unwrap(), p2.unwrap()), - ); - } - // Raster Colour command - CSI_CMD_RCS => { - ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); - if ret > 0 { - ctx.current_state.raster_color = DEFAULT_CLUT - [(ctx.current_state.clut_high_idx << 4 | p1.unwrap() as u8) as usize]; - } - isdb_command_log("Command:CSI: RCS ({})\n", format_args!("{}", p1.unwrap())); - } - // Set Horizontal Spacing - CSI_CMD_SHS => { - ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); - if ret > 0 { - ls.cell_spacing.col = p1.unwrap() as i32; - } - isdb_command_log("Command:CSI: SHS ({})\n", format_args!("{}", p1.unwrap())); - } - // Set Vertical Spacing - CSI_CMD_SVS => { - ret = get_csi_params(&arg, &mut p1, &mut None).unwrap_or(-1); - if ret > 0 { - ls.cell_spacing.row = p1.unwrap() as i32; - } - isdb_command_log("Command:CSI: SVS ({})\n", format_args!("{}", p1.unwrap())); - } - // Active Coordinate Position Set - CSI_CMD_ACPS => { - isdb_command_log("Command:CSI: ACPS\n", format_args!("")); - ret = get_csi_params(&arg, &mut p1, &mut p2).unwrap_or(-1); - if ret > 0 { - ls.acps[0] = p1.unwrap() as i32; - ls.acps[1] = p2.unwrap() as i32; - } - } - _ => { - isdb_log(format_args!( - "Command:CSI: Unknown command 0x{:x}\n", - buf[i] - )); - } - } - i as i32 -} - -pub fn parse_command(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { - let buf_pivot = buf; - let code_lo = buf[0] & 0x0f; - let code_hi = (buf[0] & 0xf0) >> 4; - let mut ret; - let state = &mut ctx.current_state; - let ls = &mut state.layout_state; - - let mut buf = &buf[1..]; - match code_hi { - 0x00 => match code_lo { - 0x0 => isdb_command_log("Command: NUL\n", format_args!("")), - 0x7 => isdb_command_log("Command: BEL\n", format_args!("")), - 0x8 => isdb_command_log("Command: ABP\n", format_args!("")), - 0x9 => isdb_command_log("Command: APF\n", format_args!("")), - 0xA => isdb_command_log("Command: APD\n", format_args!("")), - 0xB => isdb_command_log("Command: APU\n", format_args!("")), - 0xC => isdb_command_log("Command: CS clear Screen\n", format_args!("")), - 0xD => isdb_command_log("Command: APR\n", format_args!("")), - 0xE => isdb_command_log("Command: LS1\n", format_args!("")), - 0xF => isdb_command_log("Command: LS0\n", format_args!("")), - _ => isdb_command_log("Command: Unknown\n", format_args!("")), - }, - 0x01 => match code_lo { - 0x6 => isdb_command_log("Command: PAPF\n", format_args!("")), - 0x8 => isdb_command_log("Command: CAN\n", format_args!("")), - 0x9 => isdb_command_log("Command: SS2\n", format_args!("")), - 0xB => isdb_command_log("Command: ESC\n", format_args!("")), - 0xC => { - isdb_command_log("Command: APS\n", format_args!("")); - set_position(ctx, (buf[0] & 0x3F) as u32, (buf[1] & 0x3F) as u32); - buf = &buf[2..]; - } - 0xD => isdb_command_log("Command: SS3\n", format_args!("")), - 0xE => isdb_command_log("Command: RS\n", format_args!("")), - 0xF => isdb_command_log("Command: US\n", format_args!("")), - _ => isdb_command_log("Command: Unknown\n", format_args!("")), - }, - 0x8 => match code_lo { - 0x0..=0x7 => { - isdb_command_log( - &format!( - "Command: Forground color (0x{:X})\n", - DEFAULT_CLUT[code_lo as usize] - ), - format_args!(""), - ); - state.fg_color = DEFAULT_CLUT[code_lo as usize]; - } - 0x8 => { - isdb_command_log("Command: SSZ\n", format_args!("")); - ls.font_scale.fscx = 50; - ls.font_scale.fscy = 50; - } - 0x9 => { - isdb_command_log("Command: MSZ\n", format_args!("")); - ls.font_scale.fscx = 200; - ls.font_scale.fscy = 200; - } - 0xA => { - isdb_command_log("Command: NSZ\n", format_args!("")); - ls.font_scale.fscx = 100; - ls.font_scale.fscy = 100; - } - 0xB => { - isdb_command_log("Command: SZX\n", format_args!("")); - buf = &buf[1..]; - } - _ => isdb_command_log("Command: Unknown\n", format_args!("")), - }, - 0x9 => match code_lo { - 0x0 => { - if buf[0] == 0x20 { - isdb_command_log( - &format!("Command: COL: Set Clut {}\n", (buf[0] & 0x0F)), - format_args!(""), - ); - buf = &buf[1..]; - ctx.current_state.clut_high_idx = (buf[0] & 0x0F) as u8; - } else if (buf[0] & 0xF0) == 0x40 { - isdb_command_log( - &format!( - "Command: COL: Set Forground 0x{:08X}\n", - DEFAULT_CLUT[(buf[0] & 0x0F) as usize] - ), - format_args!(""), - ); - ctx.current_state.fg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; - } else if (buf[0] & 0xF0) == 0x50 { - isdb_command_log( - &format!( - "Command: COL: Set Background 0x{:08X}\n", - DEFAULT_CLUT[(buf[0] & 0x0F) as usize] - ), - format_args!(""), - ); - ctx.current_state.bg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; - } else if (buf[0] & 0xF0) == 0x60 { - isdb_command_log( - &format!( - "Command: COL: Set half Forground 0x{:08X}\n", - DEFAULT_CLUT[(buf[0] & 0x0F) as usize] - ), - format_args!(""), - ); - ctx.current_state.hfg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; - } else if (buf[0] & 0xF0) == 0x70 { - isdb_command_log( - &format!( - "Command: COL: Set Half Background 0x{:08X}\n", - DEFAULT_CLUT[(buf[0] & 0x0F) as usize] - ), - format_args!(""), - ); - ctx.current_state.hbg_color = DEFAULT_CLUT[(buf[0] & 0x0F) as usize]; - } - buf = &buf[1..]; - } - 0x1 => { - isdb_command_log("Command: FLC\n", format_args!("")); - buf = &buf[1..]; - } - 0x2 => { - isdb_command_log("Command: CDC\n", format_args!("")); - buf = &buf[3..]; - } - 0x3 => { - isdb_command_log("Command: POL\n", format_args!("")); - buf = &buf[1..]; - } - 0x4 => { - isdb_command_log("Command: WMM\n", format_args!("")); - buf = &buf[3..]; - } - 0x5 => { - isdb_command_log("Command: MACRO\n", format_args!("")); - buf = &buf[1..]; - } - 0x7 => { - isdb_command_log("Command: HLC\n", format_args!("")); - buf = &buf[1..]; - } - 0x8 => { - isdb_command_log("Command: RPC\n", format_args!("")); - buf = &buf[1..]; - } - 0x9 => isdb_command_log("Command: SPL\n", format_args!("")), - 0xA => isdb_command_log("Command: STL\n", format_args!("")), - 0xB => { - ret = parse_csi(ctx, buf); - buf = &buf[ret as usize..]; - } - 0xD => { - isdb_command_log("Command: TIME\n", format_args!("")); - buf = &buf[2..]; - } - _ => isdb_command_log("Command: Unknown\n", format_args!("")), - }, - _ => isdb_command_log("Command: Unknown\n", format_args!("")), - } - - (buf_pivot.len() - buf.len()) as i32 -} - -pub fn parse_caption_management_data(ctx: &mut ISDBSubContext, buf: &[u8]) -> i32 { - let buf_pivot = buf; - let mut buf = buf; - - ctx.tmd = match buf[0] >> 6 { - 0 => IsdbTmd::Free, - 1 => IsdbTmd::RealTime, - 2 => IsdbTmd::OffsetTime, - _ => IsdbTmd::Free, // default case - }; - isdb_log(format_args!("CC MGMT DATA: TMD: {:?}\n", ctx.tmd)); - buf = &buf[1..]; - - match ctx.tmd { - ISDB_TMD_FREE => { - isdb_log(format_args!( - "Playback time is not restricted to synchronize to the clock.\n" - )); - } - ISDB_TMD_OFFSET_TIME => { - ctx.offset_time.hour = ((buf[0] >> 4) * 10 + (buf[0] & 0xf)) as i32; - buf = &buf[1..]; - ctx.offset_time.min = ((buf[0] >> 4) * 10 + (buf[0] & 0xf)) as i32; - buf = &buf[1..]; - ctx.offset_time.sec = ((buf[0] >> 4) * 10 + (buf[0] & 0xf)) as i32; - buf = &buf[1..]; - ctx.offset_time.milli = - ((buf[0] >> 4) * 100 + ((buf[0] & 0xf) * 10) + (buf[1] & 0xf)) as i32; - buf = &buf[2..]; - isdb_log(format_args!( - "CC MGMT DATA: OTD( h:{} m:{} s:{} millis: {}\n", - ctx.offset_time.hour, - ctx.offset_time.min, - ctx.offset_time.sec, - ctx.offset_time.milli - )); - } - _ => { - isdb_log(format_args!( - "Playback time is in accordance with the time of the clock, \ - which is calibrated by clock signal (TDT). Playback time is \ - given by PTS.\n" - )); - } - } - ctx.nb_lang = buf[0] as i32; - isdb_log(format_args!( - "CC MGMT DATA: nb languages: {}\n", - ctx.nb_lang - )); - buf = &buf[1..]; - - for _ in 0..ctx.nb_lang { - isdb_log(format_args!("CC MGMT DATA: {}\n", (buf[0] & 0x1F) >> 5)); - ctx.dmf = buf[0] & 0x0F; - isdb_log(format_args!("CC MGMT DATA: DMF 0x{:X}\n", ctx.dmf)); - buf = &buf[1..]; - if ctx.dmf == 0xC || ctx.dmf == 0xD || ctx.dmf == 0xE { - ctx.dc = buf[0]; - if ctx.dc == 0x00 { - isdb_log(format_args!("Attenuation Due to Rain\n")); - } - } - isdb_log(format_args!( - "CC MGMT DATA: languages: {}{}{}\n", - buf[0] as char, buf[1] as char, buf[2] as char - )); - buf = &buf[3..]; - isdb_log(format_args!("CC MGMT DATA: Format: 0x{:X}\n", buf[0] >> 4)); - isdb_log(format_args!( - "CC MGMT DATA: TCS: 0x{:X}\n", - (buf[0] >> 2) & 0x3 - )); - ctx.current_state.rollup_mode = if (buf[0] & 0x3) != 0 { 1 } else { 0 }; - isdb_log(format_args!( - "CC MGMT DATA: Rollup mode: 0x{:X}\n", - ctx.current_state.rollup_mode - )); - buf = &buf[1..]; - } - (buf_pivot.len() - buf.len()) as i32 -} - -// in/from ccx_common_common.c -use std::ffi::CString; -pub fn add_cc_sub_text( - sub: &mut CcSubtitle, - str: &str, - start_time: i64, - end_time: i64, - info: Option<&str>, - mode: Option<&str>, - e_type: CcxEncodingType, -) -> i32 { - if str.is_empty() { - return 0; - } - - let target_sub = if sub.nb_data > 0 { - let mut current = sub; - while !current.next.is_null() { - current = unsafe { &mut *current.next }; - } - let new_sub = Box::into_raw(Box::new(CcSubtitle { - data: ptr::null_mut(), - datatype: Subdatatype::Generic, - nb_data: 0, - type_: Subtype::Text, - enc_type: e_type, - start_time: 0, - end_time: 0, - flags: 0, - lang_index: 0, - got_output: 0, - mode: [0; 5], - info: [0; 4], - time_out: 0, - next: ptr::null_mut(), - prev: current, - })); - current.next = new_sub; - unsafe { &mut *new_sub } - } else { - sub - }; - - target_sub.type_ = Subtype::Text; - target_sub.enc_type = e_type; - target_sub.data = CString::new(str).unwrap().into_raw() as *mut c_void; - target_sub.datatype = Subdatatype::Generic; - target_sub.nb_data = str.len() as u32; - target_sub.start_time = start_time; - target_sub.end_time = end_time; - if let Some(info_str) = info { - let info_bytes = info_str.as_bytes(); - target_sub.info[..info_bytes.len()].copy_from_slice(&info_bytes[..4]); - } - if let Some(mode_str) = mode { - let mode_bytes = mode_str.as_bytes(); - target_sub.mode[..mode_bytes.len()].copy_from_slice(&mode_bytes[..4]); - } - target_sub.got_output = 1; - target_sub.next = ptr::null_mut(); - - 0 -} - -pub fn parse_statement(ctx: &mut ISDBSubContext, buf: &[u8], size: i32) -> i32 { - let buf_pivot = buf; - let mut buf = buf; - let mut ret; - - while (buf.len() - buf_pivot.len()) < size as usize { - let code = (buf[0] & 0xf0) >> 4; - let code_lo = buf[0] & 0x0f; - if code <= 0x1 { - ret = parse_command(ctx, buf); - } else if code == 0x2 && code_lo == 0x0 { - ret = append_char(ctx, buf[0] as char); - } else if code == 0x7 && code_lo == 0xF { - ret = append_char(ctx, buf[0] as char); - } else if code <= 0x7 { - ret = append_char(ctx, buf[0] as char); - } else if code <= 0x9 { - ret = parse_command(ctx, buf); - } else if code == 0xA && code_lo == 0x0 { - // still todo :) - ret = 1; - } else if code == 0x0F && code_lo == 0xF { - // still todo :) - ret = 1; - } else { - ret = append_char(ctx, buf[0] as char); - } - if ret < 0 { - break; - } - buf = &buf[ret as usize..]; - } - 0 -} - -pub fn parse_data_unit(ctx: &mut ISDBSubContext, buf: &[u8], size: i32) -> i32 { - let mut buf = buf; - // unit separator - buf = &buf[1..]; - - let unit_parameter = buf[0]; - buf = &buf[1..]; - let len = rb24(buf); - buf = &buf[3..]; - - match unit_parameter { - // statement body - 0x20 => { - parse_statement(ctx, buf, len); - } - _ => {} - } - 0 -} - -pub fn parse_caption_statement_data( - ctx: &mut ISDBSubContext, - lang_id: i32, - buf: &[u8], - size: i32, - sub: &mut CcSubtitle, -) -> i32 { - let mut buf = buf; - let tmd = buf[0] >> 6; - buf = &buf[1..]; - - // skipping timing data - if tmd == 1 || tmd == 2 { - buf = &buf[5..]; - } - - let len = rb24(buf); - buf = &buf[3..]; - let ret = parse_data_unit(ctx, buf, len); - if ret < 0 { - return -1; - } - - let mut buffer = [0u8; 1024]; - let ret = get_text(ctx, &mut buffer, 1024); - // cpy data if there in buffer - if ret < 0 { - return 0; - } - - if ret > 0 { - add_cc_sub_text( - sub, - std::str::from_utf8(&buffer).unwrap_or(""), - ctx.prev_timestamp as i64, - ctx.timestamp as i64, - Some("NA"), - Some("ISDB"), - CcxEncodingType::Utf8, - ); - if sub.start_time == sub.end_time { - sub.end_time += 2; - } - ctx.prev_timestamp = ctx.timestamp; - } - 0 -} - -fn rb24(buf: &[u8]) -> i32 { - ((buf[0] as i32) << 16) | ((buf[1] as i32) << 8) | (buf[2] as i32) -} - -pub static mut CCX_OPTIONS: CcxOptions = CcxOptions { - messages_target: CCX_MESSAGES_QUIET, -}; - -pub fn mprint(message: &str) { - unsafe { - if CCX_OPTIONS.messages_target == CCX_MESSAGES_QUIET { - return; - } - - match CCX_OPTIONS.messages_target { - CCX_MESSAGES_STDOUT => { - print!("{}", message); - io::stdout().flush().unwrap(); - } - CCX_MESSAGES_STDERR => { - eprint!("{}", message); - io::stderr().flush().unwrap(); - } - _ => (), - } - } -} - -pub fn cstr(s: &str) -> std::ffi::CString { - std::ffi::CString::new(s).unwrap() -} - -pub fn isdb_parse_data_group(codec_ctx: *mut c_void, buf: &[u8], sub: &mut CcSubtitle) -> i32 { - let ctx = unsafe { &mut *(codec_ctx as *mut ISDBSubContext) }; - let buf_pivot = buf; - let mut buf = buf; - let id = buf[0] >> 2; - let mut group_size = 0; - let mut ret = 0; - - if (id >> 4) == 0 { - isdb_log(format_args!("ISDB group A\n")); - } else if (id >> 4) == 0 { - isdb_log(format_args!("ISDB group B\n")); - } - - buf = &buf[3..]; - - group_size = rb16(buf); - buf = &buf[2..]; - isdb_log(format_args!( - "ISDB (Data group) group_size {}\n", - group_size - )); - - if ctx.prev_timestamp > ctx.timestamp { - ctx.prev_timestamp = ctx.timestamp; - } - if (id & 0x0F) == 0 { - // for caption management - ret = parse_caption_management_data(ctx, buf); - } else if (id & 0x0F) < 8 { - // caption sttmnt data - isdb_log(format_args!("ISDB {} language\n", id)); - ret = parse_caption_statement_data(ctx, (id & 0x0F) as i32, buf, group_size as i32, sub); - } else { - // coz not allowed in spec - } - if ret < 0 { - return -1; - } - buf = &buf[group_size as usize..]; - - // crc todo still :) - buf = &buf[2..]; - - (buf_pivot.len() - buf.len()) as i32 -} - -fn rb16(buf: &[u8]) -> i32 { - ((buf[0] as i32) << 8) | (buf[1] as i32) -} - -pub fn isdbsub_decode(dec_ctx: &mut LibCcDecode, buf: &[u8], sub: &mut CcSubtitle) -> i32 { - let mut buf = buf; - let mut ret = 0; - let ctx = unsafe { &mut *(dec_ctx.private_data as *mut ISDBSubContext) }; - - if buf[0] != 0x80 { - mprint("Not a Syncronised PES\n"); - return -1; - } - buf = &buf[1..]; - - // pvt data stream is 0xFF - buf = &buf[1..]; - let header_end = buf[0] & 0x0f; - buf = &buf[1..]; - - while buf.len() > header_end as usize { - // todo header spec finding :) - buf = &buf[1..]; - } - - ctx.cfg_no_rollup = dec_ctx.no_rollup; - ret = isdb_parse_data_group(ctx as *mut _ as *mut c_void, buf, sub); - if ret < 0 { - return -1; - } - - 1 -} diff --git a/src/rust/lib_ccxr/src/isdbs/mod.rs b/src/rust/lib_ccxr/src/isdbs/mod.rs deleted file mode 100644 index be0869812..000000000 --- a/src/rust/lib_ccxr/src/isdbs/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod isdbd; -pub mod isdbs; diff --git a/src/rust/lib_ccxr/src/lib.rs b/src/rust/lib_ccxr/src/lib.rs index d1ae32357..bd76547ab 100644 --- a/src/rust/lib_ccxr/src/lib.rs +++ b/src/rust/lib_ccxr/src/lib.rs @@ -1,7 +1,7 @@ pub mod activity; pub mod common; +pub mod decoder_isdb; pub mod hardsubx; -pub mod isdbs; pub mod subtitle; pub mod teletext; pub mod time; diff --git a/src/rust/src/libccxr_exports/isdb.rs b/src/rust/src/libccxr_exports/isdb.rs deleted file mode 100644 index 46a3c9cbf..000000000 --- a/src/rust/src/libccxr_exports/isdb.rs +++ /dev/null @@ -1,72 +0,0 @@ -extern crate libc; - -use lib_ccxr::isdbs::isdbd::*; -use lib_ccxr::isdbs::isdbs::*; - -use std::mem; -use std::os::raw::{c_int, c_uint, c_void}; -use std::ptr; - -#[no_mangle] -pub extern "C" fn ccxr_isdb_set_global_time(dec_ctx: *mut LibCcDecode, timestamp: u64) -> c_int { - unsafe { - let ctx = (*dec_ctx).private_data as *mut ISDBSubContext; - if !ctx.is_null() { - isdb_set_global_time(&mut *ctx, timestamp); - 0 // return 0 if everything is CCX_OK - } else { - -1 // return an error code if ctx is null - } - } -} - -#[no_mangle] -pub unsafe extern "C" fn ccxr_delete_isdb_decoder(isdb_ctx: *mut *mut c_void) { - let ctx = *isdb_ctx as *mut ISDBSubContext; - delete_isdb_decoder(ctx); - freep(isdb_ctx); -} - -#[no_mangle] -pub extern "C" fn ccxr_init_isdb_decoder() -> *mut c_void { - unsafe { - let ctx = libc::malloc(mem::size_of::()) as *mut ISDBSubContext; - if ctx.is_null() { - return ptr::null_mut(); - } - - libc::memset(ctx as *mut c_void, 0, mem::size_of::()); - - init_list_head(&mut (*ctx).text_list_head); - init_list_head(&mut (*ctx).buffered_text); - (*ctx).prev_timestamp = c_uint::MAX as u64; - - (*ctx).current_state.clut_high_idx = 0; - (*ctx).current_state.rollup_mode = 0; - init_layout(&mut (*ctx).current_state.layout_state); - - ctx as *mut c_void - } -} - -#[no_mangle] -pub extern "C" fn ccxr_isdb_parse_data_group( - codec_ctx: *mut c_void, - buf: *const u8, - size: i32, - sub: *mut CcSubtitle, -) -> i32 { - let buf_slice = unsafe { std::slice::from_raw_parts(buf, size as usize) }; - isdb_parse_data_group(codec_ctx, buf_slice, unsafe { &mut *sub }) -} - -#[no_mangle] -pub extern "C" fn ccxr_isdbsub_decode( - dec_ctx: *mut LibCcDecode, - buf: *const u8, - buf_size: usize, - sub: *mut CcSubtitle, -) -> i32 { - let buf_slice = unsafe { std::slice::from_raw_parts(buf, buf_size) }; - isdbsub_decode(unsafe { &mut *dec_ctx }, buf_slice, unsafe { &mut *sub }) -} diff --git a/src/rust/src/libccxr_exports/isdb_exports.rs b/src/rust/src/libccxr_exports/isdb_exports.rs new file mode 100644 index 000000000..6892e3fa0 --- /dev/null +++ b/src/rust/src/libccxr_exports/isdb_exports.rs @@ -0,0 +1,95 @@ +use lib_ccxr::decoder_isdb::exit_codes::*; +use lib_ccxr::decoder_isdb::functions_isdb::*; +use lib_ccxr::decoder_isdb::structs_ccdecode::*; +use lib_ccxr::decoder_isdb::structs_isdb::*; +use lib_ccxr::decoder_isdb::structs_xds::*; + +/// # Safety +/// +/// - The caller must ensure that `isdb_ctx` is a valid, non-null pointer to a mutable `Option>`. +/// - The function dereferences `isdb_ctx` and calls `as_mut()` on it, so passing an invalid or null pointer will result in undefined behavior. +/// - After calling this function, the memory associated with the `ISDBSubContext` may be freed, so the caller must not use the pointer again. +#[no_mangle] +pub unsafe extern "C" fn ccxr_delete_isdb_decoder(isdb_ctx: *mut Option>) { + if let Some(ctx) = unsafe { isdb_ctx.as_mut() } { + delete_isdb_decoder(ctx); + } +} + +/// # Safety +/// +/// - The caller is responsible for managing the lifetime of the returned pointer. +/// - The returned pointer must eventually be passed to a function that properly deallocates it (e.g., `ccxr_delete_isdb_decoder`). +/// - Using the pointer after it has been deallocated will result in undefined behavior. +#[no_mangle] +pub extern "C" fn ccxr_init_isdb_decoder() -> *mut ISDBSubContext { + if let Some(ctx) = init_isdb_decoder() { + Box::into_raw(ctx) + } else { + std::ptr::null_mut() + } +} + +/// # Safety +/// +/// - The caller must ensure that `codec_ctx` is a valid, non-null pointer to an `ISDBSubContext`. +/// - The caller must ensure that `buf` points to a valid, null-terminated buffer of `u8` values. +/// - The caller must ensure that `sub` is a valid, non-null pointer to a `CcSubtitle`. +/// - Passing invalid or null pointers to any of these parameters will result in undefined behavior. +#[no_mangle] +pub unsafe extern "C" fn ccxr_isdb_parse_data_group( + codec_ctx: *mut ISDBSubContext, + buf: *const u8, + sub: *mut CcSubtitle, +) -> i32 { + if let (Some(codec_ctx), Some(sub)) = (unsafe { codec_ctx.as_mut() }, unsafe { sub.as_mut() }) { + let mut len = 0; + while unsafe { *buf.add(len) } != 0 { + len += 1; + } + let buf_slice = unsafe { std::slice::from_raw_parts(buf, len) }; + isdb_parse_data_group(codec_ctx, buf_slice, sub) + } else { + -1 + } +} + +/// # Safety +/// +/// - The caller must ensure that `dec_ctx` is a valid, non-null pointer to a `LibCcDecode`. +/// - The caller must ensure that `buf` points to a valid buffer of at least `buf_len` bytes. +/// - The caller must ensure that `sub` is a valid, non-null pointer to a `CcSubtitle`. +/// - Passing invalid or null pointers to any of these parameters will result in undefined behavior. +#[no_mangle] +pub unsafe extern "C" fn ccxr_isdbsub_decode( + dec_ctx: *mut LibCcDecode, + buf: *const u8, + buf_len: usize, + sub: *mut CcSubtitle, +) -> i32 { + if let (Some(dec_ctx), Some(sub)) = (unsafe { dec_ctx.as_mut() }, unsafe { sub.as_mut() }) { + let buf_slice = unsafe { std::slice::from_raw_parts(buf, buf_len) }; + isdbsub_decode(dec_ctx, buf_slice, sub) + } else { + -1 + } +} + +/// # Safety +/// +/// - The caller must ensure that `dec_ctx` is a valid, non-null pointer to a `LibCcDecode`. +/// - The `private_data` field of `LibCcDecode` must point to a valid `ISDBSubContext`. +/// - Passing an invalid or null pointer, or an invalid `private_data` field, will result in undefined behavior. +#[no_mangle] +pub unsafe extern "C" fn ccxr_isdb_set_global_time( + dec_ctx: *mut LibCcDecode, + timestamp: u64, +) -> i32 { + if let Some(ctx) = + unsafe { (dec_ctx.as_mut()).and_then(|d| (d.private_data as *mut ISDBSubContext).as_mut()) } + { + isdb_set_global_time(ctx, timestamp) + } else { + CCX_EINVAL as i32 + } +} diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs index 016304bdc..d803158d3 100644 --- a/src/rust/src/libccxr_exports/mod.rs +++ b/src/rust/src/libccxr_exports/mod.rs @@ -1,6 +1,6 @@ //! Provides C-FFI functions that are direct equivalent of functions available in C. -pub mod isdb; +pub mod isdb_exports; pub mod time; use crate::ccx_options; use lib_ccxr::util::log::*; From 60d152a72bd8a87871b864507a65a2d6663b7d6b Mon Sep 17 00:00:00 2001 From: vats004 <=> Date: Sat, 29 Mar 2025 08:02:07 +0530 Subject: [PATCH 3/4] update lib_ccxr/cargo.lock --- src/rust/lib_ccxr/Cargo.lock | 297 +++++++++++++++++++++-------------- 1 file changed, 183 insertions(+), 114 deletions(-) diff --git a/src/rust/lib_ccxr/Cargo.lock b/src/rust/lib_ccxr/Cargo.lock index d787dc251..10873ab23 100644 --- a/src/rust/lib_ccxr/Cargo.lock +++ b/src/rust/lib_ccxr/Cargo.lock @@ -3,31 +3,30 @@ version = 4 [[package]] -name = "anyhow" -version = "1.0.97" +name = "aho-corasick" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" - -[[package]] -name = "bitflags" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" +dependencies = [ + "memchr", +] [[package]] -name = "bytes" -version = "1.10.1" +name = "atty" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" +checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8" +dependencies = [ + "hermit-abi", + "libc", + "winapi", +] [[package]] -name = "cc" -version = "1.2.17" +name = "bitflags" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fcb57c740ae1daf453ae85f16e37396f672b039e00d9d866e07ddb24e328e3a" -dependencies = [ - "shlex", -] +checksum = "5c8214115b7bf84099f1309324e63141d4c5d7cc26862f97a0a857dbefe165bd" [[package]] name = "cfg-if" @@ -35,15 +34,6 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cmake" -version = "0.1.54" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7caa3f9de89ddbe2c607f4101924c5abec803763ae9534e4f4d7d8f84aa81f0" -dependencies = [ - "cc", -] - [[package]] name = "convert_case" version = "0.4.0" @@ -93,10 +83,17 @@ dependencies = [ ] [[package]] -name = "either" -version = "1.15.0" +name = "env_logger" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3" +dependencies = [ + "atty", + "humantime", + "log", + "regex", + "termcolor", +] [[package]] name = "equivalent" @@ -113,12 +110,6 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "gcc" -version = "0.3.55" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f5f3913fa0bfe7ee1fd8248b6b9f42a5af4b9d65ec2dd2c3c26132b950ecfc2" - [[package]] name = "hashbrown" version = "0.15.2" @@ -131,6 +122,21 @@ version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" +[[package]] +name = "hermit-abi" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" +dependencies = [ + "libc", +] + +[[package]] +name = "humantime" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b112acc8b3adf4b107a8ec20977da0273a8c386765a3ec0229bd500a1443f9f" + [[package]] name = "icu_collections" version = "1.5.0" @@ -280,27 +286,12 @@ dependencies = [ "hashbrown", ] -[[package]] -name = "itertools" -version = "0.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" -dependencies = [ - "either", -] - [[package]] name = "itoa" version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" -[[package]] -name = "lazy_static" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - [[package]] name = "lib_ccxr" version = "0.1.0" @@ -308,13 +299,9 @@ dependencies = [ "bitflags", "crc32fast", "derive_more", - "lazy_static", - "libc", - "nanomsg", - "nanomsg-sys", + "env_logger", + "log", "num_enum", - "prost", - "prost-types", "strum", "strum_macros", "thiserror", @@ -335,32 +322,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" [[package]] -name = "memchr" -version = "2.7.4" +name = "log" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" [[package]] -name = "nanomsg" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "617e0160fba522f8667df7bc79f3b4a74a0e3968d08023ebb3ce717a5f3bd3ac" -dependencies = [ - "libc", - "nanomsg-sys", -] - -[[package]] -name = "nanomsg-sys" -version = "0.7.2" +name = "memchr" +version = "2.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78aa3ccb6d007dfecb4f7070725c4b1670a87677babb6621cb0c8cce9cfdc004" -dependencies = [ - "cmake", - "gcc", - "libc", - "pkg-config", -] +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "num-conv" @@ -401,12 +372,6 @@ version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - [[package]] name = "powerfmt" version = "0.2.0" @@ -433,45 +398,42 @@ dependencies = [ ] [[package]] -name = "prost" -version = "0.13.5" +name = "quote" +version = "1.0.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" +checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" dependencies = [ - "bytes", - "prost-derive", + "proc-macro2", ] [[package]] -name = "prost-derive" -version = "0.13.5" +name = "regex" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ - "anyhow", - "itertools", - "proc-macro2", - "quote", - "syn", + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", ] [[package]] -name = "prost-types" -version = "0.13.5" +name = "regex-automata" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52c2c1bf36ddb1a1c396b3601a3cec27c2462e45f07c386894ec3ccf5332bd16" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ - "prost", + "aho-corasick", + "memchr", + "regex-syntax", ] [[package]] -name = "quote" -version = "1.0.39" +name = "regex-syntax" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1f1914ce909e1658d9907913b4b91947430c7d9be598b15a1912935b8c04801" -dependencies = [ - "proc-macro2", -] +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "rustc_version" @@ -514,12 +476,6 @@ dependencies = [ "syn", ] -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - [[package]] name = "smallvec" version = "1.14.0" @@ -573,6 +529,15 @@ dependencies = [ "syn", ] +[[package]] +name = "termcolor" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" +dependencies = [ + "winapi-util", +] + [[package]] name = "thiserror" version = "1.0.69" @@ -680,6 +645,110 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", + "windows_i686_gnullvm", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + [[package]] name = "winnow" version = "0.5.40" From 40c64d92ed4526d1dd4a1286f035be45bace31db Mon Sep 17 00:00:00 2001 From: vats004 <=> Date: Sat, 29 Mar 2025 19:10:38 +0530 Subject: [PATCH 4/4] fixed some typos --- src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs | 2 +- src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs b/src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs index 2bc1e4427..118ffe7a3 100644 --- a/src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs +++ b/src/rust/lib_ccxr/src/decoder_isdb/exit_codes.rs @@ -9,7 +9,7 @@ pub const CCX_ENOSUPP: i64 = -103; pub const CCX_ENOMEM: i64 = -104; pub const NUM_BYTES_PER_PACKET: i64 = 35; // Class + type (repeated for convenience) + data + zero -pub const NUM_XDS_BUFFERS: i64 = 35; // CEA recommends no more than one level of interleaving. Play it safe +pub const NUM_XDS_BUFFERS: i64 = 9; // CEA recommends no more than one level of interleaving. Play it safe pub const MAXBFRAMES: usize = 50; pub const SORTBUF: usize = 2 * MAXBFRAMES + 1; diff --git a/src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs b/src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs index 584d4370a..df1514b09 100644 --- a/src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs +++ b/src/rust/lib_ccxr/src/decoder_isdb/structs_isdb.rs @@ -142,9 +142,7 @@ pub struct CcxDecoderVbiCtx { pub vbi_decoder_inited: c_int, pub zvbi_decoder: VbiRawDecoder, // vbi3_raw_decoder zvbi_decoder; - // #ifdef VBI_DEBUG - // pub vbi_debug_dump: *mut FILE, - // #endif + pub vbi_debug_dump: Option>, } #[repr(C)]