diff --git a/docker/dockerfile b/docker/dockerfile index 255de9255..09abb5fd8 100644 --- a/docker/dockerfile +++ b/docker/dockerfile @@ -7,8 +7,8 @@ RUN apk add --no-cache --update git curl gcc cmake glew glfw \ zlib-dev libpng-dev libjpeg-turbo-dev openssl-dev freetype-dev libxml2-dev RUN cd && git clone https://github.com/gpac/gpac -WORKDIR root/gpac/ -RUN ./configure && make && make install-lib && cd && rm -rf /root/gpac +WORKDIR /root/gpac/ +RUN ./configure && make && make install-lib && cd && rm -rf /root/gpac WORKDIR /root RUN git clone https://github.com/CCExtractor/ccextractor.git diff --git a/docs/CHANGES.TXT b/docs/CHANGES.TXT index 74e16bc43..d6765ffb2 100644 --- a/docs/CHANGES.TXT +++ b/docs/CHANGES.TXT @@ -1,5 +1,6 @@ 1.0 (to be released) ----------------- +- Fix: Improved handling of IETF language tags in Matroska files (#1665) - New: Create unit test for rust code (#1615) - Breaking: Major argument flags revamp for CCExtractor (#1564 & #1619) - New: Create a Docker image to simplify the CCExtractor usage without any environmental hustle (#1611) diff --git a/src/lib_ccx/ccx_common_timing.c b/src/lib_ccx/ccx_common_timing.c index b02151bdf..4dd4fec2a 100644 --- a/src/lib_ccx/ccx_common_timing.c +++ b/src/lib_ccx/ccx_common_timing.c @@ -314,6 +314,7 @@ LLONG get_fts(struct ccx_common_timing_ctx *ctx, int current_field) return fts; } + LLONG get_fts_max(struct ccx_common_timing_ctx *ctx) { #ifndef DISABLE_RUST diff --git a/src/lib_ccx/ccx_decoders_vbi.c b/src/lib_ccx/ccx_decoders_vbi.c index 3c20cf157..f3d6b2c76 100644 --- a/src/lib_ccx/ccx_decoders_vbi.c +++ b/src/lib_ccx/ccx_decoders_vbi.c @@ -12,6 +12,7 @@ void delete_decoder_vbi(struct ccx_decoder_vbi_ctx **arg) freep(arg); } + struct ccx_decoder_vbi_ctx *init_decoder_vbi(struct ccx_decoder_vbi_cfg *cfg) { struct ccx_decoder_vbi_ctx *vbi; diff --git a/src/lib_ccx/matroska.c b/src/lib_ccx/matroska.c index 01b7e634f..f7c3e8f70 100644 --- a/src/lib_ccx/matroska.c +++ b/src/lib_ccx/matroska.c @@ -154,10 +154,13 @@ void parse_ebml(FILE *file) default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping EBML block\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - set_bytes(file, pos + len); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } @@ -232,10 +235,13 @@ void parse_segment_info(FILE *file) default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping segment info block\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - set_bytes(file, pos + len); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } @@ -489,10 +495,13 @@ void parse_segment_cluster_block_group(struct matroska_ctx *mkv_ctx, ULLONG clus default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping segment cluster block group\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - set_bytes(file, pos + len); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } @@ -597,10 +606,13 @@ void parse_segment_cluster(struct matroska_ctx *mkv_ctx) default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping segment cluster block\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - set_bytes(file, pos + len); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } @@ -728,6 +740,7 @@ void parse_segment_track_entry(struct matroska_ctx *mkv_ctx) enum matroska_track_entry_type track_type = MATROSKA_TRACK_TYPE_VIDEO; char *lang = strdup("eng"); char *header = NULL; + char *lang_ietf = NULL; char *codec_id_string = NULL; enum matroska_track_subtitle_codec_id codec_id = MATROSKA_TRACK_SUBTITLE_CODEC_ID_UTF8; @@ -863,6 +876,31 @@ void parse_segment_track_entry(struct matroska_ctx *mkv_ctx) case MATROSKA_SEGMENT_TRACK_TRICK_MASTER_TRACK_SEGMENT_UID: read_vint_block_skip(file); MATROSKA_SWITCH_BREAK(code, code_len); + case MATROSKA_SEGMENT_TRACK_LANGUAGE_IETF: + lang_ietf = read_vint_block_string(file); + mprint(" Language IETF: %s\n", lang_ietf); + // We'll store this for later use rather than freeing it immediately + if (track_type == MATROSKA_TRACK_TYPE_SUBTITLE) + { + // Don't free lang_ietf here, store in track + if (lang != NULL) + { + // If we previously allocated lang, free it as we'll prefer IETF + free(lang); + lang = NULL; + } + // Default to "eng" if we somehow don't have a language yet + if (lang == NULL) + { + lang = strdup("eng"); + } + } + else + { + free(lang_ietf); // Free if not a subtitle track + lang_ietf = NULL; + } + MATROSKA_SWITCH_BREAK(code, code_len); /* Misc ids */ case MATROSKA_VOID: @@ -874,10 +912,13 @@ void parse_segment_track_entry(struct matroska_ctx *mkv_ctx) default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping segment track entry block\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - set_bytes(file, pos + len); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } @@ -888,6 +929,7 @@ void parse_segment_track_entry(struct matroska_ctx *mkv_ctx) struct matroska_sub_track *sub_track = malloc(sizeof(struct matroska_sub_track)); sub_track->header = header; sub_track->lang = lang; + sub_track->lang_ietf = lang_ietf; sub_track->track_number = track_number; sub_track->lang_index = 0; sub_track->codec_id = codec_id; @@ -904,6 +946,8 @@ void parse_segment_track_entry(struct matroska_ctx *mkv_ctx) else { free(lang); + if (lang_ietf) + free(lang_ietf); if (codec_id_string) free(codec_id_string); } @@ -997,10 +1041,13 @@ void parse_segment_tracks(struct matroska_ctx *mkv_ctx) default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping segment tracks block\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - set_bytes(file, pos + len); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } @@ -1058,10 +1105,13 @@ void parse_segment(struct matroska_ctx *mkv_ctx) default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping segment block\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - set_bytes(file, pos + len); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } @@ -1071,11 +1121,15 @@ void parse_segment(struct matroska_ctx *mkv_ctx) char *generate_filename_from_track(struct matroska_ctx *mkv_ctx, struct matroska_sub_track *track) { char *buf = malloc(sizeof(char) * 200); + // Use lang_ietf if available, otherwise fall back to lang + const char *lang_to_use = track->lang_ietf ? track->lang_ietf : track->lang; + if (track->lang_index == 0) - sprintf(buf, "%s_%s.%s", get_basename(mkv_ctx->filename), track->lang, matroska_track_text_subtitle_id_extensions[track->codec_id]); - else - sprintf(buf, "%s_%s_" LLD ".%s", get_basename(mkv_ctx->filename), track->lang, track->lang_index, + sprintf(buf, "%s_%s.%s", get_basename(mkv_ctx->filename), lang_to_use, matroska_track_text_subtitle_id_extensions[track->codec_id]); + else + sprintf(buf, "%s_%s_" LLD ".%s", get_basename(mkv_ctx->filename), lang_to_use, + track->lang_index, matroska_track_text_subtitle_id_extensions[track->codec_id]); return buf; } @@ -1263,6 +1317,8 @@ void free_sub_track(struct matroska_sub_track *track) free(track->header); if (track->lang != NULL) free(track->lang); + if (track->lang_ietf != NULL) + free(track->lang_ietf); if (track->codec_id_string != NULL) free(track->codec_id_string); for (int i = 0; i < track->sentence_count; i++) @@ -1281,7 +1337,12 @@ void matroska_save_all(struct matroska_ctx *mkv_ctx, char *lang) { if (lang) { - if ((match = strstr(lang, mkv_ctx->sub_tracks[i]->lang)) != NULL) + // Try to match against IETF tag first if available + if (mkv_ctx->sub_tracks[i]->lang_ietf && + (match = strstr(lang, mkv_ctx->sub_tracks[i]->lang_ietf)) != NULL) + save_sub_track(mkv_ctx, mkv_ctx->sub_tracks[i]); + // Fall back to 3-letter code + else if ((match = strstr(lang, mkv_ctx->sub_tracks[i]->lang)) != NULL) save_sub_track(mkv_ctx, mkv_ctx->sub_tracks[i]); } else @@ -1337,9 +1398,13 @@ void matroska_parse(struct matroska_ctx *mkv_ctx) default: if (code_len == MATROSKA_MAX_ID_LENGTH) { - mprint(MATROSKA_ERROR "Unknown element 0x%x at position " LLD ", skipping file parsing\n", code, + mprint(MATROSKA_WARNING "Unknown element 0x%x at position " LLD ", skipping this element\n", code, get_current_byte(file) - MATROSKA_MAX_ID_LENGTH); - return; + // Skip just the unknown element, not the entire block + read_vint_block_skip(file); + // Reset code and code_len to start fresh with next element + code = 0; + code_len = 0; } break; } diff --git a/src/lib_ccx/matroska.h b/src/lib_ccx/matroska.h index fe8506001..c84410b32 100644 --- a/src/lib_ccx/matroska.h +++ b/src/lib_ccx/matroska.h @@ -120,6 +120,7 @@ /* Misc ids */ #define MATROSKA_VOID 0xEC #define MATROSKA_CRC32 0xBF +#define MATROSKA_SEGMENT_TRACK_LANGUAGE_IETF 0x22B59D /* DEFENCE FROM THE FOOL - deprecated IDs */ #define MATROSKA_SEGMENT_TRACK_TRACK_TIMECODE_SCALE 0x23314F @@ -214,6 +215,7 @@ struct matroska_avc_frame { struct matroska_sub_track { char* header; // Style header for ASS/SSA (and other) subtitles char* lang; + char *lang_ietf; //IETF language tag (BCP47) ULLONG track_number; ULLONG lang_index; enum matroska_track_subtitle_codec_id codec_id; diff --git a/src/lib_ccx/ocr.c b/src/lib_ccx/ocr.c index 1ca0d89b8..40477fb8b 100644 --- a/src/lib_ccx/ocr.c +++ b/src/lib_ccx/ocr.c @@ -9,7 +9,6 @@ #include "ccx_encoders_helpers.h" #include "ccx_encoders_spupng.h" #include "ocr.h" -#undef OCR_DEBUG struct ocrCtx { @@ -686,7 +685,6 @@ char *ocr_bitmap(void *arg, png_color *palette, png_byte *alpha, unsigned char * TessResultIteratorDelete(ri); } // End Color Detection - freep(&text_out); boxDestroy(&crop_points); pixDestroy(&pix); @@ -698,23 +696,15 @@ char *ocr_bitmap(void *arg, png_color *palette, png_byte *alpha, unsigned char * return text_out; } -void erode(png_color *palette, png_byte *alpha, uint8_t *bitmap, int w, int h, int nb_color) +void erode(png_color *palette, png_byte *alpha, uint8_t *bitmap, int w, int h, int nb_color, int background_index) { - int background_index; - for (background_index = 0; background_index < nb_color; background_index++) - { - if (alpha[background_index]) - { - break; - } - } // we will use a 2*2 kernel for the erosion for (int row = 0; row < h - 1; row++) { for (int col = 0; col < w - 1; col++) { - if (alpha[bitmap[row * w + col]] || alpha[bitmap[(row + 1) * w + col]] || - alpha[bitmap[row * w + (col + 1)]] || alpha[bitmap[(row + 1) * w + (col + 1)]]) + if (bitmap[row * w + col] == background_index || bitmap[(row + 1) * w + col] == background_index || + bitmap[row * w + (col + 1)] == background_index || bitmap[(row + 1) * w + (col + 1)] == background_index) { bitmap[row * w + col] = background_index; } @@ -722,23 +712,15 @@ void erode(png_color *palette, png_byte *alpha, uint8_t *bitmap, int w, int h, i } } -void dilate(png_color *palette, png_byte *alpha, uint8_t *bitmap, int w, int h, int nb_color) +void dilate(png_color *palette, png_byte *alpha, uint8_t *bitmap, int w, int h, int nb_color, int foreground_index) { - int foreground_index; - for (foreground_index = 0; foreground_index < nb_color; foreground_index++) - { - if (!alpha[foreground_index]) - { - break; - } - } // we will use a 2*2 kernel for the erosion for (int row = 0; row < h - 1; row++) { for (int col = 0; col < w - 1; col++) { - if (!(alpha[bitmap[row * w + col]] && alpha[bitmap[(row + 1) * w + col]] && - alpha[bitmap[row * w + (col + 1)]] && alpha[bitmap[(row + 1) * w + (col + 1)]])) + if ((bitmap[row * w + col] == foreground_index && bitmap[(row + 1) * w + col] == foreground_index && + bitmap[row * w + (col + 1)] == foreground_index && bitmap[(row + 1) * w + (col + 1)] == foreground_index)) { bitmap[row * w + col] = foreground_index; } @@ -769,6 +751,7 @@ static int quantize_map(png_byte *alpha, png_color *palette, */ uint32_t *mcit = NULL; struct transIntensity ti = {alpha, palette}; + int text_color, text_bg_color; int ret = 0; @@ -835,6 +818,14 @@ static int quantize_map(png_byte *alpha, png_color *palette, max_ind = j; } } + + // Assume second most frequent color to be text background (first is alpha channel) + if (i == 1) + text_bg_color = iot[max_ind]; + // Assume third most frequent color to be text color + if (i == 2) + text_color = iot[max_ind]; + for (j = i; j > 0 && max_ind < mcit[j - 1]; j--) { mcit[j] = mcit[j - 1]; @@ -878,8 +869,8 @@ static int quantize_map(png_byte *alpha, png_color *palette, palette[iot[i]].green = palette[index].green; } } - erode(palette, alpha, bitmap, w, h, nb_color); - dilate(palette, alpha, bitmap, w, h, nb_color); + erode(palette, alpha, bitmap, w, h, nb_color, text_bg_color); + dilate(palette, alpha, bitmap, w, h, nb_color, text_color); #ifdef OCR_DEBUG ccx_common_logging.log_ftn("Colors present in quantized Image\n"); for (int i = 0; i < nb_color; i++) @@ -1062,7 +1053,13 @@ char *paraof_ocrtext(struct cc_subtitle *sub, struct encoder_ctx *context) len += strlen(rect->ocr_text); } if (len <= 0) + { + for (i = 0, rect = sub->data; i < sub->nb_data; i++, rect++) + { + freep(&rect->ocr_text); + } return NULL; + } else { str = malloc(len + 1 + 10); // Extra space for possible trailing '/n's at the end of tesseract UTF8 text @@ -1076,7 +1073,7 @@ char *paraof_ocrtext(struct cc_subtitle *sub, struct encoder_ctx *context) if (!rect->ocr_text) continue; add_ocrtext2str(str, rect->ocr_text, context->encoded_crlf, context->encoded_crlf_length); - free(rect->ocr_text); + freep(&rect->ocr_text); } return str; } diff --git a/src/lib_ccx/zvbi/raw_decoder.c b/src/lib_ccx/zvbi/raw_decoder.c index 4161c1cd1..76095634c 100644 --- a/src/lib_ccx/zvbi/raw_decoder.c +++ b/src/lib_ccx/zvbi/raw_decoder.c @@ -188,7 +188,7 @@ _vbi_service_table [] = { /* No useful FRC, but a six bit CRC */ 0, }, { -#endif +#endif VBI_SLICED_CAPTION_525_F1, "Closed Caption 525, field 1", VBI_VIDEOSTD_SET_525_60, diff --git a/src/rust/CMakeLists.txt b/src/rust/CMakeLists.txt index 595a3f9c1..680d081ca 100644 --- a/src/rust/CMakeLists.txt +++ b/src/rust/CMakeLists.txt @@ -2,7 +2,7 @@ include(FetchContent) FetchContent_Declare( Corrosion GIT_REPOSITORY https://github.com/corrosion-rs/corrosion.git - GIT_TAG 64289b1d79d6d19cd2e241db515381a086bb8407 # v0.5.0 + GIT_TAG v0.5.1 FIND_PACKAGE_ARGS ) FetchContent_MakeAvailable(Corrosion) 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..4f9b40452 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -28,6 +28,12 @@ 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.toml b/src/rust/lib_ccxr/Cargo.toml index 6162aef3a..5008e9188 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 = { version = "0.7.2" } +prost = "0.13.4" +prost-types = "0.13.4" +lazy_static = "1.5.0" +nanomsg-sys = "0.7.2" [features] default = [ diff --git a/src/rust/lib_ccxr/src/decoder_vbi/exit_codes.rs b/src/rust/lib_ccxr/src/decoder_vbi/exit_codes.rs new file mode 100644 index 000000000..283341901 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_vbi/exit_codes.rs @@ -0,0 +1,102 @@ +// 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 = 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; + +// 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, vbi common 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; + +// vbi specific codes + +pub const _VBI3_RAW_DECODER_MAX_JOBS: usize = 8; + +pub const VBI_SLICED_CAPTION_525_F1: u32 = 0x00000020; +pub const VBI_SLICED_CAPTION_525_F2: u32 = 0x00000040; +pub const VBI_SLICED_CAPTION_525: u32 = VBI_SLICED_CAPTION_525_F1 | VBI_SLICED_CAPTION_525_F2; + +pub const VBI_SLICED_VBI_525: u32 = 0x40000000; +pub const VBI_SLICED_VBI_625: u32 = 0x20000000; +pub const _VBI3_RAW_DECODER_MAX_WAYS: usize = 8; + +pub const VBI_SLICED_TELETEXT_B_L10_625: u32 = 0x00000001; +pub const VBI_SLICED_TELETEXT_B_L25_625: u32 = 0x00000002; +pub const VBI_SLICED_TELETEXT_B: u32 = + VBI_SLICED_TELETEXT_B_L10_625 | VBI_SLICED_TELETEXT_B_L25_625; + +pub const VBI_SLICED_CAPTION_625_F1: u32 = 0x00000008; +pub const VBI_SLICED_CAPTION_625_F2: u32 = 0x00000010; +pub const VBI_SLICED_CAPTION_625: u32 = VBI_SLICED_CAPTION_625_F1 | VBI_SLICED_CAPTION_625_F2; + +pub const VBI_SLICED_VPS: u32 = 0x00000004; +pub const VBI_SLICED_VPS_F2: u32 = 0x00001000; + +pub const VBI_SLICED_WSS_625: u32 = 0x00000400; + +pub const CCX_DMT_PARSE: i32 = 1; + +pub const VBI_VIDEOSTD_SET_EMPTY: u64 = 0; +pub const VBI_VIDEOSTD_SET_PAL_BG: u64 = 1; +pub const VBI_VIDEOSTD_SET_625_50: u64 = 1; +pub const VBI_VIDEOSTD_SET_525_60: u64 = 2; +pub const VBI_VIDEOSTD_SET_ALL: u64 = 3; +pub const VBI_SLICED_TELETEXT_A: u32 = 0x00002000; +pub const VBI_SLICED_TELETEXT_C_625: u32 = 0x00004000; +pub const VBI_SLICED_TELETEXT_D_625: u32 = 0x00008000; +pub const VBI_SLICED_TELETEXT_B_525: u32 = 0x00010000; +pub const VBI_SLICED_TELETEXT_C_525: u32 = 0x00000100; +pub const VBI_SLICED_TELETEXT_D_525: u32 = 0x00020000; +pub const VBI_SLICED_2XCAPTION_525: u32 = 0x00000080; // VBI_SLICED_2xCAPTION_525 + +pub const DEF_THR_FRAC: u32 = 9; +pub const LP_AVG: u32 = 4; +pub const RAW_DECODER_PATTERN_DUMP: bool = false; +pub const VBI_SLICED_TELETEXT_BD_525: u32 = 0x00000200; diff --git a/src/rust/lib_ccxr/src/decoder_vbi/functions_vbi.rs b/src/rust/lib_ccxr/src/decoder_vbi/functions_vbi.rs new file mode 100644 index 000000000..1b9eb2ff9 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_vbi/functions_vbi.rs @@ -0,0 +1,130 @@ +use log::info; + +use crate::decode_vbi::exit_codes::*; +use crate::decode_vbi::structs_ccdecode::*; +use crate::decode_vbi::structs_isdb::*; +use crate::decode_vbi::structs_vbi::*; +use crate::decode_vbi::structs_xds::*; + +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_long, c_uchar, c_uint, c_void}; + +use crate::common::BufferdataType; // ccxbufferdatatype + +use crate::common::FrameType; // ccxframetype + +use crate::util::log::GuiXdsMessage; // CcxCommonLoggingGui + +// | `fatal`, `ccx_common_logging.fatal_ftn` | [`fatal!`] | +// | `mprint`, `ccx_common_logging.log_ftn` | [`info!`] | +// | `dbg_print`, `ccx_common_logging.debug_ftn` | [`debug!`] | +// | `activity_library_process`, `ccx_common_logging.gui_ftn` | [`send_gui`] | +// #[repr(C)] +// pub struct CcxCommonLogging { +// pub debug_mask: i64, // Equivalent to LLONG in C +// pub fatal_ftn: Option, +// pub debug_ftn: Option, +// pub log_ftn: Option, +// pub gui_ftn: Option, +// } + +//-------------------vbi-functions-start------------------------- + +pub fn vbi3_raw_decoder_debug(rd: &mut Vbi3RawDecoder, enable: bool) -> bool { + let mut _sp_lines: Option> = None; // Not used in the original code + let mut result = true; + + rd.debug = enable; + + let mut n_lines = 0; + if enable { + n_lines = rd.sampling.count[0] + rd.sampling.count[1]; + } + + match rd.sampling.sampling_format { + VbiPixfmt::Yuv420 => {} + _ => { + // Not implemented + n_lines = 0; + result = false; + } + } + + if rd.n_sp_lines == n_lines as i64 { + return result; + } + + rd.sp_lines.clear(); + rd.n_sp_lines = 0; + + if n_lines > 0 { + rd.sp_lines = vec![Vbi3RawDecoderSpLine::default(); n_lines as usize]; + rd.n_sp_lines = n_lines as i64; + } + + result +} + +pub fn vbi3_raw_decoder_reset(rd: &mut Vbi3RawDecoder) { + assert!(!rd.pattern.is_empty()); + + rd.pattern.clear(); + rd.services = 0; + rd.n_jobs = 0; + rd.readjust = 1; + + rd.jobs + .iter_mut() + .for_each(|job| *job = Vbi3RawDecoderJob::default()); +} + +pub unsafe fn _vbi3_raw_decoder_destroy(rd: *mut Vbi3RawDecoder) { + vbi3_raw_decoder_reset(&mut *rd); + + vbi3_raw_decoder_debug(&mut *rd, false); + + // Make unusable + std::ptr::write_bytes(rd, 0, 1); +} + +pub fn vbi3_raw_decoder_delete(rd: Option>) { + if let Some(rd) = rd { + unsafe { + _vbi3_raw_decoder_destroy(Box::into_raw(rd)); + } + } +} + +pub unsafe fn vbi_raw_decoder_destroy(rd: *mut VbiRawDecoder) { + assert!(!rd.is_null()); + + let rd3 = (*rd).pattern as *mut Vbi3RawDecoder; + + vbi3_raw_decoder_delete(if rd3.is_null() { + None + } else { + Some(Box::from_raw(rd3)) + }); + + std::ptr::write_bytes(rd, 0, 1); +} + +pub fn vbi_pixfmt_bpp(fmt: VbiPixfmt) -> i32 { + match fmt { + VbiPixfmt::Yuv420 => 1, + VbiPixfmt::Rgba32Le | VbiPixfmt::Rgba32Be | VbiPixfmt::Bgra32Le | VbiPixfmt::Bgra32Be => 4, + VbiPixfmt::Rgb24 | VbiPixfmt::Bgr24 => 3, + _ => 2, + } +} diff --git a/src/rust/lib_ccxr/src/decoder_vbi/mod.rs b/src/rust/lib_ccxr/src/decoder_vbi/mod.rs new file mode 100644 index 000000000..4557af8a3 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_vbi/mod.rs @@ -0,0 +1,7 @@ +pub mod codeegg; +pub mod functions_vbi; +pub mod structs_ccdecode; +pub mod structs_isdb; +pub mod structs_vbi; +pub mod structs_xds; +pub mod vbiegg; diff --git a/src/rust/lib_ccxr/src/decoder_vbi/structs_ccdecode.rs b/src/rust/lib_ccxr/src/decoder_vbi/structs_ccdecode.rs new file mode 100644 index 000000000..e1ad514bc --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_vbi/structs_ccdecode.rs @@ -0,0 +1,280 @@ +//------------------------ccdecode-structs-start--------------------- + +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 +} + +//------------------------ccdecode-structs-end--------------------- diff --git a/src/rust/lib_ccxr/src/decoder_vbi/structs_isdb.rs b/src/rust/lib_ccxr/src/decoder_vbi/structs_isdb.rs new file mode 100644 index 000000000..2759efda9 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_vbi/structs_isdb.rs @@ -0,0 +1,794 @@ +use crate::decode_vbi::exit_codes::*; +use crate::decode_vbi::structs_xds::*; +use crate::time::*; + +//----------------------isdb_structs-start------------------------- +use std::os::raw::c_ulonglong; + +#[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 +} + +#[derive(Debug)] +#[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], +} + +#[derive(Debug)] +#[repr(C)] +pub struct VbiRawDecoderJob { + pub id: c_uint, + pub offset: c_int, + pub slicer: VbiBitSlicer, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +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, +} + +#[derive(Debug)] +#[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(), + } + } +} + +//----------------------isdb_structs-end--------------------------- diff --git a/src/rust/lib_ccxr/src/decoder_vbi/structs_vbi.rs b/src/rust/lib_ccxr/src/decoder_vbi/structs_vbi.rs new file mode 100644 index 000000000..9c2f5b512 --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_vbi/structs_vbi.rs @@ -0,0 +1,268 @@ +use crate::decode_vbi::exit_codes::*; +use crate::decode_vbi::structs_xds::*; + +//----------------------vbi_structs-start------------------------- + +#[derive(Debug)] +#[repr(C)] +pub struct Vbi3RawDecoder { + sampling: VbiSamplingPar, + services: VbiServiceSet, + log: VbiLogHook, + debug: bool, + n_jobs: i64, + n_sp_lines: i64, + readjust: i64, + pattern: Vec, // n scan lines * MAX_WAYS + jobs: [Vbi3RawDecoderJob; 8], // VBI3_RAW_DECODER_MAX_JOBS = 8 + sp_lines: Vec, +} + +pub type VbiServiceSet = i64; + +pub type VbiBool = i64; + +#[derive(Debug)] +#[repr(C)] +pub struct Vbi3RawDecoderJob { + id: VbiServiceSet, + slicer: Vbi3BitSlicer, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub enum Vbi3BitSlicerBit { + CriBit = 1, + FrcBit, + PayloadBit, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Vbi3BitSlicerPoint { + /** Whether this struct refers to a CRI, FRC or payload bit. */ + kind: Vbi3BitSlicerBit, + + /** Number of the sample times 256. */ + index: i64, + + /** Signal amplitude at this sample, in range 0 to 65535. */ + level: i64, + + /** 0/1 threshold at this sample, in range 0 to 65535. */ + thresh: i64, +} + +pub type Vbi3BitSlicerFn = fn( + bs: &mut Vbi3BitSlicer, + buffer: &mut [u8], + points: &mut [Vbi3BitSlicerPoint], + n_points: &mut i64, + raw: &[u8], +) -> VbiBool; + +#[derive(Debug)] +#[repr(C)] +pub struct Vbi3BitSlicer { + func: Option, + sample_format: VbiPixfmt, + cri: i64, + cri_mask: i64, + thresh: i64, + thresh_frac: i64, + cri_samples: i64, + cri_rate: i64, + oversampling_rate: i64, + phase_shift: i64, + step: i64, + frc: i64, + frc_bits: i64, + total_bits: i64, + payload: i64, + endian: i64, + bytes_per_sample: i64, + skip: i64, + green_mask: i64, + log: VbiLogHook, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub enum VbiLogMask { + /** External error causes, for example lack of memory. */ + Error = 1 << 3, + + /** + * Invalid parameters and similar problems which suggest + * a bug in the application using the library. + */ + Warning = 1 << 4, + + /** + * Causes of possibly undesired results, for example when a + * data service cannot be decoded with the current video + * standard setting. + */ + Notice = 1 << 5, + + /** Progress messages. */ + Info = 1 << 6, + + /** Information useful to debug the library. */ + Debug = 1 << 7, + + /** Driver responses (strace). Not implemented yet. */ + Driver = 1 << 8, + + /** More detailed debugging information. */ + Debug2 = 1 << 9, + Debug3 = 1 << 10, +} + +#[derive(Debug, Clone, Copy)] +#[repr(C)] +pub struct Vbi3RawDecoderSpLine { + points: [Vbi3BitSlicerPoint; 512], + n_points: i64, +} + +pub type VbiLogFn = + fn(level: VbiLogMask, context: &str, message: &str, user_data: &mut dyn std::any::Any); + +#[derive(Debug)] +#[repr(C)] +pub struct VbiLogHook { + func: Option, + user_data: Option>, + mask: VbiLogMask, +} + +pub type VbiSamplingPar = VbiRawDecoder; + +#[repr(C)] +pub struct CcxDecoderVbiCfg { + pub debug_file_name: *mut c_char, +} + +//-----------defaults-vbi-satart-------------- +impl Default for Vbi3RawDecoderSpLine { + fn default() -> Self { + Self { + points: [Vbi3BitSlicerPoint::default(); 512], + n_points: 0, + } + } +} + +impl Default for Vbi3BitSlicerPoint { + fn default() -> Self { + Self { + kind: Vbi3BitSlicerBit::CriBit, + index: 0, + level: 0, + thresh: 0, + } + } +} + +impl Default for Vbi3BitSlicerBit { + fn default() -> Self { + Vbi3BitSlicerBit::CriBit + } +} + +impl Default for Vbi3RawDecoderJob { + fn default() -> Self { + Self { + id: 0, + slicer: Vbi3BitSlicer::default(), + } + } +} + +impl Default for Vbi3BitSlicer { + fn default() -> Self { + Self { + func: None, + sample_format: VbiPixfmt::Yuv420, + cri: 0, + cri_mask: 0, + thresh: 0, + thresh_frac: 0, + cri_samples: 0, + cri_rate: 0, + oversampling_rate: 0, + phase_shift: 0, + step: 0, + frc: 0, + frc_bits: 0, + total_bits: 0, + payload: 0, + endian: 0, + bytes_per_sample: 0, + skip: 0, + green_mask: 0, + log: VbiLogHook::default(), + } + } +} + +impl Default for VbiLogHook { + fn default() -> Self { + Self { + func: None, + user_data: None, + mask: VbiLogMask::Error, + } + } +} + +impl Default for VbiLogMask { + fn default() -> Self { + VbiLogMask::Error + } +} + +impl Default for VbiRawDecoderJob { + fn default() -> Self { + Self { + id: 0, + offset: 0, + slicer: VbiBitSlicer::default(), + } + } +} + +// impl Default for Vbi3RawDecoderJob { +// fn default() -> Self { +// Self { +// id: 0, +// slicer: Vbi3BitSlicer::default(), +// } +// } +// } + +impl Default for VbiBitSlicer { + fn default() -> Self { + Self { + func: None, + cri: 0, + cri_mask: 0, + thresh: 0, + cri_bytes: 0, + cri_rate: 0, + oversampling_rate: 0, + phase_shift: 0, + step: 0, + frc: 0, + frc_bits: 0, + payload: 0, + endian: 0, + skip: 0, + } + } +} + +//----------------defaults-vbi-end------------------- + +//-------------------vbi-structs-end----------------------------- diff --git a/src/rust/lib_ccxr/src/decoder_vbi/structs_xds.rs b/src/rust/lib_ccxr/src/decoder_vbi/structs_xds.rs new file mode 100644 index 000000000..ec934974a --- /dev/null +++ b/src/rust/lib_ccxr/src/decoder_vbi/structs_xds.rs @@ -0,0 +1,395 @@ +use crate::decode_vbi::exit_codes::*; +use crate::time::*; + +//------------------------xds-structs-start------------------------ + +#[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(), + } + } +} + +//----------------------xds-struct-end------------------------ diff --git a/src/rust/lib_ccxr/src/lib.rs b/src/rust/lib_ccxr/src/lib.rs index 9f32678db..500b3cd9a 100644 --- a/src/rust/lib_ccxr/src/lib.rs +++ b/src/rust/lib_ccxr/src/lib.rs @@ -1,5 +1,6 @@ pub mod activity; pub mod common; +pub mod decoder_vbi; pub mod hardsubx; pub mod subtitle; pub mod teletext; diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs index c2f64a258..57fa7d0d4 100644 --- a/src/rust/src/libccxr_exports/mod.rs +++ b/src/rust/src/libccxr_exports/mod.rs @@ -1,6 +1,7 @@ //! Provides C-FFI functions that are direct equivalent of functions available in C. pub mod time; + use crate::ccx_options; use lib_ccxr::util::log::*; use lib_ccxr::util::{bits::*, levenshtein::*};