diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index c654aceac..c81af5327 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -74,22 +74,6 @@ jobs: working-directory: build - name: Display version information run: ./build/ccextractor --version - cmake_ocr_hardsubx: - runs-on: macos-latest - steps: - - uses: actions/checkout@v4 - - name: Install dependencies - run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg - - name: cmake - run: | - mkdir build && cd build - cmake -DWITH_OCR=ON -DWITH_HARDSUBX=ON ../src - - name: build - run: | - make -j$(nproc) - working-directory: build - - name: Display version information - run: ./build/ccextractor --version build_rust: runs-on: macos-latest steps: diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 7a3d84f1d..f54201388 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -51,4 +51,4 @@ jobs: run: cargo fmt --all -- --check - name: clippy run: | - cargo clippy --all-features -- -D warnings + cargo clippy -- -D warnings diff --git a/.github/workflows/test_rust.yml b/.github/workflows/test_rust.yml new file mode 100644 index 000000000..4791f821c --- /dev/null +++ b/.github/workflows/test_rust.yml @@ -0,0 +1,37 @@ +name: Unit Test Rust +on: + push: + paths: + - ".github/workflows/test.yml" + - "src/rust/**" + tags-ignore: + - "*.*" + pull_request: + types: [opened, synchronize, reopened] + paths: + - ".github/workflows/test.yml" + - "src/rust/**" +jobs: + test_rust: + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./src/rust + steps: + - uses: actions/checkout@v4 + - name: cache + uses: actions/cache@v3 + with: + path: | + src/rust/.cargo/registry + src/rust/.cargo/git + src/rust/target + key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }} + restore-keys: ${{ runner.os }}-cargo- + - uses: actions-rs/toolchain@v1 + with: + toolchain: stable + override: true + - name: Test main module + run: cargo test + working-directory: ./src/rust diff --git a/.gitignore b/.gitignore index 3f8a2ed82..0628f64fb 100644 --- a/.gitignore +++ b/.gitignore @@ -17,8 +17,10 @@ CVS mac/ccextractor linux/ccextractor linux/depend +windows/x86_64-pc-windows-msvc/** windows/Debug/** windows/Debug-OCR/** +windows/release-with-debug/** windows/Release/** windows/Release-Full/** windows/Release-OCR/** @@ -154,3 +156,4 @@ windows/ccx_rust.lib windows/*/debug/* windows/*/CACHEDIR.TAG windows/.rustc_info.json +linux/configure~ diff --git a/docs/CHANGES.TXT b/docs/CHANGES.TXT index bf6b6503a..a5cd21720 100644 --- a/docs/CHANGES.TXT +++ b/docs/CHANGES.TXT @@ -1,5 +1,6 @@ -0.95 (to be released) +1.0 (to be released) ----------------- +- Breaking: Major argument flags revamp for CCExtractor (#1564 & #1619) - New: Create a Docker image to simplify the CCExtractor usage without any environmental hustle (#1611) - New: Add time units module in lib_ccxr (#1623) - New: Add bits and levenshtein module in lib_ccxr (#1627) diff --git a/docs/FFMPEG.md b/docs/FFMPEG.md index a722c07a4..335ae0dd8 100644 --- a/docs/FFMPEG.md +++ b/docs/FFMPEG.md @@ -42,7 +42,7 @@ Note:If you installed ffmpeg on non-standard location, please change/update your ### On Windows: #### Set preprocessor flag `ENABLE_FFMPEG=1` -1. In visual studio 2013 right click and select property. +1. In visual studio 2022 right click and select property. 2. In the left panel, select Configuration Properties, C/C++, Preprocessor. 3. In the right panel, in the right-hand column of the Preprocessor Definitions property, open the drop-down menu and choose Edit. 4. In the Preprocessor Definitions dialog box, add `ENABLE_FFMPEG=1`. Choose OK to save your changes. diff --git a/docs/OCR.md b/docs/OCR.md index 99f52c7c8..10edc1813 100644 --- a/docs/OCR.md +++ b/docs/OCR.md @@ -93,7 +93,7 @@ Download prebuild library of leptonica and tesseract from following link https://drive.google.com/file/d/0B2ou7ZfB-2nZOTRtc3hJMHBtUFk/view?usp=sharing put the path of libs/include of leptonica and tesseract in library paths. -1. In visual studio 2013 right click and select property. +1. In visual studio 2022 right click and select property. 2. Select Configuration properties in left panel(column) of property. 3. Select VC++ Directory. 4. In the right pane, in the right-hand column of the VC++ Directory property, open the drop-down menu and choose Edit. @@ -101,7 +101,7 @@ put the path of libs/include of leptonica and tesseract in library paths. Set preprocessor flag ENABLE_OCR=1 -1. In visual studio 2013 right click and select property. +1. In visual studio 2022 right click and select property. 2. In the left panel, select Configuration Properties, C/C++, Preprocessor. 3. In the right panel, in the right-hand column of the Preprocessor Definitions property, open the drop-down menu and choose Edit. 4. In the Preprocessor Definitions dialog box, add ENABLE_OCR=1. Choose OK to save your changes. diff --git a/mac/Makefile.am b/mac/Makefile.am index 9933556aa..9870c07bf 100644 --- a/mac/Makefile.am +++ b/mac/Makefile.am @@ -249,6 +249,7 @@ GPAC_CPPFLAGS = $(shell pkg-config --cflags gpac) ccextractor_CPPFLAGS =-I../src/lib_ccx/ -I../src/thirdparty/libpng/ -I../src/thirdparty/zlib/ -I../src/lib_ccx/zvbi/ -I../src/thirdparty/lib_hash/ -I../src/thirdparty/protobuf-c/ -I../src/thirdparty -I../src/ -I../src/thirdparty/freetype/include/ ccextractor_CPPFLAGS += $(GPAC_CPPFLAGS) +ccextractor_CPPFLAGS += $(FFMPEG_CPPFLAGS) ccextractor_LDADD=-lm -lpthread -ldl @@ -271,7 +272,7 @@ if HARDSUBX_IS_ENABLED ccextractor_CFLAGS += -DENABLE_HARDSUBX ccextractor_CPPFLAGS+= ${libavcodec_CFLAGS} ccextractor_CPPFLAGS+= ${libavformat_CFLAGS} -ccextractor_CPPFLAGS+= ${libavutil_CFALGS} +ccextractor_CPPFLAGS+= ${libavutil_CFLAGS} ccextractor_CPPFLAGS+= ${libswscale_CFLAGS} AV_LIB = ${libavcodec_LIBS} AV_LIB += ${libavformat_LIBS} diff --git a/src/ccextractor.c b/src/ccextractor.c index 1e089f9dd..3f159f716 100644 --- a/src/ccextractor.c +++ b/src/ccextractor.c @@ -446,7 +446,11 @@ int main(int argc, char *argv[]) // If "ccextractor.cnf" is present, takes options from it. // See docs/ccextractor.cnf.sample for more info. +#ifndef DISABLE_RUST + int compile_ret = ccxr_parse_parameters(api_options, argc, argv); +#else int compile_ret = parse_parameters(api_options, argc, argv); +#endif if (compile_ret == EXIT_NO_INPUT_FILES) { diff --git a/src/lib_ccx/lib_ccx.h b/src/lib_ccx/lib_ccx.h index 3d107826e..f3defaa9a 100644 --- a/src/lib_ccx/lib_ccx.h +++ b/src/lib_ccx/lib_ccx.h @@ -161,6 +161,9 @@ extern void ccxr_init_basic_logger(struct ccx_s_options *opts); void print_end_msg(void); //params.c +#ifndef DISABLE_RUST +extern int ccxr_parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]); +#endif int parse_parameters (struct ccx_s_options *opt, int argc, char *argv[]); void print_usage (void); int atoi_hex (char *s); diff --git a/src/lib_ccx/matroska.c b/src/lib_ccx/matroska.c index bf6e738f9..01b7e634f 100644 --- a/src/lib_ccx/matroska.c +++ b/src/lib_ccx/matroska.c @@ -1362,8 +1362,8 @@ int matroska_loop(struct lib_ccx_ctx *ctx) { if (ccx_options.write_format_rewritten) { - mprint(MATROSKA_WARNING "You are using -out=, but Matroska parser extract subtitles in a recorded format\n"); - mprint("-out= will be ignored\n"); + mprint(MATROSKA_WARNING "You are using --out=, but Matroska parser extract subtitles in a recorded format\n"); + mprint("--out= will be ignored\n"); } // Don't need generated input file diff --git a/src/lib_ccx/params.c b/src/lib_ccx/params.c index eb1562e50..5f96fc732 100644 --- a/src/lib_ccx/params.c +++ b/src/lib_ccx/params.c @@ -127,6 +127,13 @@ int parsedelay(struct ccx_s_options *opt, char *par) return 0; } +void set_binary_mode() +{ +#ifdef WIN32 + setmode(fileno(stdin), O_BINARY); +#endif +} + int append_file_to_queue(struct ccx_s_options *opt, char *filename) { if (filename[0] == '\0') // skip files with empty file name (ex : ./ccextractor "") @@ -978,14 +985,14 @@ void print_usage(void) mprint(" a .d extension. Each .png file will contain an image representing one caption\n"); mprint(" and named subNNNN.png, starting with sub0000.png.\n"); mprint(" For example, the command:\n"); - mprint(" ccextractor -out=spupng input.mpg\n"); + mprint(" ccextractor --out=spupng input.mpg\n"); mprint(" will create the files:\n"); mprint(" input.xml\n"); mprint(" input.d/sub0000.png\n"); mprint(" input.d/sub0001.png\n"); mprint(" ...\n"); mprint(" The command:\n"); - mprint(" ccextractor -out=spupng -o /tmp/output --12 input.mpg\n"); + mprint(" ccextractor --out=spupng -o /tmp/output --12 input.mpg\n"); mprint(" will create the files:\n"); mprint(" /tmp/output_1.xml\n"); mprint(" /tmp/output_1.d/sub0000.png\n"); @@ -1245,9 +1252,8 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } if (strcmp(argv[i], "-") == 0 || strcmp(argv[i], "--stdin") == 0) { -#ifdef WIN32 - setmode(fileno(stdin), O_BINARY); -#endif + set_binary_mode(); + opt->input_source = CCX_DS_STDIN; if (!opt->live_stream) opt->live_stream = -1; @@ -2934,7 +2940,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } if (opt->write_format == CCX_OF_SPUPNG && opt->cc_to_stdout) { - print_error(opt->gui_mode_reports, "You cannot use -out=spupng with -stdout.\n"); + print_error(opt->gui_mode_reports, "You cannot use --out=spupng with -stdout.\n"); return EXIT_INCOMPATIBLE_PARAMETERS; } diff --git a/src/lib_ccx/ts_tables.c b/src/lib_ccx/ts_tables.c index f370d1669..ceb99a383 100644 --- a/src/lib_ccx/ts_tables.c +++ b/src/lib_ccx/ts_tables.c @@ -332,7 +332,7 @@ int parse_PMT(struct ccx_demuxer *ctx, unsigned char *buf, int len, struct progr #ifndef ENABLE_OCR if (ccx_options.write_format != CCX_OF_SPUPNG) { - mprint("DVB subtitles detected, OCR subsystem not present. Use -out=spupng for graphic output\n"); + mprint("DVB subtitles detected, OCR subsystem not present. Use --out=spupng for graphic output\n"); continue; } #endif diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 309e944bd..5c4957377 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -11,6 +11,55 @@ dependencies = [ "memchr", ] +[[package]] +name = "anstream" +version = "0.6.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418c75fa768af9c03be99d17643f93f79bbba589895012a80e3452a19ddda15b" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" + +[[package]] +name = "anstyle-parse" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03a11a9034d92058ceb6ee011ce58af4a9bf61491aa7e1e59ecd24bd40d22d4" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a64c907d4e79225ac72e2a354c9ce84d50ebb4586dee56c82b3ee73004f537f5" +dependencies = [ + "windows-sys", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61a38449feb7068f52bb06c12759005cf459ee52bb4adc1d5a7c4322d716fb19" +dependencies = [ + "anstyle", + "windows-sys", +] + [[package]] name = "approx" version = "0.5.1" @@ -78,7 +127,7 @@ dependencies = [ "regex", "rustc-hash", "shlex", - "syn 2.0.71", + "syn 2.0.66", "which", ] @@ -105,14 +154,21 @@ name = "ccx_rust" version = "0.1.0" dependencies = [ "bindgen 0.64.0", + "cfg-if", + "clap", "env_logger", "iconv", "leptonica-sys", "lib_ccxr", "log", + "num-integer", "palette", + "pkg-config", "rsmpeg", + "strum", + "strum_macros", "tesseract-sys", + "time", ] [[package]] @@ -132,15 +188,61 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] name = "clang-sys" -version = "1.8.1" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +checksum = "67523a3b4be3ce1989d607a828d036249522dd9c1c8de7f4dd2dae43a37369d1" dependencies = [ "glob", "libc", "libloading", ] +[[package]] +name = "clap" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.66", +] + +[[package]] +name = "clap_lex" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" + +[[package]] +name = "colorchoice" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b6a852b24ab71dffc585bcb46eaf7959d175cb865a7152e35b348d1b2960422" + [[package]] name = "convert_case" version = "0.4.0" @@ -166,7 +268,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.71", + "syn 2.0.66", ] [[package]] @@ -177,9 +279,9 @@ checksum = "74c57ab96715773d9cb9789b38eb7cbf04b3c6f5624a9d98f51761603376767c" [[package]] name = "either" -version = "1.13.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" +checksum = "3dca9240753cf90908d7e4aac30f630662b02aebaa1b58a3cadabdb23385b58b" [[package]] name = "env_logger" @@ -219,6 +321,18 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" +[[package]] +name = "heck" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.1.19" @@ -253,6 +367,12 @@ dependencies = [ "libc", ] +[[package]] +name = "is_terminal_polyfill" +version = "1.70.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8478577c03552c21db0e2724ffb8986a5ce7af88107e6be5d2ee6e158c12800" + [[package]] name = "itertools" version = "0.12.1" @@ -282,9 +402,9 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55" [[package]] name = "leptonica-sys" -version = "0.4.8" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c924779fadc73838b9390ddda5fc1939f844fb43bd44ef6794c32bd6e52238a" +checksum = "eff3f1dc2f0112411228f8db99ca8a6a1157537a7887b28b1c91fdc4051fb326" dependencies = [ "bindgen 0.64.0", "pkg-config", @@ -309,9 +429,9 @@ checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" [[package]] name = "libloading" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e310b3a6b5907f99202fcdb4960ff45b93735d7c7d96b760fcff8db2dc0e103d" +checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" dependencies = [ "cfg-if", "windows-targets", @@ -325,15 +445,15 @@ checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" [[package]] name = "log" -version = "0.4.22" +version = "0.4.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" [[package]] name = "memchr" -version = "2.7.4" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" +checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" [[package]] name = "minimal-lexical" @@ -357,6 +477,15 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -438,7 +567,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.66", ] [[package]] @@ -469,14 +598,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f12335488a2f3b0a83b14edad48dca9879ce89b2edd10e80237e4e852dd645e" dependencies = [ "proc-macro2", - "syn 2.0.71", + "syn 2.0.66", ] [[package]] name = "proc-macro2" -version = "1.0.86" +version = "1.0.83" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77" +checksum = "0b33eb56c327dec362a9e55b3ad14f9d2f0904fb5a5b03b513ab5465399e9f43" dependencies = [ "unicode-ident", ] @@ -507,9 +636,9 @@ checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" [[package]] name = "regex" -version = "1.10.5" +version = "1.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91213439dad192326a0d7c6ee3955910425f441d7038e0d6933b0aec5c4517f" +checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" dependencies = [ "aho-corasick", "memchr", @@ -519,9 +648,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.7" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38caf58cc5ef2fed281f89292ef23f6365465ed9a41b7a7754eb4e26496c92df" +checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" dependencies = [ "aho-corasick", "memchr", @@ -530,9 +659,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.4" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a66a03ae7c801facd77a29370b4faec201768915ac14a721ba36f20bc9c209b" +checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" [[package]] name = "rsmpeg" @@ -574,6 +703,12 @@ dependencies = [ "windows-sys", ] +[[package]] +name = "rustversion" +version = "1.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6" + [[package]] name = "rusty_ffmpeg" version = "0.13.3+ffmpeg.6.1" @@ -596,22 +731,22 @@ checksum = "61697e0a1c7e512e84a621326239844a24d8207b4669b41bc18b32ea5cbf988b" [[package]] name = "serde" -version = "1.0.204" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc76f558e0cbb2a839d37354c575f1dc3fdc6546b5be373ba43d95f231bf7c12" +checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.204" +version = "1.0.202" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0cd7e117be63d3c3678776753929474f3b04a43a080c744d6b0ae2a8c28e222" +checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.66", ] [[package]] @@ -626,6 +761,31 @@ version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290d54ea6f91c969195bdbcd7442c8c2a2ba87da8bf60a7ee86a235d4bc1e125" + +[[package]] +name = "strum_macros" +version = "0.25.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23dc1fa9ac9c169a78ba62f0b841814b7abae11bdd047b9c58f893439e309ea0" +dependencies = [ + "heck 0.4.1", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.66", +] + [[package]] name = "syn" version = "1.0.109" @@ -639,9 +799,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.71" +version = "2.0.66" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b146dcf730474b4bcd16c311627b31ede9ab149045db4d6088b3becaea046462" +checksum = "c42f3f41a2de00b01c0aaad383c5a45241efc8b2d1eda5661812fda5f3cdcff5" dependencies = [ "proc-macro2", "quote", @@ -671,22 +831,22 @@ dependencies = [ [[package]] name = "thiserror" -version = "1.0.62" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2675633b1499176c2dff06b0856a27976a8f9d436737b4cf4f312d4d91d8bbb" +checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" dependencies = [ "thiserror-impl", ] [[package]] name = "thiserror-impl" -version = "1.0.62" +version = "1.0.61" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20468752b09f49e909e55a5d338caa8bedf615594e9d80bc4c565d30faf798c" +checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" dependencies = [ "proc-macro2", "quote", - "syn 2.0.71", + "syn 2.0.66", ] [[package]] @@ -735,6 +895,12 @@ version = "1.0.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +[[package]] +name = "utf8parse" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" + [[package]] name = "vcpkg" version = "0.2.15" @@ -795,9 +961,9 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ "windows_aarch64_gnullvm", "windows_aarch64_msvc", @@ -811,48 +977,48 @@ dependencies = [ [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" [[package]] name = "windows_aarch64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" [[package]] name = "windows_i686_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" [[package]] name = "windows_i686_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" [[package]] name = "windows_i686_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" [[package]] name = "windows_x86_64_gnu" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" [[package]] name = "windows_x86_64_msvc" -version = "0.52.6" +version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 93ca96dd4..4c1e73dcf 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -15,15 +15,30 @@ log = "0.4.0" env_logger = "0.8.4" iconv = "0.1.1" palette = "0.6.0" -rsmpeg = { version = "0.14.2", optional = true, features = [ - "link_system_ffmpeg", +rsmpeg = { version = "0.14.1", optional = true, features = [ + "link_system_ffmpeg", ] } tesseract-sys = { version = "0.5.14", optional = true, default-features = false } -leptonica-sys = { version = "0.4.3", optional = true, default-features = false } +leptonica-sys = { version = "= 0.4.6", optional = true, default-features = false } +clap = { version = "4.4.4", features = ["derive"] } +strum = "0.25" +strum_macros = "0.25" +time = "0.3.25" +cfg-if = "1.0.0" +num-integer = "0.1.45" lib_ccxr = { path = "lib_ccxr" } [build-dependencies] bindgen = "0.64.0" +pkg-config = "0.3.30" [features] +enable_sharing = [] +wtv_debug = [] +enable_ffmpeg = [] +with_libcurl = [] hardsubx_ocr = ["rsmpeg", "tesseract-sys", "leptonica-sys"] + +[profile.release-with-debug] +inherits = "release" +debug = true diff --git a/src/rust/build.rs b/src/rust/build.rs index 6c860c5c1..8d04bfdf4 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -1,3 +1,4 @@ +extern crate pkg_config; use std::env; use std::path::PathBuf; @@ -9,6 +10,8 @@ fn main() { "get_fts", "printdata", "writercwtdata", + "version", + "set_binary_mode", ]); #[cfg(feature = "hardsubx_ocr")] @@ -27,6 +30,16 @@ fn main() { "cc_subtitle", "ccx_output_format", "ccx_s_options", + "ccx_s_teletext_config", + "ccx_output_format", + "ccx_boundary_time", + "ccx_output_date_format", + "ccx_encoding_type", + "ccx_output_date_format", + "ccx_decoder_608_settings", + "ccx_decoder_608_report", + "ccx_output_format", + "uint8_t", ]); #[cfg(feature = "hardsubx_ocr")] diff --git a/src/rust/lib_ccxr/Cargo.toml b/src/rust/lib_ccxr/Cargo.toml index 8bd21e57b..a54d59b6a 100644 --- a/src/rust/lib_ccxr/Cargo.toml +++ b/src/rust/lib_ccxr/Cargo.toml @@ -13,11 +13,7 @@ derive_more = "0.99.18" [features] default = [ - "enable_sharing", - "wtv_debug", "enable_ffmpeg", - "debug", - "with_libcurl", ] enable_sharing = [] wtv_debug = [] diff --git a/src/rust/src/activity.rs b/src/rust/src/activity.rs new file mode 100644 index 000000000..46266fbad --- /dev/null +++ b/src/rust/src/activity.rs @@ -0,0 +1,15 @@ +use std::io; +use std::io::Write; + +use crate::common::CcxOptions; + +impl CcxOptions { + pub fn activity_report_version(&mut self) { + if self.gui_mode_reports { + let mut stderr = io::stderr(); + let version = env!("CARGO_PKG_VERSION"); + writeln!(stderr, "###VERSION#CCExtractor#{}", version).unwrap(); + stderr.flush().unwrap(); + } + } +} diff --git a/src/rust/src/args.rs b/src/rust/src/args.rs new file mode 100644 index 000000000..ec71b6d6c --- /dev/null +++ b/src/rust/src/args.rs @@ -0,0 +1,1012 @@ +use cfg_if::cfg_if; +use clap::{Parser, ValueEnum}; +use strum_macros::Display; + +const FILE_NAME_RELATED_OPTIONS: &str = "File name related options"; +const OUTPUT_FILE_SEGMENTATION: &str = "Output File Segmentation"; +const NETWORK_SUPPORT: &str = "Network support"; +const OPTION_AFFECT_PROCESSED: &str = "Options that affect what will be processed"; +const INPUT_FORMATS: &str = "Input Formats"; +const OUTPUT_FORMATS: &str = "Output Formats"; +const OPTIONS_AFFECTING_INPUT_FILES: &str = "Options that affect how input files will be processed"; +const LEVENSHTEIN_DISTANCE: &str = "Levenshtein distance"; +const OUTPUT_AFFECTING_OUTPUT_FILES: &str = + "Options that affect what kind of output will be produced"; +const OUTPUT_AFFECTING_BUFFERING: &str = + "Options that affect how ccextractor reads and writes (buffering)"; +const OUTPUT_AFFECTING_TIMING: &str = "Options that affect timing"; +const OUTPUT_AFFECTING_SEGMENT: &str = + "Options that affect what segment of the input file(s) to process"; +const OUTPUT_AFFECTING_CODEC: &str = + "Options that affect which codec is to be used have to be searched in input"; +const ADDING_CREDITS: &str = "Adding start and end credits"; +const OUTPUT_AFFECTING_DEBUG_DATA: &str = "Options that affect debug data"; +const TELETEXT_OPTIONS: &str = "Teletext related options"; +const TRANSCRIPT_OPTIONS: &str = "Transcript customizing options"; +const COMMUNICATION_PROTOCOL: &str = "Communication with other programs and console output"; +const BURNEDIN_SUBTITLE_EXTRACTION: &str = "Burned-in subtitle extraction"; + +cfg_if! { + if #[cfg(feature = "enable_sharing")] { + const SHARING_EXTRACTED_CAPTIONS: &str = "Sharing extracted captions via TCP"; + const CCTRANSLATE_INTEGRATION: &str = "CCTranslate application integration"; + } +} + +#[derive(Debug, Parser)] +#[command(name = "CCExtractor")] +#[command(author = "Carlos Fernandez Sanz, Volker Quetschke.")] +#[command(version = "1.0")] +#[command(about = "Teletext portions taken from Petr Kutalek's telxcc +-------------------------------------------------------------------------- +Originally based on McPoodle's tools. Check his page for lots of information +on closed captions technical details. +(http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML) + +This tool home page: +http://www.ccextractor.org + Extracts closed captions and teletext subtitles from video streams. + (DVB, .TS, ReplayTV 4000 and 5000, dvr-ms, bttv, Tivo, Dish Network, + .mp4, HDHomeRun are known to work). + + Syntax: + ccextractor [options] inputfile1 [inputfile2...] [-o outputfilename] +")] +#[command( + help_template = "{name} {version}, {author}.\n{about}\n {all-args} {tab}\n +An example command for burned-in subtitle extraction is as follows: +ccextractor video.mp4 --hardsubx --subcolor white --detect_italics --whiteness_thresh 90 --conf_thresh 60 + +Notes on File name related options: + You can pass as many input files as you need. They will be processed in order. + If a file name is suffixed by +, ccextractor will try to follow a numerical + sequence. For example, DVD001.VOB+ means DVD001.VOB, DVD002.VOB and so on + until there are no more files. + Output will be one single file (either raw or srt). Use this if you made your + recording in several cuts (to skip commercials for example) but you want one + subtitle file with contiguous timing. + +Notes on Options that affect what will be processed: + In general, if you want English subtitles you don't need to use these options + as they are broadcast in field 1, channel 1. If you want the second language + (usually Spanish) you may need to try -2, or -cc2, or both. + +Notes on Levenshtein distance: + When processing teletext files CCExtractor tries to correct typos by + comparing consecutive lines. If line N+1 is almost identical to line N except + for minor changes (plus next characters) then it assumes that line N that a + typo that was corrected in N+1. This is currently implemented in teletext + because it's where samples files that could benefit from this were available. + You can adjust, or disable, the algorithm settings with the following + parameters. + +Notes on times: + --startat and --endat times are used first, then -delay. + So if you use --srt -startat 3:00 --endat 5:00 --delay 120000, ccextractor will + generate a .srt file, with only data from 3:00 to 5:00 in the input file(s) + and then add that (huge) delay, which would make the final file start at + 5:00 and end at 7:00. + +Notes on codec options: + If codec type is not selected then first elementary stream suitable for + subtitle is selected, please consider --teletext -noteletext override this + option. + no-codec and codec parameter must not be same if found to be same + then parameter of no-codec is ignored, this flag should be passed + once, more then one are not supported yet and last parameter would + taken in consideration + +Notes on adding credits: + CCExtractor can _try_ to add a custom message (for credits for example) at + the start and end of the file, looking for a window where there are no + captions. If there is no such window, then no text will be added. + The start window must be between the times given and must have enough time + to display the message for at least the specified time. + +Notes on the CEA-708 decoder: + While it is starting to be useful, it's + a work in progress. A number of things don't work yet in the decoder + itself, and many of the auxiliary tools (case conversion to name one) + won't do anything yet. Feel free to submit samples that cause problems + and feature requests. + +Notes on spupng output format: + One .xml file is created per output field. A set of .png files are created in + a directory with the same base name as the corresponding .xml file(s), but with + a .d extension. Each .png file will contain an image representing one caption + and named subNNNN.png, starting with sub0000.png. + For example, the command: + ccextractor --out=spupng input.mpg + will create the files: + input.xml + input.d/sub0000.png + input.d/sub0001.png + ... + The command: + ccextractor --out=spupng -o /tmp/output --12 input.mpg + will create the files: + /tmp/output_1.xml + /tmp/output_1.d/sub0000.png + /tmp/output_1.d/sub0001.png + ... + /tmp/output_2.xml + /tmp/output_2.d/sub0000.png + /tmp/output_2.d/sub0001.png + ..." +)] +#[command(arg_required_else_help = true)] +pub struct Args { + /// file(s) to process + #[arg(value_name = "inputfile")] + pub inputfile: Option>, + /// Use -o parameters to define output filename if you don't + /// like the default ones (same as infile plus _1 or _2 when + /// needed and file extension, e.g. .srt). + #[arg(short, value_name="outputfilename", verbatim_doc_comment, help_heading=FILE_NAME_RELATED_OPTIONS)] + pub output: Option, + /// Write output to stdout (console) instead of file. If + /// stdout is used, then -o can't be used. Also + /// --stdout will redirect all messages to stderr (error). + #[arg(long, verbatim_doc_comment, help_heading=FILE_NAME_RELATED_OPTIONS)] + pub stdout: bool, + /// Dump the PES Header to stdout (console). This is + /// used for debugging purposes to see the contents + /// of each PES packet header. + #[arg(long, verbatim_doc_comment, help_heading=FILE_NAME_RELATED_OPTIONS)] + pub pesheader: bool, + /// Write the DVB subtitle debug traces to console. + #[arg(long, help_heading=FILE_NAME_RELATED_OPTIONS)] + pub debugdvdsub: bool, + /// Ignore PTS jumps (default). + #[arg(long, help_heading=FILE_NAME_RELATED_OPTIONS)] + pub ignoreptsjumps: bool, + /// fix pts jumps. Use this parameter if you + /// experience timeline resets/jumps in the output. + #[arg(long, verbatim_doc_comment, conflicts_with="ignoreptsjumps", help_heading=FILE_NAME_RELATED_OPTIONS)] + pub fixptsjumps: bool, + /// Reads input from stdin (console) instead of file. + /// Alternatively, - can be used instead of --stdin + #[arg(long, verbatim_doc_comment, help_heading=FILE_NAME_RELATED_OPTIONS)] + pub stdin: bool, + #[arg(long, value_name="x", help_heading=OUTPUT_FILE_SEGMENTATION)] + pub outinterval: Option, + /// When segmenting files, do it only after a I frame + /// trying to behave like FFmpeg + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_FILE_SEGMENTATION)] + pub segmentonkeyonly: bool, + /// Read the input via UDP (listening in the specified port) + /// instead of reading a file. + /// Host can be a + /// hostname or IPv4 address. If host is not specified + /// then listens on the local host. + #[arg(long, value_name="[host:]port", verbatim_doc_comment, help_heading=NETWORK_SUPPORT)] + pub udp: Option, + /// Can be a hostname or IPv4 address. + #[arg(long, value_name="port", verbatim_doc_comment, help_heading=NETWORK_SUPPORT)] + pub src: Option, + /// Sends data in BIN format to the server + /// according to the CCExtractor's protocol over + /// TCP. For IPv6 use [address] instead + #[arg(long, value_name="port", verbatim_doc_comment, help_heading=NETWORK_SUPPORT)] + pub sendto: Option, + /// Specfies optional port for sendto + #[arg(long, value_name="port", verbatim_doc_comment, help_heading=NETWORK_SUPPORT)] + pub sendto_port: Option, + /// Reads the input da`ta in BIN format according to + /// CCExtractor's protocol, listening specified port on the + /// local host + #[arg(long, value_name="port", verbatim_doc_comment, help_heading=NETWORK_SUPPORT)] + pub tcp: Option, + /// Sets server password for new connections to + /// tcp server + #[arg(long, value_name="port", verbatim_doc_comment, help_heading=NETWORK_SUPPORT)] + pub tcp_password: Option, + /// Sends to the server short description about + /// captions e.g. channel name or file name + #[arg(long, value_name="port", verbatim_doc_comment, help_heading=NETWORK_SUPPORT)] + pub tcp_description: Option, + /// Output field1 data, field2 data, or both + #[arg(long, value_name="1/2/both", verbatim_doc_comment, help_heading=OPTION_AFFECT_PROCESSED)] + pub output_field: Option, + /// Use --append to prevent overwriting of existing files. The output will be + /// appended instead. + #[arg(long, verbatim_doc_comment, help_heading=OPTION_AFFECT_PROCESSED)] + pub append: bool, + /// When in srt/sami mode, process captions in channel 2 + /// instead of channel 1. + #[arg(long, verbatim_doc_comment, help_heading=OPTION_AFFECT_PROCESSED)] + pub cc2: bool, + /// Enable CEA-708 (DTVCC) captions processing for the listed + /// services. The parameter is a comma delimited list + /// of services numbers, such as "1,2" to process the + /// primary and secondary language services. + /// Pass "all" to process all services found. + /// If captions in a service are stored in 16-bit encoding, + /// you can specify what charset or encoding was used. Pass + /// its name after service number (e.g. "1[EUC-KR],3" or + /// "all[EUC-KR]") and it will encode specified charset to + /// UTF-8 using iconv. See iconv documentation to check if + /// required encoding/charset is supported. + #[arg(long="service", value_name="services", verbatim_doc_comment, help_heading=OPTION_AFFECT_PROCESSED)] + pub cea708services: Option, + /// With the exception of McPoodle's raw format, which is just the closed + /// caption data with no other info, CCExtractor can usually detect the + /// input format correctly. Use this parameter to override the detected + #[arg(long, alias="in", value_name="format",verbatim_doc_comment, help_heading=INPUT_FORMATS)] + pub input: Option, + #[arg(long, hide = true)] + pub es: bool, + #[arg(long, hide = true)] + pub ts: bool, + #[arg(long, hide = true)] + pub ps: bool, + #[arg(long, hide = true)] + pub asf: bool, + #[arg(long, hide = true)] + pub wtv: bool, + #[arg(long, hide = true)] + pub mp4: bool, + #[arg(long, hide = true)] + pub mkv: bool, + #[arg(long, hide = true)] + pub dvr_ms: bool, + #[arg(long, value_name="format", help_heading=OUTPUT_FORMATS)] + pub out: Option, + #[arg(long, hide = true)] + pub srt: bool, + #[arg(long, hide = true)] + pub webvtt: bool, + #[arg(long, hide = true)] + pub sami: bool, + #[arg(long, hide = true)] + pub smi: bool, + #[arg(long, hide = true)] + pub dvdraw: bool, + #[arg(long, hide = true)] + pub mcc: bool, + #[arg(long, hide = true)] + pub txt: bool, + #[arg(long, hide = true)] + pub ttxt: bool, + #[arg(long, hide = true)] + pub null: bool, + /// Use GOP for timing instead of PTS. This only applies + /// to Program or Transport Streams with MPEG2 data and + /// overrides the default PTS timing. + /// GOP timing is always used for Elementary Streams. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub goptime: bool, + /// Never use GOP timing (use PTS), even if ccextractor + /// detects GOP timing is the reasonable choice. + #[arg(long, verbatim_doc_comment, conflicts_with="goptime", help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub no_goptime: bool, + /// Fix padding - some cards (or providers, or whatever) + /// seem to send 0000 as CC padding instead of 8080. If you + /// get bad timing, this might solve it. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub fixpadding: bool, + /// Use 90090 (instead of 90000) as MPEG clock frequency. + /// (reported to be needed at least by Panasonic DMR-ES15 + /// DVD Recorder) + #[arg(long="90090", verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub mpeg90090: bool, + /// By default, ccextractor will process input files in + /// sequence as if they were all one large file (i.e. + /// split by a generic, non video-aware tool. If you + /// are processing video hat was split with a editing + /// tool, use --videoedited so ccextractor doesn't try to rebuild + /// the original timing. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub videoedited: bool, + /// Consider the file as a continuous stream that is + /// growing as ccextractor processes it, so don't try + /// to figure out its size and don't terminate processing + /// when reaching the current end (i.e. wait for more + /// data to arrive). If the optional parameter secs is + /// present, it means the number of seconds without any + /// new data after which ccextractor should exit. Use + /// this parameter if you want to process a live stream + /// but not kill ccextractor externally. + /// Note: If --s is used then only one input file is + /// allowed. + #[arg(short, long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub stream: Option, + /// Use the pic_order_cnt_lsb in AVC/H.264 data streams + /// to order the CC information. The default way is to + /// use the PTS information. Use this switch only when + /// needed. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub usepicorder: bool, + /// Force MythTV code branch. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub myth: bool, + /// Disable MythTV code branch. + /// The MythTV branch is needed for analog captures where + /// the closed caption data is stored in the VBI, such as + /// those with bttv cards (Hauppage 250 for example). This + /// is detected automatically so you don't need to worry + /// about this unless autodetection doesn't work for you. + #[arg(long, verbatim_doc_comment, conflicts_with="myth", help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub no_myth: bool, + /// This switch works around a bug in Windows 7's built in + /// software to convert *.wtv to *.dvr-ms. For analog NTSC + /// recordings the CC information is marked as digital + /// captions. Use this switch only when needed. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub wtvconvertfix: bool, + /// Read the captions from the MPEG2 video stream rather + /// than the captions stream in WTV files + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub wtvmpeg2: bool, + /// In TS mode, specifically select a program to process. + /// Not needed if the TS only has one. If this parameter + /// is not specified and CCExtractor detects more than one + /// program in the input, it will list the programs found + /// and terminate without doing anything, unless + /// --autoprogram (see below) is used. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub program_number: Option, + /// If there's more than one program in the stream, just use + /// the first one we find that contains a suitable stream. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub autoprogram: bool, + /// Uses multiple programs from the same input stream. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub multiprogram: bool, + /// Don't try to find out the stream for caption/teletext + /// data, just use this one instead. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub datapid: Option, + /// Instead of selecting the stream by its PID, select it + /// by its type (pick the stream that has this type in + /// the PMT) + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub datastreamtype: Option, + /// Assume the data is of this type, don't autodetect. This + /// parameter may be needed if --datapid or --datastreamtype + /// is used and CCExtractor cannot determine how to process + /// the stream. The value will usually be 2 (MPEG video) or + /// 6 (MPEG private data). + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub streamtype: Option, + /// If the video was recorder using a Hauppauge card, it + /// might need special processing. This parameter will + /// force the special treatment. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub hauppauge: bool, + /// In MP4 files the closed caption data can be embedded in + /// the video track or in a dedicated CC track. If a + /// dedicated track is detected it will be processed instead + /// of the video track. If you need to force the video track + /// to be processed instead use this option. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub mp4vidtrack: bool, + /// Some streams come with broadcast date information. When + /// such data is available, CCExtractor will set its time + /// reference to the received data. Use this parameter if + /// you prefer your own reference. Note: Current this only + /// affects Teletext in timed transcript with --datets. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub no_autotimeref: bool, + /// Ignore SCTE-20 data if present. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub no_scte20: bool, + /// Create a separate file for CSS instead of inline. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub webvtt_create_css: bool, + /// Enable debug so the calculated distance for each two + /// strings is displayed. The output includes both strings, + /// the calculated distance, the maximum allowed distance, + /// and whether the strings are ultimately considered + /// equivalent or not, i.e. the calculated distance is + /// less or equal than the max allowed. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub deblev: bool, + /// Analyze the video stream even if it's not used for + /// subtitles. This allows to provide video information. + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub analyzevideo: bool, + /// Enable the X-TIMESTAMP-MAP header for WebVTT (HLS) + #[arg(long, verbatim_doc_comment, help_heading=OPTIONS_AFFECTING_INPUT_FILES)] + pub timestamp_map: bool, + /// Don't attempt to correct typos with Levenshtein distance. + #[arg(long, verbatim_doc_comment, help_heading=LEVENSHTEIN_DISTANCE)] + pub no_levdist: bool, + /// Minimum distance we always allow regardless + /// of the length of the strings.Default 2. + /// This means that if the calculated distance + /// is 0,1 or 2, we consider the strings to be equivalent. + #[arg(long, value_name="value", verbatim_doc_comment, help_heading=LEVENSHTEIN_DISTANCE)] + pub levdistmincnt: Option, + /// Maximum distance we allow, as a percentage of + /// the shortest string length. Default 10%.0 + /// For example, consider a comparison of one string of + /// 30 characters and one of 60 characters. We want to + /// determine whether the first 30 characters of the longer + /// string are more or less the same as the shortest string, + /// i.e. whether the longest string is the shortest one + /// plus new characters and maybe some corrections. Since + /// the shortest string is 30 characters and the default + /// percentage is 10%, we would allow a distance of up + /// to 3 between the first 30 characters. + #[arg(long, value_name="value", verbatim_doc_comment, help_heading=LEVENSHTEIN_DISTANCE)] + pub levdistmaxpct: Option, + /// (Experimental) Produces a chapter file from MP4 files. + /// Note that this must only be used with MP4 files, + /// for other files it will simply generate subtitles file. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub chapters: bool, + /// Append a BOM (Byte Order Mark) to output files. + /// Note that most text processing tools in linux will not + /// like BOM. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub bom: bool, + /// Do not append a BOM (Byte Order Mark) to output + /// files. Note that this may break files when using + /// Windows. This is the default in non-Windows builds. + #[arg(long, verbatim_doc_comment, conflicts_with="bom", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub no_bom: bool, + /// Encode subtitles in Unicode instead of Latin-1. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub unicode: bool, + /// Encode subtitles in UTF-8 (no longer needed. + /// because UTF-8 is now the default). + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub utf8: bool, + /// Encode subtitles in Latin-1 + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub latin1: bool, + /// For .srt/.sami/.vtt, don't add font color tags. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub no_fontcolor: bool, + /// For .srt/.sami/.vtt, don't covert html unsafe character + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub no_htmlescape: bool, + /// For .srt/.sami/.vtt, don't add typesetting tags. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub no_typesetting: bool, + /// Trim lines. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub trim: bool, + /// Select a different default color (instead of + /// white). This causes all output in .srt/.smi/.vtt + /// files to have a font tag, which makes the files + /// larger. Add the color you want in RGB, such as + /// --dc #FF0000 for red. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub defaultcolor: Option, + /// Sentence capitalization. Use if you hate + /// ALL CAPS in subtitles. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub sentencecap: bool, + /// Add the contents of 'file' to the list of words + /// that must be capitalized. For example, if file + /// is a plain text file that contains + /// + /// Tony + /// Alan + /// + /// Whenever those words are found they will be written + /// exactly as they appear in the file. + /// Use one line per word. Lines starting with # are + /// considered comments and discarded. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub capfile: Option, + /// Censors profane words from subtitles. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub kf: bool, + /// Add the contents of to the list of words that. + /// must be censored. The content of , follows the + /// same syntax as for the capitalization file + #[arg(long, verbatim_doc_comment, value_name="file", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub profanity_file: Option, + /// Split output text so each frame contains a complete + /// sentence. Timings are adjusted based on number of + /// characters + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub splitbysentence: bool, + /// For timed transcripts that have an absolute date + /// instead of a timestamp relative to the file start), use + /// this time reference (UNIX timestamp). 0 => Use current + /// system time. + /// ccextractor will automatically switch to transport + /// stream UTC timestamps when available. + #[arg(long, verbatim_doc_comment, value_name="REF", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub unixts: Option, + /// In transcripts, write time as YYYYMMDDHHMMss,ms. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub datets: bool, + /// In transcripts, write time as ss,ms + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub sects: bool, + /// Transcripts are generated with a specific format + /// that is convenient for a specific project, feel + /// free to play with it but be aware that this format + /// is really live - don't rely on its output format + /// not changing between versions. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub ucla: bool, + /// Map Latin symbols to Cyrillic ones in special cases + /// of Russian Teletext files (issue #1086) + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub latrusmap: bool, + /// In timed transcripts, all XDS information will be saved + /// to the output file. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub xds: bool, + /// Use LF (UNIX) instead of CRLF (DOS, Windows) as line + /// terminator. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub lf: bool, + /// For MCC Files, force dropframe frame count. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub df: bool, + /// Based on position on screen, attempt to determine + /// the different speakers and a dash (-) when each + /// of them talks (.srt/.vtt only, --trim required). + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub autodash: bool, + /// produce an XMLTV file containing the EPG data from + /// the source TS file. Mode: 1 = full output + /// 2 = live output. 3 = both + #[arg(long, verbatim_doc_comment, value_name="mode", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub xmltv: Option, + /// interval of x seconds between writing live mode xmltv output. + #[arg(long, verbatim_doc_comment, value_name="x", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub xmltvliveinterval: Option, + /// interval of x seconds between writing full file xmltv output. + #[arg(long, verbatim_doc_comment, value_name="x", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub xmltvoutputinterval: Option, + /// Only print current events for xmltv output. + #[arg(long, verbatim_doc_comment, value_name="x", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub xmltvonlycurrent: Option, + /// Create a .sem file for each output file that is open + /// and delete it on file close. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub sem: bool, + /// For DVB subtitles, select which language's caption + /// stream will be processed. e.g. 'eng' for English. + /// If there are multiple languages, only this specified + /// language stream will be processed (default). + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub dvblang: Option, + /// Manually select the name of the Tesseract .traineddata + /// file. Helpful if you want to OCR a caption stream of + /// one language with the data of another language. + /// e.g. '-dvblang chs --ocrlang chi_tra' will decode the + /// Chinese (Simplified) caption stream but perform OCR + /// using the Chinese (Traditional) trained data + /// This option is also helpful when the traineddata file + /// has non standard names that don't follow ISO specs + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub ocrlang: Option, + /// How to quantize the bitmap before passing it to tesseract + /// for OCR'ing. + /// 0: Don't quantize at all. + /// 1: Use CCExtractor's internal function (default). + /// 2: Reduce distinct color count in image for faster results. + #[arg(long, verbatim_doc_comment, value_name="mode", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub quant: Option, + /// Select the OEM mode for Tesseract. + /// Available modes : + /// 0: OEM_TESSERACT_ONLY - the fastest mode. + /// 1: OEM_LSTM_ONLY - use LSTM algorithm for recognition. + /// 2: OEM_TESSERACT_LSTM_COMBINED - both algorithms. + /// Default value depends on the tesseract version linked : + /// Tesseract v3 : default mode is 0, + /// Tesseract v4 : default mode is 1. + #[arg(long, verbatim_doc_comment, value_name="mode", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub oem: Option, + /// For MKV subtitles, select which language's caption + /// stream will be processed. e.g. 'eng' for English. + /// Language codes can be either the 3 letters bibliographic + /// ISO-639-2 form (like "fre" for french) or a language + /// code followed by a dash and a country code for specialities + /// in languages (like "fre-ca" for Canadian French). + #[arg(long, verbatim_doc_comment, value_name="lang", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub mkvlang: Option, + /// When processing DVB don't use the OCR to write the text as + /// comments in the XML file. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub no_spupngocr: bool, + /// Specify the full path of the font that is to be used when + /// generating SPUPNG files. If not specified, you need to + /// have the default font installed (Helvetica for macOS, Calibri + /// for Windows, and Noto for other operating systems at their + /// default location) + #[arg(long, verbatim_doc_comment, value_name="path", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub font: Option, + /// Specify the full path of the italics font that is to be used when + /// generating SPUPNG files. If not specified, you need to + /// have the default font installed (Helvetica Oblique for macOS, Calibri Italic + /// for Windows, and NotoSans Italic for other operating systems at their + /// default location) + #[arg(long, verbatim_doc_comment, value_name="path", help_heading=OUTPUT_AFFECTING_OUTPUT_FILES)] + pub italics: Option, + /// Forces input buffering. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub bufferinput: bool, + /// Disables input buffering. + #[arg(long, verbatim_doc_comment, conflicts_with="bufferinput", help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub no_bufferinput: bool, + /// Specify a size for reading, in bytes (suffix with K or + /// or M for kilobytes and megabytes). Default is 16M. + #[arg(long, verbatim_doc_comment, value_name="val", help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub buffersize: Option, + /// keep-output-close. If used then CCExtractor will close + /// the output file after writing each subtitle frame and + /// attempt to create it again when needed. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub koc: bool, + /// Flush the file buffer whenever content is written. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub forceflush: bool, + /// Direct Roll-Up. When in roll-up mode, write character by + /// character instead of line by line. Note that this + /// produces (much) larger files. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub dru: bool, + /// If you hate the repeated lines caused by the roll-up + /// emulation, you can have ccextractor write only one + /// line at a time, getting rid of these repeated lines. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub no_rollup: bool, + /// roll-up captions can consist of 2, 3 or 4 visible + /// lines at any time (the number of lines is part of + /// the transmission). If having 3 or 4 lines annoys + /// you you can use --ru to force the decoder to always + /// use 1, 2 or 3 lines. Note that 1 line is not + /// a real mode rollup mode, so CCExtractor does what + /// it can. + /// In --ru1 the start timestamp is actually the timestamp + /// of the first character received which is possibly more + /// accurate. + #[arg(long="ru1", conflicts_with="rollup2", verbatim_doc_comment, value_name="type", help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub rollup1: bool, + #[arg(long="ru2", conflicts_with="rollup3", verbatim_doc_comment, value_name="type", help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub rollup2: bool, + #[arg(long="ru3", conflicts_with="rollup1", verbatim_doc_comment, value_name="type", help_heading=OUTPUT_AFFECTING_BUFFERING)] + pub rollup3: bool, + /// For srt/sami/webvtt, add this number of milliseconds to + /// all times. For example, --delay 400 makes subtitles + /// appear 400ms late. You can also use negative numbers + /// to make subs appear early. + #[arg(long, verbatim_doc_comment, value_name="ms", help_heading=OUTPUT_AFFECTING_TIMING)] + pub delay: Option, + /// Only write caption information that starts after the + /// given time. + /// Time can be seconds, MM:SS or HH:MM:SS. + /// For example, --startat 3:00 means 'start writing from + /// minute 3. + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=OUTPUT_AFFECTING_SEGMENT)] + pub startat: Option, + /// Stop processing after the given time (same format as + /// --startat). + /// The --startat and --endat options are honored in all + /// output formats. In all formats with timing information + /// the times are unchanged. + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=OUTPUT_AFFECTING_SEGMENT)] + pub endat: Option, + /// Write 'num' screenfuls and terminate processing. + #[arg(long, verbatim_doc_comment, value_name="num", help_heading=OUTPUT_AFFECTING_SEGMENT)] + pub screenfuls: Option, + /// --codec dvbsub + /// select the dvb subtitle from all elementary stream, + /// if stream of dvb subtitle type is not found then + /// nothing is selected and no subtitle is generated + /// --codec teletext + /// select the teletext subtitle from elementary stream + #[arg(long, verbatim_doc_comment, value_name="value", help_heading=OUTPUT_AFFECTING_CODEC)] + pub codec: Option, + /// --no-codec dvbsub + /// ignore dvb subtitle and follow default behaviour + /// --no-codec teletext + /// ignore teletext subtitle + #[arg(long, verbatim_doc_comment, conflicts_with="codec", value_name="value", help_heading=OUTPUT_AFFECTING_CODEC)] + pub no_codec: Option, + /// Write this text as start credits. If there are + /// several lines, separate them with the + /// characters \n, for example Line1\nLine 2. + #[arg(long, verbatim_doc_comment, value_name="text", help_heading=ADDING_CREDITS)] + pub startcreditstext: Option, + /// Don't display the start credits before this + /// time (S, or MM:SS). Default: 0 + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=ADDING_CREDITS)] + pub startcreditsnotbefore: Option, + /// Don't display the start credits after this + /// time (S, or MM:SS). Default: 5:00 + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=ADDING_CREDITS)] + pub startcreditsnotafter: Option, + /// Start credits need to be displayed for at least + /// this time (S, or MM:SS). Default: 2 + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=ADDING_CREDITS)] + pub startcreditsforatleast: Option, + /// Start credits should be displayed for at most + /// this time (S, or MM:SS). Default: 5 + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=ADDING_CREDITS)] + pub startcreditsforatmost: Option, + /// Write this text as end credits. If there are + /// several lines, separate them with the + /// characters \n, for example Line1\nLine 2. + #[arg(long, verbatim_doc_comment, value_name="txt", help_heading=ADDING_CREDITS)] + pub endcreditstext: Option, + /// End credits need to be displayed for at least + /// this time (S, or MM:SS). Default: 2 + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=ADDING_CREDITS)] + pub endcreditsforatleast: Option, + /// End credits should be displayed for at most + /// this time (S, or MM:SS). Default: 5 + #[arg(long, verbatim_doc_comment, value_name="time", help_heading=ADDING_CREDITS)] + pub endcreditsforatmost: Option, + /// Show lots of debugging output. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub debug: bool, + /// Print debug traces from the EIA-608 decoder. + /// If you need to submit a bug report, please send + /// the output from this option. + #[arg(long="608", verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub eia608: bool, + /// Print debug information from the (currently + /// in development) EIA-708 (DTV) decoder. + #[arg(long="708", verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub eia708: bool, + /// Enable lots of time stamp output. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub goppts: bool, + /// Enable XDS debug data (lots of it). + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub xdsdebug: bool, + /// Print debug info about the analysed elementary + /// video stream. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub vides: bool, + /// Print debug trace with the raw 608/708 data with + /// time stamps. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub cbraw: bool, + /// Disable the syncing code. Only useful for debugging + /// purposes. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub no_sync: bool, + /// Disable the removal of trailing padding blocks + /// when exporting to bin format. Only useful for + /// for debugging purposes. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub fullbin: bool, + /// Print debug info about the parsed container + /// file. (Only for TS/ASF files at the moment.) + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub parsedebug: bool, + /// Print Program Association Table dump. + #[arg(long="parsePAT", verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub parse_pat: bool, + /// Print Program Map Table dump. + #[arg(long="parsePMT", verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub parse_pmt: bool, + /// Hex-dump defective TS packets. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub dumpdef: bool, + /// If no CC packets are detected based on the PMT, try + /// to find data in all packets by scanning. + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub investigate_packets: bool, + #[cfg(feature = "enable_sharing")] + /// Print extracted CC sharing service messages + #[arg(long, verbatim_doc_comment, help_heading=OUTPUT_AFFECTING_DEBUG_DATA)] + pub sharing_debug: bool, + /// Use this page for subtitles (if this parameter + /// is not used, try to autodetect). In Spain the + /// page is always 888, may vary in other countries. + #[arg(long, verbatim_doc_comment, value_name="page", help_heading=TELETEXT_OPTIONS)] + pub tpage: Option, + /// Enable verbose mode in the teletext decoder. + #[arg(long, verbatim_doc_comment, help_heading=TELETEXT_OPTIONS)] + pub tverbose: bool, + /// Force teletext mode even if teletext is not detected. + /// If used, you should also pass --datapid to specify + /// the stream ID you want to process. + #[arg(long, verbatim_doc_comment, help_heading=TELETEXT_OPTIONS)] + pub teletext: bool, + /// Disable teletext processing. This might be needed + /// for video streams that have both teletext packets + /// and CEA-608/708 packets (if teletext is processed + /// then CEA-608/708 processing is disabled). + #[arg(long, verbatim_doc_comment, conflicts_with="teletext", help_heading=TELETEXT_OPTIONS)] + pub no_teletext: bool, + /// Use the passed format to customize the (Timed) Transcript + /// output. The format must be like this: 1100100 (7 digits). + /// These indicate whether the next things should be + /// displayed or not in the (timed) transcript. They + /// represent (in order): + /// - Display start time + /// - Display end time + /// - Display caption mode + /// - Display caption channel + /// - Use a relative timestamp ( relative to the sample) + /// - Display XDS info + /// - Use colors + /// Examples: + /// 0000101 is the default setting for transcripts + /// 1110101 is the default for timed transcripts + /// 1111001 is the default setting for --ucla + /// Make sure you use this parameter after others that might + /// affect these settings (--out, --ucla, --xds, --txt, + /// --ttxt ...) + #[arg(long, verbatim_doc_comment, value_name="format", help_heading=TRANSCRIPT_OPTIONS)] + pub customtxt: Option, + /// Report progress and interesting events to stderr + /// in a easy to parse format. This is intended to be + /// used by other programs. See docs directory for. + /// details. + #[arg(long, verbatim_doc_comment, help_heading=COMMUNICATION_PROTOCOL)] + pub gui_mode_reports: bool, + /// Suppress the output of the progress bar + #[arg(long, verbatim_doc_comment, help_heading=COMMUNICATION_PROTOCOL)] + pub no_progress_bar: bool, + /// Don't write any message. + #[arg(long, verbatim_doc_comment, help_heading=COMMUNICATION_PROTOCOL)] + pub quiet: bool, + #[cfg(feature = "enable_sharing")] + /// Enables real-time sharing of extracted captions + #[arg(long, verbatim_doc_comment, help_heading=SHARING_EXTRACTED_CAPTIONS)] + pub enable_sharing: bool, + #[cfg(feature = "enable_sharing")] + /// Set url for sharing service in nanomsg format. Default: tcp://*:3269 + #[arg(long, value_name="url", verbatim_doc_comment, help_heading=SHARING_EXTRACTED_CAPTIONS)] + pub sharing_url: Option, + #[cfg(feature = "enable_sharing")] + /// Enables real-time sharing of extracted captions + #[arg(long, value_name="languages", verbatim_doc_comment, help_heading=CCTRANSLATE_INTEGRATION)] + pub translate: Option, + #[cfg(feature = "enable_sharing")] + /// Set Translation Service authorization data to make translation possible + /// In case of Google Translate API - API Key + #[arg(long, verbatim_doc_comment, help_heading=CCTRANSLATE_INTEGRATION)] + pub translate_auth: Option, + /// Enable the burned-in subtitle extraction subsystem. + /// + /// NOTE: This is needed to use the below burned-in + /// subtitle extractor options + #[arg(long, verbatim_doc_comment, help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub hardsubx: bool, + /// Search for burned-in ticker text at the bottom of + /// the screen. + #[arg(long, verbatim_doc_comment, help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub tickertext: bool, + /// Set the OCR mode to either frame-wise, word-wise + /// or letter wise. + /// e.g. --ocr-mode frame (default), --ocr-mode word, + /// --ocr-mode letter + #[arg(long, verbatim_doc_comment, value_name="mode", help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub ocr_mode: Option, + /// Specify the color of the subtitles + /// Possible values are in the set + /// {white,yellow,green,cyan,blue,magenta,red}. + /// Alternatively, a custom hue value between 1 and 360 + /// may also be specified. + /// e.g. --subcolor white or --subcolor 270 (for violet). + /// Refer to an HSV color chart for values. + #[arg(long, verbatim_doc_comment, value_name="color", help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub subcolor: Option, + /// Specify the minimum duration that a subtitle line + /// must exist on the screen. + /// The value is specified in seconds. + /// A lower value gives better results, but takes more + /// processing time. + /// The recommended value is 0.5 (default). + /// e.g. --min_sub_duration 1.0 (for a duration of 1 second) + #[arg(long, verbatim_doc_comment, value_name="duration", help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub min_sub_duration: Option, + /// Specify whether italics are to be detected from the + /// OCR text. + /// Italic detection automatically enforces the OCR mode + /// to be word-wise + #[arg(long, verbatim_doc_comment, help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub detect_italics: bool, + /// Specify the classifier confidence threshold between + /// 1 and 100. + /// Try and use a threshold which works for you if you get + /// a lot of garbage text. + /// e.g. --conf_thresh 50 + #[arg(long, verbatim_doc_comment, help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub conf_thresh: Option, + /// For white subtitles only, specify the luminance + /// threshold between 1 and 100 + /// This threshold is content dependent, and adjusting + /// values may give you better results + /// Recommended values are in the range 80 to 100. + /// The default value is 95 + #[arg(long, verbatim_doc_comment, value_name="threshold", help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub whiteness_thresh: Option, + /// This option will be used if the file should have both + /// closed captions and burned in subtitles + #[arg(long, verbatim_doc_comment, help_heading=BURNEDIN_SUBTITLE_EXTRACTION)] + pub hcc: bool, + #[cfg(feature = "with_libcurl")] + #[arg(long, hide = true)] + pub curlposturl: Option, +} + +#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum Codec { + Dvbsub, + Teletext, +} + +#[derive(Display, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum InFormat { + /// For Transport Streams. + Ts, + /// For Program Streams. + Ps, + /// For Elementary Streams. + Es, + /// ASF container (such as DVR-MS). + Asf, + /// Windows Television (WTV). + Wtv, + /// CCExtractor's own binary format. + Bin, + /// For McPoodle's raw files. + Raw, + /// MP4/MOV/M4V and similar. + Mp4, + /// BDAV MPEG-2 Transport Stream. + M2ts, + /// Matroska container and WebM. + Mkv, + /// Material Exchange Format (MXF). + Mxf, + #[cfg(feature = "wtv_debug")] + // For WTV Debug mode only + Hex, +} + +#[derive(Display, Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, ValueEnum)] +pub enum OutFormat { + /// SubRip (default, so not actually needed). + Srt, + /// SubStation Alpha. + Ass, + /// SubStation Alpha. + Ssa, + /// Scenarist Closed Caption Disassembly format. + Ccd, + /// Scenarist Closed Caption format. + Scc, + /// WebVTT format. + Webvtt, + /// WebVTT format with styling. + WebvttFull, + /// MS Synchronized Accesible Media Interface. + Sami, + /// CC data in CCExtractor's own binary format. + Bin, + /// CC data in McPoodle's Broadcast format. + Raw, + /// CC data in McPoodle's DVD format. + Dvdraw, + /// CC data compressed using MacCaption Format. + Mcc, + /// Transcript (no time codes, no roll-up captions, just the plain transcription). + Txt, + /// Timed Transcript (transcription with time info). + Ttxt, + /// Grid 608 format. + G608, + /// SMPTE Timed Text (W3C TTML) format. + Smptett, + /// Set of .xml and .png files for use with dvdauthor's spumux. + /// See "Notes on spupng output format". + Spupng, + /// Don't produce any file output. + Null, + /// Prints to stdout information about captions in specified input. + /// Don't produce any file output. + Report, + #[cfg(feature = "with_libcurl")] + /// POST plain transcription frame-by-frame to a + /// URL specified by --curlposturl. Don't produce + /// any file output. + Curl, +} diff --git a/src/rust/src/ccx_encoders_helpers.rs b/src/rust/src/ccx_encoders_helpers.rs new file mode 100644 index 000000000..9aca063c2 --- /dev/null +++ b/src/rust/src/ccx_encoders_helpers.rs @@ -0,0 +1,65 @@ +// Some basic English words, so user-defined doesn't have to +// include the common stuff + +pub static mut PROFANE: Vec = Vec::new(); +pub static mut CAPITALIZATION_LIST: Vec = Vec::new(); + +pub const CAPITALIZED_BUILTIN: [&str; 29] = [ + "I", + "I'd", + "I've", + "I'd", + "I'll", + "January", + "February", + "March", + "April", // May skipped intentionally + "June", + "July", + "August", + "September", + "October", + "November", + "December", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday", + "Sunday", + "Halloween", + "United States", + "Spain", + "France", + "Italy", + "England", +]; + +pub const PROFANE_BUILTIN: [&str; 25] = [ + "arse", + "ass", + "asshole", + "bastard", + "bitch", + "bollocks", + "child-fucker", + "crap", + "cunt", + "damn", + "frigger", + "fuck", + "goddamn", + "godsdamn", + "hell", + "holy", + "horseshit", + "motherfucker", + "nigga", + "nigger", + "prick", + "shit", + "shitass", + "slut", + "twat", +]; diff --git a/src/rust/src/common.rs b/src/rust/src/common.rs new file mode 100644 index 000000000..daff71a36 --- /dev/null +++ b/src/rust/src/common.rs @@ -0,0 +1,1316 @@ +use crate::bindings::*; +use crate::utils::string_to_c_char; +use crate::utils::string_to_c_chars; + +#[derive(Debug)] +pub struct CcxTeletextConfig { + pub verbose: bool, + pub page: u16, + pub tid: u16, + pub offset: f64, + pub bom: bool, + pub nonempty: bool, + pub user_page: u16, + pub dolevdist: i32, + pub levdistmincnt: i32, + pub levdistmaxpct: i32, + pub extraction_start: CcxBoundaryTime, + pub extraction_end: CcxBoundaryTime, + pub write_format: CcxOutputFormat, + pub gui_mode_reports: bool, + pub date_format: CcxOutputDateFormat, + pub noautotimeref: bool, + pub send_to_srv: bool, + pub encoding: CcxEncodingType, + pub nofontcolor: bool, + pub nohtmlescape: bool, + pub millis_separator: char, + pub latrusmap: bool, +} + +impl Default for CcxTeletextConfig { + fn default() -> Self { + Self { + verbose: true, + page: 0, + tid: 0, + offset: 0.0, + bom: true, + nonempty: true, + user_page: 0, + dolevdist: 0, + levdistmincnt: 0, + levdistmaxpct: 0, + extraction_start: CcxBoundaryTime::default(), + extraction_end: CcxBoundaryTime::default(), + write_format: CcxOutputFormat::default(), + gui_mode_reports: false, + date_format: CcxOutputDateFormat::default(), + noautotimeref: false, + send_to_srv: false, + encoding: CcxEncodingType::default(), + nofontcolor: false, + nohtmlescape: false, + millis_separator: ',', + latrusmap: false, + } + } +} + +impl CcxTeletextConfig { + pub fn to_ctype(&self) -> ccx_s_teletext_config { + let mut config = ccx_s_teletext_config { + _bitfield_1: Default::default(), + _bitfield_2: Default::default(), + _bitfield_align_1: Default::default(), + _bitfield_align_2: Default::default(), + page: self.page, + tid: self.tid, + offset: self.offset, + user_page: self.user_page, + dolevdist: self.dolevdist, + levdistmincnt: self.levdistmincnt, + levdistmaxpct: self.levdistmaxpct, + extraction_start: self.extraction_start.to_ctype(), + extraction_end: self.extraction_end.to_ctype(), + write_format: self.write_format.into(), + gui_mode_reports: self.gui_mode_reports as _, + date_format: self.date_format as _, + noautotimeref: self.noautotimeref as _, + send_to_srv: self.send_to_srv as _, + encoding: self.encoding as _, + nofontcolor: self.nofontcolor as _, + nohtmlescape: self.nohtmlescape as _, + millis_separator: self.millis_separator as _, + latrusmap: self.latrusmap as _, + }; + config.set_verbose(if self.verbose { 1 } else { 0 }); + config.set_bom(if self.bom { 1 } else { 0 }); + config.set_nonempty(if self.nonempty { 1 } else { 0 }); + + config + } +} + +#[derive(Debug, Default, Clone)] +pub struct CcxBoundaryTime { + pub hh: i32, + pub mm: i32, + pub ss: i32, + pub time_in_ms: i64, + pub set: bool, +} + +impl CcxBoundaryTime { + pub fn start_credits_not_before() -> CcxBoundaryTime { + CcxBoundaryTime { + hh: 0, + mm: 0, + ss: 0, + time_in_ms: 0, + set: true, + } + } + pub fn start_credits_not_after() -> CcxBoundaryTime { + CcxBoundaryTime { + hh: 0, + mm: 5, + ss: 0, + time_in_ms: 300000, + set: true, + } + } + pub fn start_credits_for_atleast() -> CcxBoundaryTime { + CcxBoundaryTime { + hh: 0, + mm: 0, + ss: 2, + time_in_ms: 2000, + set: true, + } + } + pub fn start_credits_for_atmost() -> CcxBoundaryTime { + CcxBoundaryTime { + hh: 0, + mm: 0, + ss: 5, + time_in_ms: 5000, + set: true, + } + } + pub fn end_credits_for_atleast() -> CcxBoundaryTime { + CcxBoundaryTime { + hh: 0, + mm: 0, + ss: 2, + time_in_ms: 2000, + set: true, + } + } + pub fn end_credits_for_atmost() -> CcxBoundaryTime { + CcxBoundaryTime { + hh: 0, + mm: 0, + ss: 5, + time_in_ms: 5000, + set: true, + } + } + + pub fn to_ctype(&self) -> ccx_boundary_time { + ccx_boundary_time { + hh: self.hh, + mm: self.mm, + ss: self.ss, + time_in_ms: self.time_in_ms, + set: self.set as _, + } + } +} + +#[derive(Debug, Default, Copy, Clone)] +pub struct CcxDecoder608Report { + pub xds: bool, + pub cc_channels: [u8; 4], +} + +impl CcxDecoder608Report { + pub fn to_ctype(&self) -> ccx_decoder_608_report { + let mut decoder = ccx_decoder_608_report { + _bitfield_1: Default::default(), + _bitfield_align_1: Default::default(), + cc_channels: self.cc_channels, + }; + decoder.set_xds(if self.xds { 1 } else { 0 }); + decoder + } +} + +#[derive(Debug, Copy, Clone)] +pub struct CcxDecoder608Settings { + pub direct_rollup: i32, + pub force_rollup: i32, + pub no_rollup: bool, + pub default_color: CcxDecoder608ColorCode, + pub screens_to_process: i32, + pub report: Option, +} + +impl Default for CcxDecoder608Settings { + fn default() -> Self { + Self { + direct_rollup: 0, + force_rollup: 0, + no_rollup: false, + default_color: CcxDecoder608ColorCode::Transparent, + screens_to_process: -1, + report: None, + } + } +} + +impl CcxDecoder608Settings { + pub fn to_ctype(&self) -> ccx_decoder_608_settings { + ccx_decoder_608_settings { + direct_rollup: self.direct_rollup, + force_rollup: self.force_rollup, + no_rollup: self.no_rollup as _, + default_color: self.default_color as _, + screens_to_process: self.screens_to_process, + report: if let Some(value) = self.report { + &mut value.to_ctype() + } else { + std::ptr::null::() as *mut ccx_decoder_608_report + }, + } + } +} + +pub const CCX_DTVCC_MAX_SERVICES: usize = 63; + +impl Default for CcxFrameType { + fn default() -> Self { + Self::ResetOrUnknown + } +} + +#[derive(Debug, Copy, Clone)] +pub enum CcxFrameType { + ResetOrUnknown = 0, + // IFrame = 1, + // PFrame = 2, + // BFrame = 3, + // DFrame = 4, +} + +#[derive(Debug, Default)] +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, + 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 +} + +impl CcxCommonTimingCtx { + pub fn to_ctype(&self) -> ccx_common_timing_ctx { + ccx_common_timing_ctx { + pts_set: self.pts_set, + min_pts_adjusted: self.min_pts_adjusted, + current_pts: self.current_pts, + current_picture_coding_type: self.current_picture_coding_type as _, + current_tref: self.current_tref, + min_pts: self.min_pts, + max_pts: self.max_pts, + sync_pts: self.sync_pts, + minimum_fts: self.minimum_fts, + fts_now: self.fts_now, + fts_offset: self.fts_offset, + fts_fc_offset: self.fts_fc_offset, + fts_max: self.fts_max, + fts_global: self.fts_global, + sync_pts2fts_set: self.sync_pts2fts_set, + sync_pts2fts_fts: self.sync_pts2fts_fts, + sync_pts2fts_pts: self.sync_pts2fts_pts, + pts_reset: self.pts_reset, + } + } +} + +impl Default for CcxDecoderDtvccSettings { + fn default() -> Self { + Self { + enabled: false, + print_file_reports: true, + no_rollup: false, + report: None, + active_services_count: 0, + services_enabled: [false; CCX_DTVCC_MAX_SERVICES], + timing: CcxCommonTimingCtx::default(), + } + } +} + +#[derive(Debug)] +pub struct CcxDecoderDtvccSettings { + pub enabled: bool, + pub print_file_reports: bool, + pub no_rollup: bool, + pub report: Option, + pub active_services_count: i32, + pub services_enabled: [bool; CCX_DTVCC_MAX_SERVICES], + pub timing: CcxCommonTimingCtx, +} + +impl CcxDecoderDtvccSettings { + pub fn to_ctype(&self) -> ccx_decoder_dtvcc_settings { + ccx_decoder_dtvcc_settings { + enabled: self.enabled as _, + print_file_reports: self.print_file_reports as _, + no_rollup: self.no_rollup as _, + report: if let Some(value) = self.report { + &mut value.to_ctype() + } else { + std::ptr::null::() as *mut ccx_decoder_dtvcc_report + }, + active_services_count: self.active_services_count, + services_enabled: self.services_enabled.map(|b| if b { 1 } else { 0 }), + timing: &mut self.timing.to_ctype(), + } + } +} + +impl Default for CcxDecoderDtvccReport { + fn default() -> Self { + Self { + reset_count: 0, + services: [0; CCX_DTVCC_MAX_SERVICES], + } + } +} + +#[derive(Debug, Copy, Clone)] + +pub struct CcxDecoderDtvccReport { + pub reset_count: i32, + pub services: [u32; CCX_DTVCC_MAX_SERVICES], +} + +impl CcxDecoderDtvccReport { + pub fn to_ctype(&self) -> ccx_decoder_dtvcc_report { + ccx_decoder_dtvcc_report { + reset_count: self.reset_count, + services: self.services, + } + } +} + +#[derive(Debug, Clone)] +pub struct CcxEncodersTranscriptFormat { + pub show_start_time: bool, + pub show_end_time: bool, + pub show_mode: bool, + pub show_cc: bool, + pub relative_timestamp: bool, + pub xds: bool, + pub use_colors: bool, + pub is_final: bool, +} + +impl Default for CcxEncodersTranscriptFormat { + fn default() -> Self { + Self { + show_start_time: false, + show_end_time: false, + show_mode: false, + show_cc: false, + relative_timestamp: true, + xds: false, + use_colors: true, + is_final: false, + } + } +} + +impl CcxEncodersTranscriptFormat { + fn to_ctype(&self) -> ccx_encoders_transcript_format { + ccx_encoders_transcript_format { + showStartTime: self.show_start_time as _, + showEndTime: self.show_end_time as _, + showMode: self.show_mode as _, + showCC: self.show_cc as _, + relativeTimestamp: self.relative_timestamp as _, + xds: self.xds as _, + useColors: self.use_colors as _, + isFinal: self.is_final as _, + } + } +} + +impl Default for CcxOutputDateFormat { + fn default() -> Self { + Self::None + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum CcxOutputDateFormat { + None = 0, + // HHMMSS = 1, + Seconds = 2, + Date = 3, + HhMmSsMs = 4, // HH:MM:SS,MILIS (.srt style) +} + +impl Default for CcxStreamMode { + fn default() -> Self { + Self::Autodetect + } +} + +#[derive(PartialEq, Debug, Copy, Clone)] +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- + #[cfg(feature = "wtv_debug")] + HexDump = 8, // Hexadecimal dump generated by wtvccdump + Wtv = 9, + #[cfg(feature = "enable_ffmpeg")] + Ffmpeg = 10, + // Gxf = 11, + Mkv = 12, + Mxf = 13, + Autodetect = 16, +} + +impl CcxStreamMode { + pub fn to_ctype(&self) -> ccx_stream_mode_enum { + match self { + CcxStreamMode::ElementaryOrNotFound => { + ccx_stream_mode_enum_CCX_SM_ELEMENTARY_OR_NOT_FOUND + } + CcxStreamMode::Transport => ccx_stream_mode_enum_CCX_SM_TRANSPORT, + CcxStreamMode::Program => ccx_stream_mode_enum_CCX_SM_PROGRAM, + CcxStreamMode::Asf => ccx_stream_mode_enum_CCX_SM_ASF, + CcxStreamMode::McpoodlesRaw => ccx_stream_mode_enum_CCX_SM_MCPOODLESRAW, + CcxStreamMode::Rcwt => ccx_stream_mode_enum_CCX_SM_RCWT, + // CcxStreamMode::Myth => ccx_stream_mode_enum_CCX_SM_MYTH, + CcxStreamMode::Mp4 => ccx_stream_mode_enum_CCX_SM_MP4, + #[cfg(feature = "wtv_debug")] + CcxStreamMode::HexDump => ccx_stream_mode_enum_CCX_SM_HEX_DUMP, + CcxStreamMode::Wtv => ccx_stream_mode_enum_CCX_SM_WTV, + #[cfg(feature = "enable_ffmpeg")] + CcxStreamMode::Ffmpeg => ccx_stream_mode_enum_CCX_SM_FFMPEG, + // CcxStreamMode::Gxf => ccx_stream_mode_enum_CCX_SM_GXF, + CcxStreamMode::Mkv => ccx_stream_mode_enum_CCX_SM_MKV, + CcxStreamMode::Mxf => ccx_stream_mode_enum_CCX_SM_MXF, + CcxStreamMode::Autodetect => ccx_stream_mode_enum_CCX_SM_AUTODETECT, + } + } +} + +impl Default for CcxCodeType { + fn default() -> Self { + Self::None + } +} + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum CcxCodeType { + Any = 0, + Teletext = 1, + Dvb = 2, + // IsdbCc = 3, + // AtscCc = 4, + None = 5, +} + +impl CcxCodeType { + pub fn to_ctype(&self) -> ccx_code_type { + match self { + CcxCodeType::Any => ccx_code_type_CCX_CODEC_ANY, + CcxCodeType::Teletext => ccx_code_type_CCX_CODEC_TELETEXT, + CcxCodeType::Dvb => ccx_code_type_CCX_CODEC_DVB, + // CcxCodeType::IsdbCc => ccx_code_type_CCX_CODEC_ISDB_CC, + // CcxCodeType::AtscCc => ccx_code_type_CCX_CODEC_ATSC_CC, + CcxCodeType::None => ccx_code_type_CCX_CODEC_NONE, + } + } +} + +impl Default for CcxDemuxerCfg { + fn default() -> Self { + Self { + m2ts: false, + auto_stream: CcxStreamMode::default(), + codec: CcxCodeType::Any, + nocodec: CcxCodeType::None, + ts_autoprogram: false, + ts_allprogram: false, + ts_cappids: [0; 128], + nb_ts_cappid: 0, + ts_forced_cappid: 0, + ts_forced_program: -1, + ts_forced_program_selected: false, + ts_datastreamtype: 0, + ts_forced_streamtype: 0, + } + } +} + +#[derive(Debug)] +pub struct CcxDemuxerCfg { + pub m2ts: bool, + pub auto_stream: CcxStreamMode, + pub codec: CcxCodeType, + pub nocodec: CcxCodeType, + pub ts_autoprogram: bool, + pub ts_allprogram: bool, + 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: bool, + pub ts_datastreamtype: i32, + pub ts_forced_streamtype: u32, +} + +impl CcxDemuxerCfg { + pub fn to_ctype(&self) -> demuxer_cfg { + demuxer_cfg { + m2ts: if self.m2ts { 1 } else { 0 }, + auto_stream: self.auto_stream.to_ctype(), + codec: self.codec.to_ctype(), + nocodec: self.nocodec.to_ctype(), + ts_autoprogram: if self.ts_autoprogram { 1 } else { 0 }, + ts_allprogram: if self.ts_allprogram as _ { 1 } else { 0 }, + ts_cappids: self.ts_cappids, + nb_ts_cappid: self.nb_ts_cappid, + ts_forced_cappid: self.ts_forced_cappid, + ts_forced_program: self.ts_forced_program, + ts_forced_program_selected: if self.ts_forced_program_selected { + 1 + } else { + 0 + }, + ts_datastreamtype: self.ts_datastreamtype, + ts_forced_streamtype: self.ts_forced_streamtype, + } + } +} + +impl Default for CcxOutputFormat { + fn default() -> Self { + Self::Raw + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum CcxOutputFormat { + Raw = 0, + Srt = 1, + Sami = 2, + Transcript = 3, + Rcwt = 4, + Null = 5, + Smptett = 6, + Spupng = 7, + Dvdraw = 8, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract + Webvtt = 9, + // SimpleXml = 10, + G608 = 11, + Curl = 12, + Ssa = 13, + Mcc = 14, + Scc = 15, + Ccd = 16, +} + +impl From for ccx_output_format { + fn from(value: CcxOutputFormat) -> ccx_output_format { + match value { + CcxOutputFormat::Raw => ccx_output_format::CCX_OF_RAW, + CcxOutputFormat::Srt => ccx_output_format::CCX_OF_SRT, + CcxOutputFormat::Sami => ccx_output_format::CCX_OF_SAMI, + CcxOutputFormat::Transcript => ccx_output_format::CCX_OF_TRANSCRIPT, + CcxOutputFormat::Rcwt => ccx_output_format::CCX_OF_RCWT, + CcxOutputFormat::Null => ccx_output_format::CCX_OF_NULL, + CcxOutputFormat::Smptett => ccx_output_format::CCX_OF_SMPTETT, + CcxOutputFormat::Spupng => ccx_output_format::CCX_OF_SPUPNG, + CcxOutputFormat::Dvdraw => ccx_output_format::CCX_OF_DVDRAW, + CcxOutputFormat::Webvtt => ccx_output_format::CCX_OF_WEBVTT, + // CcxOutputFormat::SimpleXml => ccx_output_format::CCX_OF_SIMPLE_XML, + CcxOutputFormat::G608 => ccx_output_format::CCX_OF_G608, + CcxOutputFormat::Curl => ccx_output_format::CCX_OF_CURL, + CcxOutputFormat::Ssa => ccx_output_format::CCX_OF_SSA, + CcxOutputFormat::Mcc => ccx_output_format::CCX_OF_MCC, + CcxOutputFormat::Scc => ccx_output_format::CCX_OF_SCC, + CcxOutputFormat::Ccd => ccx_output_format::CCX_OF_CCD, + } + } +} + +impl Default for CcxEncodingType { + fn default() -> Self { + Self::Utf8 + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum CcxEncodingType { + Unicode = 0, + Latin1 = 1, + Utf8 = 2, + // Ascii = 3, +} + +impl Default for CcxEncoderCfg { + fn default() -> Self { + Self { + extract: 1, + dtvcc_extract: false, + gui_mode_reports: false, + output_filename: String::default(), + write_format: CcxOutputFormat::default(), + keep_output_closed: false, + force_flush: false, + append_mode: false, + ucla: false, + encoding: CcxEncodingType::default(), + date_format: CcxOutputDateFormat::default(), + millis_separator: ',', + autodash: false, + trim_subs: false, + sentence_cap: false, + splitbysentence: false, + curlposturl: None, + filter_profanity: false, + with_semaphore: false, + start_credits_text: String::default(), + end_credits_text: String::default(), + startcreditsnotbefore: CcxBoundaryTime::start_credits_not_before(), + startcreditsnotafter: CcxBoundaryTime::start_credits_not_after(), + startcreditsforatleast: CcxBoundaryTime::start_credits_for_atleast(), + startcreditsforatmost: CcxBoundaryTime::start_credits_for_atmost(), + endcreditsforatleast: CcxBoundaryTime::end_credits_for_atleast(), + endcreditsforatmost: CcxBoundaryTime::end_credits_for_atmost(), + transcript_settings: CcxEncodersTranscriptFormat::default(), + send_to_srv: false, + no_bom: true, + first_input_file: String::default(), + multiple_files: false, + no_font_color: false, + no_type_setting: false, + cc_to_stdout: false, + line_terminator_lf: false, + subs_delay: 0, + program_number: 0, + in_format: 1, + nospupngocr: false, + force_dropframe: false, + render_font: String::default(), + render_font_italics: String::default(), + services_enabled: [false; CCX_DTVCC_MAX_SERVICES], + services_charsets: vec![], + all_services_charset: String::default(), + extract_only_708: false, + } + } +} + +#[derive(Debug)] +pub struct CcxEncoderCfg { + pub extract: i32, + pub dtvcc_extract: bool, + pub gui_mode_reports: bool, + pub output_filename: String, + pub write_format: CcxOutputFormat, + pub keep_output_closed: bool, + pub force_flush: bool, + pub append_mode: bool, + pub ucla: bool, + pub encoding: CcxEncodingType, + pub date_format: CcxOutputDateFormat, + pub millis_separator: char, + pub autodash: bool, + pub trim_subs: bool, + pub sentence_cap: bool, + pub splitbysentence: bool, + pub curlposturl: Option, + pub filter_profanity: bool, + pub with_semaphore: bool, + pub start_credits_text: String, + pub end_credits_text: String, + 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: bool, + pub no_bom: bool, + pub first_input_file: String, + pub multiple_files: bool, + pub no_font_color: bool, + pub no_type_setting: bool, + pub cc_to_stdout: bool, + pub line_terminator_lf: bool, + pub subs_delay: i64, + pub program_number: i32, + pub in_format: u8, + pub nospupngocr: bool, + pub force_dropframe: bool, + pub render_font: String, + pub render_font_italics: String, + pub services_enabled: [bool; CCX_DTVCC_MAX_SERVICES], + pub services_charsets: Vec, + pub all_services_charset: String, + pub extract_only_708: bool, +} + +impl CcxEncoderCfg { + pub fn to_ctype(&self) -> encoder_cfg { + unsafe { + encoder_cfg { + extract: self.extract, + dtvcc_extract: self.dtvcc_extract as _, + gui_mode_reports: self.gui_mode_reports as _, + output_filename: string_to_c_char(&self.output_filename), + write_format: self.write_format.into(), + keep_output_closed: self.keep_output_closed as _, + force_flush: self.force_flush as _, + append_mode: self.append_mode as _, + ucla: self.ucla as _, + encoding: self.encoding as _, + date_format: self.date_format as _, + millis_separator: self.millis_separator as _, + autodash: self.autodash as _, + trim_subs: self.trim_subs as _, + sentence_cap: self.sentence_cap as _, + splitbysentence: self.splitbysentence as _, + #[cfg(feature = "with_libcurl")] + curlposturl: string_to_c_char(&self.curlposturl.clone().unwrap()), + filter_profanity: self.filter_profanity as _, + with_semaphore: self.with_semaphore as _, + start_credits_text: string_to_c_char(&self.start_credits_text), + end_credits_text: string_to_c_char(&self.end_credits_text), + startcreditsnotbefore: self.startcreditsnotbefore.to_ctype(), + startcreditsnotafter: self.startcreditsnotafter.to_ctype(), + startcreditsforatleast: self.startcreditsforatleast.to_ctype(), + startcreditsforatmost: self.startcreditsforatmost.to_ctype(), + endcreditsforatleast: self.endcreditsforatleast.to_ctype(), + endcreditsforatmost: self.endcreditsforatmost.to_ctype(), + transcript_settings: self.transcript_settings.to_ctype(), + send_to_srv: self.send_to_srv as _, + no_bom: self.no_bom as _, + first_input_file: string_to_c_char(&self.first_input_file), + multiple_files: self.multiple_files as _, + no_font_color: self.no_font_color as _, + no_type_setting: self.no_type_setting as _, + cc_to_stdout: self.cc_to_stdout as _, + line_terminator_lf: self.line_terminator_lf as _, + subs_delay: self.subs_delay, + program_number: self.program_number, + in_format: self.in_format, + nospupngocr: self.nospupngocr as _, + force_dropframe: self.force_dropframe as _, + render_font: string_to_c_char(&self.render_font), + render_font_italics: string_to_c_char(&self.render_font_italics), + services_enabled: self.services_enabled.map(|b| if b { 1 } else { 0 }), + services_charsets: string_to_c_chars(self.services_charsets.clone()), + all_services_charset: string_to_c_char(&self.all_services_charset), + extract_only_708: self.extract_only_708 as _, + } + } + } +} + +#[derive(Debug)] +pub struct CcxOptions { + pub extract: i32, + pub no_rollup: bool, + pub noscte20: bool, + pub webvtt_create_css: bool, + pub cc_channel: i32, + pub buffer_input: bool, + pub nofontcolor: bool, + pub write_format: CcxOutputFormat, + pub send_to_srv: bool, + pub nohtmlescape: bool, + pub notypesetting: bool, + pub extraction_start: CcxBoundaryTime, + pub extraction_end: CcxBoundaryTime, + pub print_file_reports: bool, + pub settings_608: CcxDecoder608Settings, + pub settings_dtvcc: CcxDecoderDtvccSettings, + pub is_608_enabled: bool, + pub is_708_enabled: bool, + pub millis_separator: char, + pub binary_concat: bool, + pub use_gop_as_pts: i32, + pub fix_padding: bool, + pub gui_mode_reports: bool, + pub no_progress_bar: bool, + pub sentence_cap_file: Option, + pub live_stream: i32, + pub filter_profanity_file: Option, + pub messages_target: i32, + pub timestamp_map: bool, + pub dolevdist: i32, + pub levdistmincnt: i32, + pub levdistmaxpct: i32, + pub investigate_packets: bool, + pub fullbin: bool, + pub nosync: bool, + pub hauppauge_mode: bool, + pub wtvconvertfix: bool, + pub wtvmpeg2: bool, + pub auto_myth: i32, + pub mp4vidtrack: bool, + pub extract_chapters: bool, + pub usepicorder: bool, + pub xmltv: i32, + pub xmltvliveinterval: i32, + pub xmltvoutputinterval: i32, + pub xmltvonlycurrent: i32, + pub keep_output_closed: bool, + pub force_flush: bool, + pub append_mode: bool, + pub ucla: bool, + pub tickertext: bool, + pub hardsubx: bool, + pub hardsubx_and_common: bool, + pub dvblang: Option, + pub ocrlang: Option, + pub ocr_oem: i32, + pub ocr_quantmode: i32, + pub mkvlang: Option, + pub analyze_video_stream: bool, + pub hardsubx_ocr_mode: i32, + pub hardsubx_subcolor: i32, + pub hardsubx_min_sub_duration: f32, + pub hardsubx_detect_italics: bool, + pub hardsubx_conf_thresh: f32, + pub hardsubx_hue: f32, + pub hardsubx_lum_thresh: f32, + pub transcript_settings: CcxEncodersTranscriptFormat, + pub date: CcxOutputDateFormat, + pub write_format_rewritten: bool, + pub use_ass_instead_of_ssa: bool, + pub use_webvtt_styling: bool, + pub debug_mask: CcxDebugMessageTypes, + pub debug_mask_on_debug: i64, + pub udpsrc: Option, + pub udpaddr: Option, + pub udpport: u32, + pub tcpport: Option, + pub tcp_password: Option, + pub tcp_desc: Option, + pub srv_addr: Option, + pub srv_port: Option, + pub noautotimeref: bool, + pub input_source: CcxDatasource, + pub output_filename: Option, + pub inputfile: Option>, + pub num_input_files: i32, + pub demux_cfg: CcxDemuxerCfg, + pub enc_cfg: CcxEncoderCfg, + pub subs_delay: i64, + pub cc_to_stdout: bool, + pub pes_header_to_stdout: bool, + pub ignore_pts_jumps: bool, + pub multiprogram: bool, + pub out_interval: i32, + pub segment_on_key_frames_only: bool, + pub curlposturl: Option, + pub sharing_enabled: bool, + pub sharing_url: Option, + pub translate_enabled: bool, + pub translate_langs: Option, + pub translate_key: Option, +} + +impl Default for CcxOptions { + fn default() -> Self { + Self { + extract: 1, + no_rollup: false, + noscte20: false, + webvtt_create_css: false, + cc_channel: 1, + buffer_input: false, + nofontcolor: false, + write_format: CcxOutputFormat::Srt, + send_to_srv: false, + nohtmlescape: false, + notypesetting: false, + extraction_start: CcxBoundaryTime::default(), + extraction_end: CcxBoundaryTime::default(), + print_file_reports: false, + settings_608: CcxDecoder608Settings::default(), + settings_dtvcc: CcxDecoderDtvccSettings::default(), + is_608_enabled: false, + is_708_enabled: false, + millis_separator: ',', + binary_concat: true, + use_gop_as_pts: 0, + fix_padding: false, + gui_mode_reports: false, + no_progress_bar: false, + sentence_cap_file: None, + live_stream: 0, + filter_profanity_file: None, + messages_target: 1, + timestamp_map: false, + dolevdist: 1, + levdistmincnt: 2, + levdistmaxpct: 10, + investigate_packets: false, + fullbin: false, + nosync: false, + hauppauge_mode: false, + wtvconvertfix: false, + wtvmpeg2: false, + auto_myth: 2, + mp4vidtrack: false, + extract_chapters: false, + usepicorder: false, + xmltv: 0, + xmltvliveinterval: 10, + xmltvoutputinterval: 0, + xmltvonlycurrent: 0, + keep_output_closed: false, + force_flush: false, + append_mode: false, + ucla: false, + tickertext: false, + hardsubx: false, + hardsubx_and_common: false, + dvblang: None, + ocrlang: None, + ocr_oem: -1, + ocr_quantmode: 1, + mkvlang: None, + analyze_video_stream: false, + hardsubx_ocr_mode: 0, + hardsubx_subcolor: 0, + hardsubx_min_sub_duration: 0.5, + hardsubx_detect_italics: false, + hardsubx_conf_thresh: 0.0, + hardsubx_hue: 0.0, + hardsubx_lum_thresh: 95.0, + transcript_settings: CcxEncodersTranscriptFormat::default(), + date: CcxOutputDateFormat::default(), + write_format_rewritten: false, + use_ass_instead_of_ssa: false, + use_webvtt_styling: false, + debug_mask: CcxDebugMessageTypes::GenericNotices, + debug_mask_on_debug: 8, + udpsrc: None, + udpaddr: None, + udpport: 0, + tcpport: None, + tcp_password: None, + tcp_desc: None, + srv_addr: None, + srv_port: None, + noautotimeref: false, + input_source: CcxDatasource::File, + output_filename: None, + inputfile: None, + num_input_files: 0, + demux_cfg: CcxDemuxerCfg::default(), + enc_cfg: CcxEncoderCfg::default(), + subs_delay: 0, + cc_to_stdout: false, + pes_header_to_stdout: false, + ignore_pts_jumps: true, + multiprogram: false, + out_interval: -1, + segment_on_key_frames_only: false, + curlposturl: None, + sharing_enabled: false, + sharing_url: None, + translate_enabled: false, + translate_langs: None, + translate_key: None, + } + } +} + +impl CcxOptions { + /// # Safety + /// + /// This function is unsafe because it dereferences the pointer passed to it. + pub unsafe fn to_ctype(&self, options: *mut ccx_s_options) { + (*options).extract = self.extract; + (*options).no_rollup = self.no_rollup as _; + (*options).noscte20 = self.noscte20 as _; + (*options).webvtt_create_css = self.webvtt_create_css as _; + (*options).cc_channel = self.cc_channel; + (*options).buffer_input = self.buffer_input as _; + (*options).nofontcolor = self.nofontcolor as _; + (*options).write_format = self.write_format.into(); + (*options).send_to_srv = self.send_to_srv as _; + (*options).nohtmlescape = self.nohtmlescape as _; + (*options).notypesetting = self.notypesetting as _; + (*options).extraction_start = self.extraction_start.to_ctype(); + (*options).extraction_end = self.extraction_end.to_ctype(); + (*options).print_file_reports = self.print_file_reports as _; + (*options).settings_608 = self.settings_608.to_ctype(); + (*options).settings_dtvcc = self.settings_dtvcc.to_ctype(); + (*options).is_608_enabled = self.is_608_enabled as _; + (*options).is_708_enabled = self.is_708_enabled as _; + (*options).millis_separator = self.millis_separator as _; + (*options).binary_concat = self.binary_concat as _; + (*options).use_gop_as_pts = self.use_gop_as_pts; + (*options).fix_padding = self.fix_padding as _; + (*options).gui_mode_reports = self.gui_mode_reports as _; + (*options).no_progress_bar = self.no_progress_bar as _; + + if self.sentence_cap_file.is_some() { + (*options).sentence_cap_file = + string_to_c_char(&self.sentence_cap_file.clone().unwrap()); + } + + (*options).live_stream = self.live_stream; + if self.filter_profanity_file.is_some() { + (*options).filter_profanity_file = + string_to_c_char(&self.filter_profanity_file.clone().unwrap()); + } + (*options).messages_target = self.messages_target; + (*options).timestamp_map = self.timestamp_map as _; + (*options).dolevdist = self.dolevdist; + (*options).levdistmincnt = self.levdistmincnt; + (*options).levdistmaxpct = self.levdistmaxpct; + (*options).investigate_packets = self.investigate_packets as _; + (*options).fullbin = self.fullbin as _; + (*options).nosync = self.nosync as _; + (*options).hauppauge_mode = self.hauppauge_mode as _; + (*options).wtvconvertfix = self.wtvconvertfix as _; + (*options).wtvmpeg2 = self.wtvmpeg2 as _; + (*options).auto_myth = self.auto_myth; + (*options).mp4vidtrack = self.mp4vidtrack as _; + (*options).extract_chapters = self.extract_chapters as _; + (*options).usepicorder = self.usepicorder as _; + (*options).xmltv = self.xmltv; + (*options).xmltvliveinterval = self.xmltvliveinterval; + (*options).xmltvoutputinterval = self.xmltvoutputinterval; + (*options).xmltvonlycurrent = self.xmltvonlycurrent; + (*options).keep_output_closed = self.keep_output_closed as _; + (*options).force_flush = self.force_flush as _; + (*options).append_mode = self.append_mode as _; + (*options).ucla = self.ucla as _; + (*options).tickertext = self.tickertext as _; + (*options).hardsubx = self.hardsubx as _; + (*options).hardsubx_and_common = self.hardsubx_and_common as _; + if self.dvblang.is_some() { + (*options).dvblang = string_to_c_char(&self.dvblang.clone().unwrap()); + } + if self.ocrlang.is_some() { + (*options).ocrlang = string_to_c_char(&self.ocrlang.clone().unwrap()); + } + (*options).ocr_oem = self.ocr_oem; + (*options).ocr_quantmode = self.ocr_quantmode; + if self.mkvlang.is_some() { + (*options).mkvlang = string_to_c_char(&self.mkvlang.clone().unwrap()); + } + (*options).analyze_video_stream = self.analyze_video_stream as _; + (*options).hardsubx_ocr_mode = self.hardsubx_ocr_mode; + (*options).hardsubx_subcolor = self.hardsubx_subcolor; + (*options).hardsubx_min_sub_duration = self.hardsubx_min_sub_duration; + (*options).hardsubx_detect_italics = self.hardsubx_detect_italics as _; + (*options).hardsubx_conf_thresh = self.hardsubx_conf_thresh; + (*options).hardsubx_hue = self.hardsubx_hue; + (*options).hardsubx_lum_thresh = self.hardsubx_lum_thresh; + (*options).transcript_settings = self.transcript_settings.to_ctype(); + (*options).date_format = self.date as _; + (*options).write_format_rewritten = self.write_format_rewritten as _; + (*options).use_ass_instead_of_ssa = self.use_ass_instead_of_ssa as _; + (*options).use_webvtt_styling = self.use_webvtt_styling as _; + (*options).debug_mask = self.debug_mask as _; + (*options).debug_mask_on_debug = self.debug_mask_on_debug; + if self.udpsrc.is_some() { + (*options).udpsrc = string_to_c_char(&self.udpsrc.clone().unwrap()); + } + if self.udpaddr.is_some() { + (*options).udpaddr = string_to_c_char(&self.udpaddr.clone().unwrap()); + } + (*options).udpport = self.udpport; + if self.tcpport.is_some() { + (*options).tcpport = string_to_c_char(&self.tcpport.unwrap().to_string()); + } + if self.tcp_password.is_some() { + (*options).tcp_password = string_to_c_char(&self.tcp_password.clone().unwrap()); + } + if self.tcp_desc.is_some() { + (*options).tcp_desc = string_to_c_char(&self.tcp_desc.clone().unwrap()); + } + if self.srv_addr.is_some() { + (*options).srv_addr = string_to_c_char(&self.srv_addr.clone().unwrap()); + } + if self.srv_port.is_some() { + (*options).srv_port = string_to_c_char(&self.srv_port.unwrap().to_string()); + } + (*options).noautotimeref = self.noautotimeref as _; + (*options).input_source = self.input_source as _; + if self.output_filename.is_some() { + (*options).output_filename = string_to_c_char(&self.output_filename.clone().unwrap()); + } + if self.inputfile.is_some() { + (*options).inputfile = string_to_c_chars(self.inputfile.clone().unwrap()); + } + (*options).num_input_files = self.num_input_files; + (*options).demux_cfg = self.demux_cfg.to_ctype(); + (*options).enc_cfg = self.enc_cfg.to_ctype(); + (*options).subs_delay = self.subs_delay; + (*options).cc_to_stdout = self.cc_to_stdout as _; + (*options).pes_header_to_stdout = self.pes_header_to_stdout as _; + (*options).ignore_pts_jumps = self.ignore_pts_jumps as _; + (*options).multiprogram = self.multiprogram as _; + (*options).out_interval = self.out_interval; + (*options).segment_on_key_frames_only = self.segment_on_key_frames_only as _; + #[cfg(feature = "with_libcurl")] + { + if self.curlposturl.is_some() { + (*options).curlposturl = string_to_c_char(&self.curlposturl.unwrap()); + } + } + #[cfg(feature = "enable_sharing")] + { + (*options).sharing_enabled = self.sharing_enabled as _; + if self.sharing_url.is_some() { + (*options).sharing_url = string_to_c_char(&self.sharing_url.unwrap()); + } + (*options).translate_enabled = self.translate_enabled as _; + if self.translate_langs.is_some() { + (*options).translate_langs = string_to_c_char(&self.translate_langs.unwrap()); + } + if self.translate_key.is_some() { + (*options).translate_key = string_to_c_char(&self.translate_key.unwrap()); + } + } + } +} + +impl Default for HardsubxOcrMode { + fn default() -> Self { + Self::Frame + } +} +#[allow(dead_code)] +pub enum HardsubxOcrMode { + Frame = 0, + Word = 1, + Letter = 2, +} + +impl Default for CcxDatasource { + fn default() -> Self { + Self::None + } +} + +#[derive(Debug, PartialEq, Clone, Copy)] +pub enum CcxDatasource { + None = -1, + File = 0, + Stdin = 1, + Network = 2, + Tcp = 3, +} + +impl Default for CcxEia608Format { + fn default() -> Self { + Self::CcScreen + } +} +pub enum CcxEia608Format { + CcScreen, + // CcLine, + // Xds, +} + +impl Default for CcModes { + fn default() -> Self { + Self::Text + } +} + +#[derive(Debug)] +pub enum CcModes { + // Popon = 0, + // Rollup2 = 1, + // Rollup3 = 2, + // Rollup4 = 3, + Text = 4, + // Painton = 5, + // FakeRollup1 = 100, +} + +impl Default for FontBits { + fn default() -> Self { + Self::Regular + } +} + +#[derive(Debug)] +pub enum FontBits { + Regular = 0, + // Italics = 1, + // Underlined = 2, + // UnderlinedItalics = 3, +} + +impl Default for CcxDecoder608ColorCode { + fn default() -> Self { + Self::Userdefined + } +} + +#[derive(Debug, Copy, Clone)] +pub enum CcxDecoder608ColorCode { + White = 0, + Green = 1, + Blue = 2, + Cyan = 3, + Red = 4, + Yellow = 5, + Magenta = 6, + Userdefined = 7, + Black = 8, + Transparent = 9, + Max, +} + +#[derive(Copy, Clone, Debug)] +pub enum ExitCode { + NoInputFiles = 2, + TooManyInputFiles = 3, + IncompatibleParameters = 4, + UnableToDetermineFileSize = 6, + MalformedParameter = 7, + ReadError = 8, + NoCaptions = 10, + WithHelp = 11, + NotClassified = 300, + ErrorInCapitalizationFile = 501, + BufferFull = 502, + MissingASFHeader = 1001, + MissingRCWTHeader = 1002, + FileCreationFailed = 5, + Unsupported = 9, + NotEnoughMemory = 500, + BugBug = 1000, +} + +// #[derive(Copy, Clone, Debug)] +// pub enum CCXResult { +// Ok = 0, +// EAGAIN = -100, +// EOF = -101, +// EINVAL = -102, +// ENOSUPP = -103, +// ENOMEM = -104, +// } + +#[derive(Copy, Clone, Debug)] +#[allow(dead_code)] +pub enum HardsubxColorType { + White = 0, + Yellow = 1, + Green = 2, + Cyan = 3, + Blue = 4, + Magenta = 5, + Red = 6, + Custom = 7, +} + +impl Default for CcxDebugMessageTypes { + fn default() -> Self { + Self::None + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +pub enum CcxDebugMessageTypes { + None = 0, + Parse = 1, + Vides = 2, + Time = 4, + Verbose = 8, + Decoder608 = 0x10, + Decoder708 = 0x20, + DecoderXds = 0x40, + Cbraw = 0x80, + GenericNotices = 0x100, + Teletext = 0x200, + Pat = 0x400, + Pmt = 0x800, + Levenshtein = 0x1000, + Dvb = 0x2000, + Dumpdef = 0x4000, + #[cfg(feature = "enable_sharing")] + Share = 0x8000, +} diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 1fec8fa5e..981b59790 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -12,27 +12,40 @@ pub mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } +pub mod activity; +pub mod args; +pub mod ccx_encoders_helpers; +pub mod common; pub mod decoder; #[cfg(feature = "hardsubx_ocr")] pub mod hardsubx; pub mod libccxr_exports; +pub mod parser; pub mod utils; #[cfg(windows)] use std::os::windows::io::{FromRawHandle, RawHandle}; -use std::{io::Write, os::raw::c_int}; +use std::{io::Write, os::raw::c_char, os::raw::c_int}; +use args::Args; use bindings::*; +use clap::{error::ErrorKind, Parser}; +use common::{CcxOptions, CcxTeletextConfig}; use decoder::Dtvcc; use utils::is_true; use env_logger::{builder, Target}; use log::{warn, LevelFilter}; +use std::ffi::CStr; + +use crate::common::ExitCode; extern "C" { static mut cb_708: c_int; static mut cb_field1: c_int; static mut cb_field2: c_int; + static mut MPEG_CLOCK_FREQ: c_int; + static mut tlt_config: ccx_s_teletext_config; } /// Initialize env logger with custom format, using stdout as target @@ -170,3 +183,72 @@ extern "C" fn ccxr_close_handle(handle: RawHandle) { let _file = File::from_raw_handle(handle); } } + +extern "C" { + fn version(location: *const c_char); + fn set_binary_mode(); +} + +/// # Safety +/// Safe if argv is a valid pointer +/// +/// Parse parameters from argv and argc +#[no_mangle] +pub unsafe extern "C" fn ccxr_parse_parameters( + mut _options: *mut ccx_s_options, + argc: c_int, + argv: *mut *mut c_char, +) -> c_int { + // Convert argv to Vec and pass it to parse_parameters + let args = std::slice::from_raw_parts(argv, argc as usize) + .iter() + .map(|&arg| { + CStr::from_ptr(arg) + .to_str() + .expect("Invalid UTF-8 sequence in argument") + .to_owned() + }) + .collect::>(); + + if args.len() <= 1 { + return ExitCode::NoInputFiles as _; + } + + let args: Args = match Args::try_parse_from(args) { + Ok(args) => args, + Err(e) => { + // Not all errors are actual errors, some are just help or version + // So handle them accordingly + match e.kind() { + ErrorKind::DisplayHelp => { + // Print the help string + println!("{}", e); + return ExitCode::WithHelp as _; + } + ErrorKind::DisplayVersion => { + version(*argv); + return ExitCode::WithHelp as _; + } + ErrorKind::UnknownArgument => { + println!("Unknown Argument"); + println!("{}", e); + return 1; + } + _ => { + println!("{}", e); + return 1; + } + } + } + }; + + let mut opt = CcxOptions::default(); + let mut _tlt_config = CcxTeletextConfig::default(); + + opt.parse_parameters(&args, &mut _tlt_config); + tlt_config = _tlt_config.to_ctype(); + // Convert the rust struct (CcxOptions) to C struct (ccx_s_options), so that it can be used by the C code + opt.to_ctype(_options); + + 0 +} diff --git a/src/rust/src/parser.rs b/src/rust/src/parser.rs new file mode 100644 index 000000000..a079c6813 --- /dev/null +++ b/src/rust/src/parser.rs @@ -0,0 +1,2284 @@ +use args::{Args, OutFormat}; +use num_integer::Integer; + +use std::convert::TryInto; +use std::fs::File; +use std::io::{prelude::*, BufReader}; +use std::ptr::addr_of_mut; +use std::string::String; + +use cfg_if::cfg_if; + +use common::CcxOptions; +use time::OffsetDateTime; + +use crate::args::{self, InFormat}; +use crate::ccx_encoders_helpers::{ + CAPITALIZATION_LIST, CAPITALIZED_BUILTIN, PROFANE, PROFANE_BUILTIN, +}; +use crate::common; +use crate::{args::Codec, common::CcxDebugMessageTypes, common::*}; + +cfg_if! { + if #[cfg(test)] { + use crate::parser::tests::{set_binary_mode, MPEG_CLOCK_FREQ}; + } else { + use crate::{set_binary_mode, MPEG_CLOCK_FREQ}; + } +} + +cfg_if! { + if #[cfg(windows)] { + const DEFAULT_FONT_PATH: &str = "C:\\\\Windows\\\\Fonts\\\\calibri.ttf"; + const DEFAULT_FONT_PATH_ITALICS: &str = "C:\\\\Windows\\\\Fonts\\\\calibrii.ttf"; + } else if #[cfg(target_os = "macos")] { + const DEFAULT_FONT_PATH: &str = "/System/Library/Fonts/Helvetica.ttc"; + const DEFAULT_FONT_PATH_ITALICS: &str = "/System/Library/Fonts/Helvetica-Oblique.ttf"; + } else { + const DEFAULT_FONT_PATH: &str = "/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf"; + const DEFAULT_FONT_PATH_ITALICS: &str = "/usr/share/fonts/truetype/noto/NotoSans-Italic.ttf"; + } +} + +pub static mut FILEBUFFERSIZE: i64 = 1024 * 1024 * 16; +static mut USERCOLOR_RGB: String = String::new(); +pub static mut UTC_REFVALUE: u64 = 0; +const CCX_DECODER_608_SCREEN_WIDTH: u16 = 32; +static mut inputfile_capacity: i32 = 0; + +fn to_ms(value: &str) -> CcxBoundaryTime { + let mut parts = value.rsplit(':'); + + let seconds: i32 = parts + .next() + .unwrap_or_else(|| { + println!("Malformed timecode: {}", value); + std::process::exit(ExitCode::MalformedParameter as i32); + }) + .parse() + .unwrap(); + + let mut minutes: i32 = 0; + + if let Some(mins) = parts.next() { + minutes = mins.parse().unwrap(); + }; + let mut hours: i32 = 0; + + if let Some(hrs) = parts.next() { + hours = hrs.parse().unwrap(); + }; + + if seconds > 60 || minutes > 60 { + println!("Malformed timecode: {}", value); + std::process::exit(ExitCode::MalformedParameter as i32); + } + + CcxBoundaryTime { + hh: hours, + mm: minutes, + ss: seconds, + time_in_ms: (hours + 60 * minutes + 3600 * seconds) as i64, + set: true, + } +} + +fn get_vector_words(string_array: &[&str]) -> Vec { + let mut vector = Vec::new(); + for string in string_array { + vector.push(String::from(*string)); + } + vector +} + +fn atol(bufsize: &str) -> i64 { + let mut val = bufsize[0..bufsize.len() - 1].parse::().unwrap(); + let size = bufsize + .to_string() + .to_uppercase() + .chars() + .nth(bufsize.len() - 1) + .unwrap(); + if size == 'M' { + val *= 1024 * 1024; + } else if size == 'K' { + val *= 1024; + } + val +} + +fn atoi_hex(s: &str) -> Result +where + T: Integer + std::str::FromStr, +{ + if s.len() > 2 && s.to_lowercase().starts_with("0x") { + // Hexadecimal + T::from_str_radix(&s[2..], 16).map_err(|_| "not a valid hexadecimal number") + } else { + // Decimal + s.parse::().map_err(|_| "not a valid decimal number") + } +} + +fn get_atoi_hex(s: &str) -> T +where + T: Integer + std::str::FromStr, +{ + match atoi_hex(s) { + Ok(val) => val, + Err(_) => { + println!("Malformed parameter: {}", s); + std::process::exit(ExitCode::MalformedParameter as i32) + } + } +} + +unsafe fn process_word_file(filename: &str, list: *mut Vec) -> Result<(), std::io::Error> { + let file = File::open(filename)?; + let reader = BufReader::new(file); + let mut num = 0; + + for line in reader.lines() { + num += 1; + let line = line.unwrap(); + if line.starts_with('#') { + continue; + } + + let new_len = line.trim().len(); + if new_len > CCX_DECODER_608_SCREEN_WIDTH as usize { + println!( + "Word in line {} too long, max = {} characters.", + num, CCX_DECODER_608_SCREEN_WIDTH + ); + continue; + } + + if new_len > 0 { + (*list).push(line.trim().to_string()); + } + } + Ok(()) +} +fn mkvlang_params_check(lang: &str) { + let mut initial = 0; + let mut _present = 0; + + for (char_index, c) in lang.to_lowercase().chars().enumerate() { + if c == ',' { + _present = char_index; + + if _present - initial < 3 || _present - initial > 6 { + println!("language codes should be xxx,xxx,xxx,....\n"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + + if _present - initial == 6 { + let sub_slice = &lang[initial.._present]; + if !sub_slice.contains('-') { + println!("language code is not of the form xxx-xx\n"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + } + + initial = _present + 1; + } + } + + // Steps to check for the last lang of multiple mkvlangs provided by the user. + _present = lang.len() - 1; + + for char_index in (0.._present).rev() { + if lang.chars().nth(char_index) == Some(',') { + initial = char_index + 1; + break; + } + } + + if _present - initial < 2 || _present - initial > 5 { + println!("last language code should be xxx.\n"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + + if _present - initial == 5 { + let sub_slice = &lang[initial.._present]; + if !sub_slice.contains('-') { + println!("last language code is not of the form xxx-xx\n"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + } +} + +impl CcxOptions { + fn set_output_format_type(&mut self, out_format: OutFormat) { + match out_format { + #[cfg(feature = "with_libcurl")] + OutFormat::Curl => self.write_format = CcxOutputFormat::Curl, + OutFormat::Ass => self.write_format = CcxOutputFormat::Ssa, + OutFormat::Ccd => self.write_format = CcxOutputFormat::Ccd, + OutFormat::Scc => self.write_format = CcxOutputFormat::Scc, + OutFormat::Srt => self.write_format = CcxOutputFormat::Srt, + OutFormat::Ssa => self.write_format = CcxOutputFormat::Ssa, + OutFormat::Webvtt => self.write_format = CcxOutputFormat::Webvtt, + OutFormat::WebvttFull => { + self.write_format = CcxOutputFormat::Webvtt; + self.use_webvtt_styling = true; + } + OutFormat::Sami => self.write_format = CcxOutputFormat::Sami, + OutFormat::Txt => { + self.write_format = CcxOutputFormat::Transcript; + self.settings_dtvcc.no_rollup = true; + } + OutFormat::Ttxt => { + self.write_format = CcxOutputFormat::Transcript; + if self.date == CcxOutputDateFormat::None { + self.date = CcxOutputDateFormat::HhMmSsMs; + } + // Sets the right things so that timestamps and the mode are printed. + if !self.transcript_settings.is_final { + self.transcript_settings.show_start_time = true; + self.transcript_settings.show_end_time = true; + self.transcript_settings.show_cc = false; + self.transcript_settings.show_mode = true; + } + } + OutFormat::Report => { + self.write_format = CcxOutputFormat::Null; + self.messages_target = 0; + self.print_file_reports = true; + self.demux_cfg.ts_allprogram = true; + } + OutFormat::Raw => self.write_format = CcxOutputFormat::Raw, + OutFormat::Smptett => self.write_format = CcxOutputFormat::Smptett, + OutFormat::Bin => self.write_format = CcxOutputFormat::Rcwt, + OutFormat::Null => self.write_format = CcxOutputFormat::Null, + OutFormat::Dvdraw => self.write_format = CcxOutputFormat::Dvdraw, + OutFormat::Spupng => self.write_format = CcxOutputFormat::Spupng, + // OutFormat::SimpleXml => self.write_format = CcxOutputFormat::SimpleXml, + OutFormat::G608 => self.write_format = CcxOutputFormat::G608, + OutFormat::Mcc => self.write_format = CcxOutputFormat::Mcc, + } + } + + fn set_output_format(&mut self, args: &Args) { + self.write_format_rewritten = true; + + if self.send_to_srv && args.out.unwrap_or(OutFormat::Null) != OutFormat::Bin { + println!("Output format is changed to bin\n"); + self.set_output_format_type(OutFormat::Bin); + return; + } + + if let Some(out_format) = args.out { + self.set_output_format_type(out_format); + } else if args.sami { + self.set_output_format_type(OutFormat::Sami); + } else if args.webvtt { + self.set_output_format_type(OutFormat::Webvtt); + } else if args.srt { + self.set_output_format_type(OutFormat::Srt); + } else if args.null { + self.set_output_format_type(OutFormat::Null); + } else if args.dvdraw { + self.set_output_format_type(OutFormat::Dvdraw); + } else if args.txt { + self.set_output_format_type(OutFormat::Txt); + } else if args.ttxt { + self.set_output_format_type(OutFormat::Ttxt); + } else if args.mcc { + self.set_output_format_type(OutFormat::Mcc); + } + } + + fn set_input_format_type(&mut self, input_format: InFormat) { + match input_format { + #[cfg(feature = "wtv_debug")] + InFormat::Hex => self.demux_cfg.auto_stream = CcxStreamMode::HexDump, + InFormat::Es => self.demux_cfg.auto_stream = CcxStreamMode::ElementaryOrNotFound, + InFormat::Ts => self.demux_cfg.auto_stream = CcxStreamMode::Transport, + InFormat::M2ts => self.demux_cfg.auto_stream = CcxStreamMode::Transport, + InFormat::Ps => self.demux_cfg.auto_stream = CcxStreamMode::Program, + InFormat::Asf => self.demux_cfg.auto_stream = CcxStreamMode::Asf, + InFormat::Wtv => self.demux_cfg.auto_stream = CcxStreamMode::Wtv, + InFormat::Raw => self.demux_cfg.auto_stream = CcxStreamMode::McpoodlesRaw, + InFormat::Bin => self.demux_cfg.auto_stream = CcxStreamMode::Rcwt, + InFormat::Mp4 => self.demux_cfg.auto_stream = CcxStreamMode::Mp4, + InFormat::Mkv => self.demux_cfg.auto_stream = CcxStreamMode::Mkv, + InFormat::Mxf => self.demux_cfg.auto_stream = CcxStreamMode::Mxf, + } + } + + fn set_input_format(&mut self, args: &Args) { + if self.input_source == CcxDatasource::Tcp { + println!("Input format is changed to bin\n"); + self.set_input_format_type(InFormat::Bin); + return; + } + + if let Some(input_format) = args.input { + self.set_input_format_type(input_format); + } else if args.es { + self.set_input_format_type(InFormat::Es); + } else if args.ts { + self.set_input_format_type(InFormat::Ts); + } else if args.ps { + self.set_input_format_type(InFormat::Ps); + } else if args.asf || args.dvr_ms { + self.set_input_format_type(InFormat::Asf); + } else if args.wtv { + self.set_input_format_type(InFormat::Wtv); + } else { + println!("Unknown input file format: {}\n", args.input.unwrap()); + std::process::exit(ExitCode::MalformedParameter as i32); + } + } + + fn parse_708_services(&mut self, s: &str) { + if s.starts_with("all") { + let charset = if s.len() > 3 { &s[4..s.len() - 1] } else { "" }; + self.settings_dtvcc.enabled = true; + self.enc_cfg.dtvcc_extract = true; + charset.clone_into(&mut self.enc_cfg.all_services_charset); + self.enc_cfg.services_charsets = vec![String::new(); CCX_DTVCC_MAX_SERVICES]; + + for i in 0..CCX_DTVCC_MAX_SERVICES { + self.settings_dtvcc.services_enabled[i] = true; + self.enc_cfg.services_enabled[i] = true; + } + + self.settings_dtvcc.active_services_count = CCX_DTVCC_MAX_SERVICES as _; + return; + } + + let mut services = Vec::new(); + let mut charsets = Vec::new(); + for c in s.split(',') { + let mut service = String::new(); + let mut charset = None; + let mut inside_charset = false; + + for e in c.chars() { + if e.is_ascii_digit() && !inside_charset { + service.push(e); + } else if e == '[' { + inside_charset = true; + } else if e == ']' { + inside_charset = false; + } else if inside_charset { + if charset.is_none() { + charset = Some(String::new()); + } + charset.as_mut().unwrap().push(e); + } + } + if service.is_empty() { + continue; + } + services.push(service); + charsets.push(charset.clone()); + } + + self.enc_cfg.services_charsets = vec![String::new(); CCX_DTVCC_MAX_SERVICES]; + + for (i, service) in services.iter().enumerate() { + let svc = service.parse::().unwrap(); + if !(1..=CCX_DTVCC_MAX_SERVICES).contains(&svc) { + println!("[CEA-708] Malformed parameter: Invalid service number ({}), valid range is 1-{}.\n", svc, CCX_DTVCC_MAX_SERVICES); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.settings_dtvcc.services_enabled[svc - 1] = true; + self.enc_cfg.services_enabled[svc - 1] = true; + self.settings_dtvcc.enabled = true; + self.enc_cfg.dtvcc_extract = true; + self.settings_dtvcc.active_services_count += 1; + + if charsets.len() > i && charsets[i].is_some() { + charsets[i] + .as_ref() + .unwrap() + .clone_into(&mut self.enc_cfg.services_charsets[svc - 1]); + } + } + + if self.settings_dtvcc.active_services_count == 0 { + println!("[CEA-708] Malformed parameter: no services\n"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + } + + fn append_file_to_queue(&mut self, filename: &str) -> i32 { + if filename.is_empty() { + return 0; + } + + let new_size: usize; + + unsafe { + if self.num_input_files >= inputfile_capacity { + inputfile_capacity += 10; + } + + new_size = inputfile_capacity.try_into().unwrap_or(0); + + if self.inputfile.is_none() { + self.inputfile = Some(Vec::with_capacity(new_size)); + } + + if let Some(ref mut inputfile) = self.inputfile { + inputfile.resize(new_size, String::new()); + + let index = self.num_input_files as usize; + inputfile[index] = filename.to_string(); + } + } + + self.num_input_files += 1; + + 0 + } + + // Used for adding a sequence of files that are numbered + // Ex: filename: video1.mp4 will search for video2.mp4, video3.mp4, ... + fn add_file_sequence(&mut self, filename: &mut String) -> i32 { + filename.pop(); + let mut n: i32 = filename.len() as i32 - 1; + let bytes = filename.as_bytes(); + + // Look for the last digit in filename + while n >= 0 && !bytes[n as usize].is_ascii_digit() { + n -= 1; + } + if n == -1 { + // None. No expansion needed + return self.append_file_to_queue(filename); + } + + let mut m: i32 = n; + while m >= 0 && bytes[m as usize].is_ascii_digit() { + m -= 1; + } + m += 1; + + // Here: Significant digits go from filename[m] to filename[n] + let num = &filename[(m as usize)..=(n as usize)]; + let mut i = num.parse::().unwrap(); + + let mut temp; + let mut filename = filename.to_string(); + + loop { + if std::path::Path::new(&filename).exists() { + if self.append_file_to_queue(filename.as_str()) != 0 { + return -1; + } + temp = format!("{}", i + 1); + let temp_len = temp.len(); + let num_len = num.len(); + if temp_len > num_len { + break; + } + filename.replace_range( + (m as usize + num_len - temp_len)..(m as usize + num_len), + &temp, + ); + filename.replace_range( + (m as usize)..(m as usize + num_len - temp_len), + &"0".repeat(num_len - temp_len), + ); + } else { + break; + } + i += 1; + } + + 0 + } + + pub fn parse_parameters(&mut self, args: &Args, tlt_config: &mut CcxTeletextConfig) { + if args.stdin { + unsafe { + set_binary_mode(); + } + self.input_source = CcxDatasource::Stdin; + self.live_stream = -1; + } + if let Some(ref files) = args.inputfile { + for inputfile in files { + let plus_sign = '+'; + + let rc: i32 = if !inputfile.ends_with(plus_sign) { + self.append_file_to_queue(inputfile) + } else { + self.add_file_sequence(&mut inputfile.clone()) + }; + + if rc < 0 { + println!("Fatal: Not enough memory to parse parameters.\n"); + std::process::exit(ExitCode::NotEnoughMemory as i32); + } + } + } + + if self.num_input_files == 0 { + println!("No input file specified\n"); + std::process::exit(ExitCode::NoInputFiles as i32); + } + + #[cfg(feature = "hardsubx_ocr")] + { + if args.hardsubx { + self.hardsubx = true; + + if args.hcc { + self.hardsubx_and_common = true; + } + + if let Some(ref ocr_mode) = args.ocr_mode { + let ocr_mode = match ocr_mode.as_str() { + "simple" | "frame" => Some(HardsubxOcrMode::Frame), + "word" => Some(HardsubxOcrMode::Word), + "letter" | "symbol" => Some(HardsubxOcrMode::Letter), + _ => None, + }; + + if ocr_mode.is_none() { + println!("Invalid OCR mode"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + + self.hardsubx_ocr_mode = ocr_mode.unwrap() as i32; + } + + if let Some(ref subcolor) = args.subcolor { + match subcolor.as_str() { + "white" => { + self.hardsubx_subcolor = HardsubxColorType::White as i32; + self.hardsubx_hue = 0.0; + } + "yellow" => { + self.hardsubx_subcolor = HardsubxColorType::Yellow as i32; + self.hardsubx_hue = 60.0; + } + "green" => { + self.hardsubx_subcolor = HardsubxColorType::Green as i32; + self.hardsubx_hue = 120.0; + } + "cyan" => { + self.hardsubx_subcolor = HardsubxColorType::Cyan as i32; + self.hardsubx_hue = 180.0; + } + "blue" => { + self.hardsubx_subcolor = HardsubxColorType::Blue as i32; + self.hardsubx_hue = 240.0; + } + "magenta" => { + self.hardsubx_subcolor = HardsubxColorType::Magenta as i32; + self.hardsubx_hue = 300.0; + } + "red" => { + self.hardsubx_subcolor = HardsubxColorType::Red as i32; + self.hardsubx_hue = 0.0; + } + _ => { + let result = subcolor.parse::(); + if result.is_err() { + println!("Invalid Hue value"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + + let hue: f32 = result.unwrap(); + + if hue <= 0.0 || hue > 360.0 { + println!("Invalid Hue value"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.hardsubx_subcolor = HardsubxColorType::Custom as i32; + self.hardsubx_hue = hue; + } + } + } + + if let Some(ref value) = args.min_sub_duration { + if *value == 0.0 { + println!("Invalid minimum subtitle duration"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.hardsubx_min_sub_duration = *value; + } + + if args.detect_italics { + self.hardsubx_detect_italics = true; + } + + if let Some(ref value) = args.conf_thresh { + if !(0.0..=100.0).contains(value) { + println!("Invalid confidence threshold, valid values are between 0 & 100"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.hardsubx_conf_thresh = *value; + } + + if let Some(ref value) = args.whiteness_thresh { + if !(0.0..=100.0).contains(value) { + println!("Invalid whiteness threshold, valid values are between 0 & 100"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.hardsubx_lum_thresh = *value; + } + } + } // END OF HARDSUBX + + if args.chapters { + self.extract_chapters = true; + } + + if args.bufferinput { + self.buffer_input = true; + } + + if args.no_bufferinput { + self.buffer_input = false; + } + + if args.koc { + self.keep_output_closed = true; + } + + if args.forceflush { + self.force_flush = true; + } + + if args.append { + self.append_mode = true; + } + + if let Some(ref buffersize) = args.buffersize { + unsafe { + FILEBUFFERSIZE = atol(buffersize); + + if FILEBUFFERSIZE < 8 { + FILEBUFFERSIZE = 8; // Otherwise crashes are guaranteed at least in MythTV + } + } + } + + if args.dru { + self.settings_608.direct_rollup = 1; + } + + if args.no_fontcolor { + self.nofontcolor = true; + } + + if args.no_htmlescape { + self.nohtmlescape = true; + } + + if args.bom { + self.enc_cfg.no_bom = false; + } + if args.no_bom { + self.enc_cfg.no_bom = true; + } + + if args.sem { + self.enc_cfg.with_semaphore = true; + } + + if args.no_typesetting { + self.notypesetting = true; + } + + if args.timestamp_map { + self.timestamp_map = true; + } + + if args.es + || args.ts + || args.ps + || args.asf + || args.wtv + || args.mp4 + || args.mkv + || args.dvr_ms + || args.input.is_some() + { + self.set_input_format(args); + } + + if let Some(ref codec) = args.codec { + match codec { + Codec::Teletext => { + self.demux_cfg.codec = CcxCodeType::Teletext; + } + Codec::Dvbsub => { + self.demux_cfg.codec = CcxCodeType::Dvb; + } + } + } + + if let Some(ref codec) = args.no_codec { + match codec { + Codec::Dvbsub => { + self.demux_cfg.nocodec = CcxCodeType::Dvb; + } + Codec::Teletext => { + self.demux_cfg.nocodec = CcxCodeType::Teletext; + } + } + } + + if let Some(ref lang) = args.dvblang { + self.dvblang = Some(lang.clone()); + } + + if let Some(ref lang) = args.ocrlang { + self.ocrlang = Some(lang.clone()); + } + + if let Some(ref quant) = args.quant { + if !(0..=2).contains(quant) { + println!("Invalid quant value"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.ocr_quantmode = *quant; + } + + if args.no_spupngocr { + self.enc_cfg.nospupngocr = true; + } + + if let Some(ref oem) = args.oem { + if !(0..=2).contains(oem) { + println!("Invalid oem value"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.ocr_oem = *oem; + } + + if let Some(ref lang) = args.mkvlang { + self.mkvlang = Some(lang.to_string()); + let str = lang.as_str(); + mkvlang_params_check(str); + } + if args.srt + || args.mcc + || args.dvdraw + || args.smi + || args.sami + || args.txt + || args.ttxt + || args.webvtt + || args.null + || args.out.is_some() + { + self.set_output_format(args); + } + + if let Some(ref startcreditstext) = args.startcreditstext { + self.enc_cfg.start_credits_text.clone_from(startcreditstext); + } + + if let Some(ref startcreditsnotbefore) = args.startcreditsnotbefore { + self.enc_cfg.startcreditsnotbefore = to_ms(startcreditsnotbefore.clone().as_str()); + } + + if let Some(ref startcreditsnotafter) = args.startcreditsnotafter { + self.enc_cfg.startcreditsnotafter = to_ms(startcreditsnotafter.clone().as_str()); + } + + if let Some(ref startcreditsforatleast) = args.startcreditsforatleast { + self.enc_cfg.startcreditsforatleast = to_ms(startcreditsforatleast.clone().as_str()); + } + if let Some(ref startcreditsforatmost) = args.startcreditsforatmost { + self.enc_cfg.startcreditsforatmost = to_ms(startcreditsforatmost.clone().as_str()); + } + + if let Some(ref endcreditstext) = args.endcreditstext { + self.enc_cfg.end_credits_text.clone_from(endcreditstext); + } + + if let Some(ref endcreditsforatleast) = args.endcreditsforatleast { + self.enc_cfg.endcreditsforatleast = to_ms(endcreditsforatleast.clone().as_str()); + } + + if let Some(ref endcreditsforatmost) = args.endcreditsforatmost { + self.enc_cfg.endcreditsforatmost = to_ms(endcreditsforatmost.clone().as_str()); + } + + /* More stuff */ + if args.videoedited { + self.binary_concat = false; + } + + if args.goptime { + self.use_gop_as_pts = 1; + } + if args.no_goptime { + self.use_gop_as_pts = -1; + } + + if args.fixpadding { + self.fix_padding = true; + } + + if args.mpeg90090 { + unsafe { + MPEG_CLOCK_FREQ = 90090; + } + } + if args.no_scte20 { + self.noscte20 = true; + } + + if args.webvtt_create_css { + self.webvtt_create_css = true; + } + + if args.no_rollup { + self.no_rollup = true; + self.settings_608.no_rollup = true; + self.settings_dtvcc.no_rollup = true; + } else if args.rollup1 { + self.settings_608.force_rollup = 1; + } else if args.rollup2 { + self.settings_608.force_rollup = 2; + } else if args.rollup3 { + self.settings_608.force_rollup = 3; + } + + if args.trim { + self.enc_cfg.trim_subs = true; + } + + if let Some(ref outinterval) = args.outinterval { + self.out_interval = *outinterval; + } + + if args.segmentonkeyonly { + self.segment_on_key_frames_only = true; + self.analyze_video_stream = true; + } + + if args.gui_mode_reports { + self.gui_mode_reports = true; + } + + if args.no_progress_bar { + self.no_progress_bar = true; + } + + if args.splitbysentence { + self.enc_cfg.splitbysentence = true; + } + + if args.sentencecap { + self.enc_cfg.sentence_cap = true; + } + + if let Some(ref capfile) = args.capfile { + self.enc_cfg.sentence_cap = true; + self.sentence_cap_file = Some(capfile.clone()); + } + + if args.kf { + self.enc_cfg.filter_profanity = true; + } + + if let Some(ref profanity_file) = args.profanity_file { + self.enc_cfg.filter_profanity = true; + self.filter_profanity_file = Some(profanity_file.clone()); + } + + if let Some(ref program_number) = args.program_number { + self.demux_cfg.ts_forced_program = get_atoi_hex(program_number.as_str()); + self.demux_cfg.ts_forced_program_selected = true; + } + + if args.autoprogram { + self.demux_cfg.ts_autoprogram = true; + } + + if args.multiprogram { + self.multiprogram = true; + self.demux_cfg.ts_allprogram = true; + } + + if let Some(ref stream) = args.stream { + self.live_stream = get_atoi_hex(stream.as_str()); + } + + if let Some(ref defaultcolor) = args.defaultcolor { + unsafe { + if defaultcolor.len() != 7 || !defaultcolor.starts_with('#') { + println!("Invalid default color"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + USERCOLOR_RGB.clone_from(defaultcolor); + self.settings_608.default_color = CcxDecoder608ColorCode::Userdefined; + } + } + + if let Some(ref delay) = args.delay { + self.subs_delay = *delay; + } + + if let Some(ref screenfuls) = args.screenfuls { + self.settings_608.screens_to_process = get_atoi_hex(screenfuls.as_str()); + } + + if let Some(ref startat) = args.startat { + self.extraction_start = to_ms(startat.clone().as_str()); + } + if let Some(ref endat) = args.endat { + self.extraction_end = to_ms(endat.clone().as_str()); + } + + if args.cc2 { + self.cc_channel = 2; + } + + if let Some(ref extract) = args.output_field { + if *extract == "1" || *extract == "2" { + self.extract = get_atoi_hex(extract); + } else if *extract == "both" { + self.extract = 12; + } else { + println!("Invalid output field"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + self.is_608_enabled = true; + } + + if args.stdout { + if self.messages_target == 1 { + self.messages_target = 2; + } + self.cc_to_stdout = true; + } + + if args.pesheader { + self.pes_header_to_stdout = true; + } + + if args.debugdvdsub { + self.debug_mask = CcxDebugMessageTypes::Dvb; + } + + if args.ignoreptsjumps { + self.ignore_pts_jumps = true; + } + + if args.fixptsjumps { + self.ignore_pts_jumps = false; + } + + if args.quiet { + self.messages_target = 0; + } + + if args.debug { + self.debug_mask = CcxDebugMessageTypes::Verbose; + } + + if args.eia608 { + self.debug_mask = CcxDebugMessageTypes::Decoder608; + } + + if args.deblev { + self.debug_mask = CcxDebugMessageTypes::Levenshtein; + } + + if args.no_levdist { + self.dolevdist = 0; + } + + if let Some(ref levdistmincnt) = args.levdistmincnt { + self.levdistmincnt = get_atoi_hex(levdistmincnt.as_str()); + } + if let Some(ref levdistmaxpct) = args.levdistmaxpct { + self.levdistmaxpct = get_atoi_hex(levdistmaxpct.as_str()); + } + + if args.eia708 { + self.debug_mask = CcxDebugMessageTypes::Parse; + } + + if args.goppts { + self.debug_mask = CcxDebugMessageTypes::Time; + } + + if args.vides { + self.debug_mask = CcxDebugMessageTypes::Vides; + self.analyze_video_stream = true; + } + + if args.analyzevideo { + self.analyze_video_stream = true; + } + + if args.xds { + self.transcript_settings.xds = true; + } + if args.xdsdebug { + self.transcript_settings.xds = true; + self.debug_mask = CcxDebugMessageTypes::DecoderXds; + } + + if args.parsedebug { + self.debug_mask = CcxDebugMessageTypes::Parse; + } + + if args.parse_pat { + self.debug_mask = CcxDebugMessageTypes::Pat; + } + + if args.parse_pmt { + self.debug_mask = CcxDebugMessageTypes::Pmt; + } + + if args.dumpdef { + self.debug_mask = CcxDebugMessageTypes::Dumpdef; + } + + if args.investigate_packets { + self.investigate_packets = true; + } + + if args.cbraw { + self.debug_mask = CcxDebugMessageTypes::Cbraw; + } + + if args.tverbose { + self.debug_mask = CcxDebugMessageTypes::Teletext; + tlt_config.verbose = true; + } + + #[cfg(feature = "enable_sharing")] + { + if args.sharing_debug { + self.debug_mask = CcxDebugMessageTypes::Share; + tlt_config.verbose = true; + } + } + + if args.fullbin { + self.fullbin = true; + } + + if args.no_sync { + self.nosync = true; + } + + if args.hauppauge { + self.hauppauge_mode = true; + } + + if args.mp4vidtrack { + self.mp4vidtrack = true; + } + + if args.unicode { + self.enc_cfg.encoding = CcxEncodingType::Unicode; + } + + if args.utf8 { + self.enc_cfg.encoding = CcxEncodingType::Utf8; + } + + if args.latin1 { + self.enc_cfg.encoding = CcxEncodingType::Latin1; + } + if args.usepicorder { + self.usepicorder = true; + } + + if args.myth { + self.auto_myth = 1; + } + if args.no_myth { + self.auto_myth = 0; + } + + if args.wtvconvertfix { + self.wtvconvertfix = true; + } + + if args.wtvmpeg2 { + self.wtvmpeg2 = true; + } + + if let Some(ref output) = args.output { + self.output_filename = Some(output.clone()); + } + + if let Some(ref service) = args.cea708services { + self.is_708_enabled = true; + self.parse_708_services(service); + } + + if let Some(ref datapid) = args.datapid { + self.demux_cfg.ts_cappids[self.demux_cfg.nb_ts_cappid as usize] = + get_atoi_hex(datapid.as_str()); + self.demux_cfg.nb_ts_cappid += 1; + } + + if let Some(ref datastreamtype) = args.datastreamtype { + self.demux_cfg.ts_datastreamtype = datastreamtype.clone().parse().unwrap(); + } + + if let Some(ref streamtype) = args.streamtype { + self.demux_cfg.ts_forced_streamtype = streamtype.clone().parse().unwrap(); + } + + if let Some(ref tpage) = args.tpage { + tlt_config.page = get_atoi_hex(tpage.as_str()); + tlt_config.user_page = tlt_config.page; + } + + // Red Hen/ UCLA Specific stuff + if args.ucla { + self.ucla = true; + self.millis_separator = '.'; + self.enc_cfg.no_bom = true; + + if !self.transcript_settings.is_final { + self.transcript_settings.show_start_time = true; + self.transcript_settings.show_end_time = true; + self.transcript_settings.show_cc = true; + self.transcript_settings.show_mode = true; + self.transcript_settings.relative_timestamp = false; + self.transcript_settings.is_final = true; + } + } + + if args.latrusmap { + tlt_config.latrusmap = true; + } + + if args.tickertext { + self.tickertext = true; + } + + if args.lf { + self.enc_cfg.line_terminator_lf = true; + } + + if args.df { + self.enc_cfg.force_dropframe = true; + } + + if args.no_autotimeref { + self.noautotimeref = true; + } + + if args.autodash { + self.enc_cfg.autodash = true; + } + + if let Some(ref xmltv) = args.xmltv { + self.xmltv = get_atoi_hex(xmltv.as_str()); + } + + if let Some(ref xmltvliveinterval) = args.xmltvliveinterval { + self.xmltvliveinterval = get_atoi_hex(xmltvliveinterval.as_str()); + } + + if let Some(ref xmltvoutputinterval) = args.xmltvoutputinterval { + self.xmltvoutputinterval = get_atoi_hex(xmltvoutputinterval.as_str()); + } + if let Some(ref xmltvonlycurrent) = args.xmltvonlycurrent { + self.xmltvonlycurrent = get_atoi_hex(xmltvonlycurrent.as_str()); + } + + if let Some(ref unixts) = args.unixts { + let mut t = get_atoi_hex(unixts.as_str()); + + if t == 0 { + t = OffsetDateTime::now_utc().unix_timestamp() as u64; + } + unsafe { + UTC_REFVALUE = t; + } + self.noautotimeref = true; + } + + if args.sects { + self.date = CcxOutputDateFormat::Seconds; + } + + if args.datets { + self.date = CcxOutputDateFormat::Date; + } + + if args.teletext { + self.demux_cfg.codec = CcxCodeType::Teletext; + } + + if args.no_teletext { + self.demux_cfg.nocodec = CcxCodeType::Teletext; + } + + if let Some(ref customtxt) = args.customtxt { + if customtxt.to_string().len() == 7 { + if self.date == CcxOutputDateFormat::None { + self.date = CcxOutputDateFormat::HhMmSsMs; + } + + if !self.transcript_settings.is_final { + let chars = format!("{}", customtxt).chars().collect::>(); + self.transcript_settings.show_start_time = chars[0] == '1'; + self.transcript_settings.show_end_time = chars[1] == '1'; + self.transcript_settings.show_mode = chars[2] == '1'; + self.transcript_settings.show_cc = chars[3] == '1'; + self.transcript_settings.relative_timestamp = chars[4] == '1'; + self.transcript_settings.xds = chars[5] == '1'; + self.transcript_settings.use_colors = chars[6] == '1'; + } + } else { + println!("Invalid customtxt value. It must be 7 digits long"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + } + + // Network stuff + if let Some(ref udp) = args.udp { + if let Some(at) = udp.find('@') { + let addr = &udp[0..at]; + let port = &udp[at + 1..]; + + self.udpsrc = Some(udp.clone()); + self.udpaddr = Some(addr.to_owned()); + self.udpport = port.parse().unwrap(); + } else if let Some(colon) = udp.find(':') { + let addr = &udp[0..colon]; + let port = get_atoi_hex(&udp[colon + 1..]); + + self.udpsrc = Some(udp.clone()); + self.udpaddr = Some(addr.to_owned()); + self.udpport = port; + } else { + self.udpaddr = None; + self.udpport = udp.parse().unwrap(); + } + + self.input_source = CcxDatasource::Network; + } + + if let Some(ref addr) = args.sendto { + self.send_to_srv = true; + self.set_output_format_type(OutFormat::Bin); + + self.xmltv = 2; + self.xmltvliveinterval = 2; + let mut _addr: String = addr.to_string(); + + if let Some(saddr) = addr.strip_prefix('[') { + _addr = saddr.to_string(); + + let result = _addr.find(']'); + if result.is_none() { + println!("Wrong address format, for IPv6 use [address]:port\n"); + std::process::exit(ExitCode::IncompatibleParameters as i32); + } + let mut br = result.unwrap(); + _addr = _addr.replace(']', ""); + + self.srv_addr = Some(_addr.clone()); + + br += 1; + if !_addr[br..].is_empty() { + self.srv_port = Some(_addr[br..].parse().unwrap()); + } + } + + self.srv_addr = Some(_addr.clone()); + + let colon = _addr.find(':').unwrap(); + _addr = _addr.replace(':', ""); + self.srv_port = Some(_addr[(colon + 1)..].parse().unwrap()); + } + + if let Some(ref tcp) = args.tcp { + self.tcpport = Some(*tcp); + self.input_source = CcxDatasource::Tcp; + self.set_input_format_type(InFormat::Bin); + } + + if let Some(ref tcppassworrd) = args.tcp_password { + self.tcp_password = Some(tcppassworrd.to_string()); + } + + if let Some(ref tcpdesc) = args.tcp_description { + self.tcp_desc = Some(tcpdesc.to_string()); + } + + if let Some(ref font) = args.font { + self.enc_cfg.render_font = font.to_string(); + } + + if let Some(ref italics) = args.italics { + self.enc_cfg.render_font_italics = italics.to_string(); + } + + #[cfg(feature = "with_libcurl")] + if let Some(ref curlposturl) = args.curlposturl { + self.curlposturl = Some(curlposturl.to_string()); + } + + #[cfg(feature = "enable_sharing")] + { + if args.enable_sharing { + self.sharing_enabled = true; + } + + if let Some(ref sharingurl) = args.sharing_url { + self.sharing_url = Some(sharingurl.to_string()); + } + + if let Some(ref translate) = args.translate { + self.translate_enabled = true; + self.sharing_enabled = true; + self.translate_langs = Some(translate.to_string()); + } + + if let Some(ref translateauth) = args.translate_auth { + self.translate_key = Some(translateauth.to_string()); + } + } + + if self.demux_cfg.auto_stream == CcxStreamMode::Mp4 + && self.input_source == CcxDatasource::Stdin + { + println!("MP4 requires an actual file, it's not possible to read from a stream, including stdin."); + std::process::exit(ExitCode::IncompatibleParameters as i32); + } + + if self.extract_chapters { + println!("Request to extract chapters recieved."); + println!("Note that this must only be used with MP4 files,"); + println!("for other files it will simply generate subtitles file.\n"); + } + + if self.gui_mode_reports { + self.no_progress_bar = true; + // Do it as soon as possible, because it something fails we might not have a chance + self.activity_report_version(); + } + + if self.enc_cfg.sentence_cap { + unsafe { + CAPITALIZATION_LIST = get_vector_words(&CAPITALIZED_BUILTIN); + if let Some(ref sentence_cap_file) = self.sentence_cap_file { + let result = + process_word_file(sentence_cap_file, addr_of_mut!(CAPITALIZATION_LIST)); + + if result.is_err() { + println!("There was an error processing the capitalization file.\n"); + std::process::exit(ExitCode::ErrorInCapitalizationFile as i32); + } + } + } + } + if self.enc_cfg.filter_profanity { + unsafe { + PROFANE = get_vector_words(&PROFANE_BUILTIN); + if let Some(ref profanityfile) = self.filter_profanity_file { + let result = process_word_file(profanityfile.as_str(), addr_of_mut!(PROFANE)); + + if result.is_err() { + println!("There was an error processing the profanity file.\n"); + std::process::exit(ExitCode::ErrorInCapitalizationFile as i32); + } + } + } + } + + if self.demux_cfg.ts_forced_program != -1 { + self.demux_cfg.ts_forced_program_selected = true; + } + + // Init telexcc redundant options + tlt_config.dolevdist = self.dolevdist; + tlt_config.levdistmincnt = self.levdistmincnt; + tlt_config.levdistmaxpct = self.levdistmaxpct; + tlt_config.extraction_start = self.extraction_start.clone(); + tlt_config.extraction_end = self.extraction_end.clone(); + tlt_config.write_format = self.write_format; + tlt_config.gui_mode_reports = self.gui_mode_reports; + tlt_config.date_format = self.date; + tlt_config.noautotimeref = self.noautotimeref; + tlt_config.send_to_srv = self.send_to_srv; + tlt_config.nofontcolor = self.nofontcolor; + tlt_config.nohtmlescape = self.nohtmlescape; + tlt_config.millis_separator = self.millis_separator; + + // teletext page number out of range + if tlt_config.page != 0 && (tlt_config.page < 100 || tlt_config.page > 899) { + println!("Teletext page number out of range (100-899)"); + std::process::exit(ExitCode::NotClassified as i32); + } + + if self.num_input_files == 0 && self.input_source == CcxDatasource::File { + std::process::exit(ExitCode::NoInputFiles as i32); + } + + if self.num_input_files != 0 && self.live_stream != 0 { + println!("Live stream mode only supports one input file"); + std::process::exit(ExitCode::TooManyInputFiles as i32); + } + + if self.num_input_files != 0 && self.input_source == CcxDatasource::Network { + println!("UDP mode is not compatible with input files"); + std::process::exit(ExitCode::TooManyInputFiles as i32); + } + + if self.input_source == CcxDatasource::Network || self.input_source == CcxDatasource::Tcp { + // TODO(prateekmedia): Check why we use ccx_options instead of opts + // currently using same] + self.buffer_input = true; + } + + if self.num_input_files != 0 && self.input_source == CcxDatasource::Tcp { + println!("TCP mode is not compatible with input files"); + std::process::exit(ExitCode::TooManyInputFiles as i32); + } + + if self.demux_cfg.auto_stream == CcxStreamMode::McpoodlesRaw + && self.write_format == CcxOutputFormat::Raw + { + println!("-in=raw can only be used if the output is a subtitle file."); + std::process::exit(ExitCode::IncompatibleParameters as i32); + } + + if self.demux_cfg.auto_stream == CcxStreamMode::Rcwt + && self.write_format == CcxOutputFormat::Rcwt + && self.output_filename.is_none() + { + println!("CCExtractor's binary format can only be used simultaneously for input and\noutput if the output file name is specified given with -o.\n"); + std::process::exit(ExitCode::IncompatibleParameters as i32); + } + + if self.write_format != CcxOutputFormat::Dvdraw + && self.cc_to_stdout + && self.extract != 0 + && self.extract == 12 + { + println!( + "You can't extract both fields to stdout at the same time in broadcast mode.\n", + ); + std::process::exit(ExitCode::IncompatibleParameters as i32); + } + + if self.write_format == CcxOutputFormat::Spupng && self.cc_to_stdout { + println!("You cannot use --out=spupng with -stdout.\n"); + std::process::exit(ExitCode::IncompatibleParameters as i32); + } + + if self.write_format == CcxOutputFormat::Webvtt + && self.enc_cfg.encoding != CcxEncodingType::Utf8 + { + self.enc_cfg.encoding = CcxEncodingType::Utf8; + println!("Note: Output format is WebVTT, forcing UTF-8"); + std::process::exit(ExitCode::IncompatibleParameters as i32); + } + + // Check WITH_LIBCURL + #[cfg(feature = "with_libcurl")] + { + if self.write_format == CcxOutputFormat::Curl && self.curlposturl.is_none() { + println!("You must pass a URL (--curlposturl) if output format is curl"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + if self.write_format != CcxOutputFormat::Curl && self.curlposturl.is_some() { + println!("--curlposturl requires that the format is curl"); + std::process::exit(ExitCode::MalformedParameter as i32); + } + } + + // Initialize some Encoder Configuration + self.enc_cfg.extract = self.extract; + if self.num_input_files > 0 { + self.enc_cfg.multiple_files = true; + self.enc_cfg.first_input_file = self.inputfile.as_ref().unwrap()[0].to_string(); + } + self.enc_cfg.cc_to_stdout = self.cc_to_stdout; + self.enc_cfg.write_format = self.write_format; + self.enc_cfg.send_to_srv = self.send_to_srv; + self.enc_cfg.date_format = self.date; + self.enc_cfg.transcript_settings = self.transcript_settings.clone(); + self.enc_cfg.millis_separator = self.millis_separator; + self.enc_cfg.no_font_color = self.nofontcolor; + self.enc_cfg.force_flush = self.force_flush; + self.enc_cfg.append_mode = self.append_mode; + self.enc_cfg.ucla = self.ucla; + self.enc_cfg.no_type_setting = self.notypesetting; + self.enc_cfg.subs_delay = self.subs_delay; + self.enc_cfg.gui_mode_reports = self.gui_mode_reports; + + if self.enc_cfg.render_font.is_empty() { + self.enc_cfg.render_font = DEFAULT_FONT_PATH.to_string(); + } + + if self.enc_cfg.render_font_italics.is_empty() { + self.enc_cfg.render_font_italics = DEFAULT_FONT_PATH_ITALICS.to_string(); + } + + if self.output_filename.is_some() && !self.multiprogram { + self.enc_cfg.output_filename = self.output_filename.clone().unwrap(); + } + + if !self.is_608_enabled && !self.is_708_enabled { + // If nothing is selected then extract both 608 and 708 subs + // 608 field 1 is enabled by default + // Enable 708 subs extraction + self.parse_708_services("all"); + } else if !self.is_608_enabled && self.is_708_enabled { + // Extract only 708 subs + // 608 field 1 is enabled by default, disable it + self.extract = 0; + self.enc_cfg.extract_only_708 = true; + } + + // Check WITH_LIBCURL + #[cfg(feature = "with_libcurl")] + { + self.enc_cfg.curlposturl = self.curlposturl.clone(); + } + } +} +#[cfg(test)] +pub mod tests { + use crate::{args::*, common::*, parser::*}; + use clap::Parser; + + #[no_mangle] + pub extern "C" fn set_binary_mode() {} + pub static mut MPEG_CLOCK_FREQ: u64 = 0; + + fn parse_args(args: &[&str]) -> (CcxOptions, CcxTeletextConfig) { + let mut common_args = vec!["./ccextractor", "input_file"]; + common_args.extend_from_slice(args); + let args = Args::try_parse_from(common_args).expect("Failed to parse arguments"); + let mut options = CcxOptions { + ..Default::default() + }; + let mut tlt_config = CcxTeletextConfig { + ..Default::default() + }; + + options.parse_parameters(&args, &mut tlt_config); + (options, tlt_config) + } + + #[test] + fn broken_1() { + let (options, _) = parse_args(&["--autoprogram", "--out", "srt", "--latin1"]); + + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn broken_2() { + let (options, _) = parse_args(&["--autoprogram", "--out", "sami", "--latin1"]); + + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.write_format, CcxOutputFormat::Sami); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn broken_3() { + let (options, _) = parse_args(&[ + "--autoprogram", + "--out", + "ttxt", + "--latin1", + "--ucla", + "--xds", + ]); + + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + assert!(options.ucla); + assert!(options.transcript_settings.xds); + } + + #[test] + fn broken_4() { + let (options, _) = parse_args(&["--out", "ttxt", "--latin1"]); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn broken_5() { + let (options, _) = parse_args(&["--out", "srt", "--latin1"]); + assert_eq!(options.write_format, CcxOutputFormat::Srt); + + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn cea708_1() { + let (options, _) = + parse_args(&["--service", "1", "--out", "txt", "--no-bom", "--no-rollup"]); + assert!(options.is_708_enabled); + + assert!(options.enc_cfg.no_bom); + assert!(options.no_rollup); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + } + + #[test] + fn cea708_2() { + let (options, _) = parse_args(&[ + "--service", + "1,2[UTF-8],3[EUC-KR],54", + "--out", + "txt", + "--no-rollup", + ]); + + assert!(options.is_708_enabled); + assert!(options.no_rollup); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.enc_cfg.services_charsets[1], "UTF-8"); + assert_eq!(options.enc_cfg.services_charsets[2], "EUC-KR"); + } + + #[test] + fn cea708_3() { + let (options, _) = parse_args(&["--service", "all[EUC-KR]", "--no-rollup"]); + assert!(options.is_708_enabled); + + assert!(options.no_rollup); + assert_eq!(options.enc_cfg.all_services_charset, "EUC-KR"); + } + + #[test] + fn dvb_1() { + let (options, _) = parse_args(&[ + "--autoprogram", + "--out", + "srt", + "--latin1", + "--teletext", + "--datapid", + "5603", + ]); + + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.demux_cfg.ts_cappids[0], 5603); + assert_eq!(options.demux_cfg.nb_ts_cappid, 1); + assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn dvb_2() { + let (options, _) = parse_args(&["--stdout", "--quiet", "--no-fontcolor"]); + assert!(options.cc_to_stdout); + + assert_eq!(options.messages_target, 0); + assert_eq!(options.nofontcolor, true); + } + + #[test] + fn dvd_1() { + let (options, _) = parse_args(&["--autoprogram", "--out", "ttxt", "--latin1"]); + assert!(options.demux_cfg.ts_autoprogram); + + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn dvr_ms_1() { + let (options, _) = parse_args(&[ + "--wtvconvertfix", + "--autoprogram", + "--out", + "srt", + "--latin1", + ]); + + assert!(options.wtvconvertfix); + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn general_1() { + let (options, _) = parse_args(&[ + "--ucla", + "--autoprogram", + "--out", + "ttxt", + "--latin1", + "--output-field", + "2", + ]); + + assert!(options.ucla); + assert!(options.demux_cfg.ts_autoprogram); + assert!(options.is_608_enabled); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.extract, 2); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn general_2() { + let (options, _) = + parse_args(&["--autoprogram", "--out", "bin", "--latin1", "--sentencecap"]); + assert!(options.demux_cfg.ts_autoprogram); + + assert!(options.enc_cfg.sentence_cap); + assert_eq!(options.write_format, CcxOutputFormat::Rcwt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn haup_1() { + let (options, _) = parse_args(&[ + "--hauppauge", + "--ucla", + "--autoprogram", + "--out", + "ttxt", + "--latin1", + ]); + + assert!(options.ucla); + assert!(options.hauppauge_mode); + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn mp4_1() { + let (options, _) = parse_args(&["--input", "mp4", "--out", "srt", "--latin1"]); + assert_eq!(options.demux_cfg.auto_stream, CcxStreamMode::Mp4); + + assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn mp4_2() { + let (options, _) = parse_args(&["--autoprogram", "--out", "srt", "--bom", "--latin1"]); + assert!(options.demux_cfg.ts_autoprogram); + + assert!(!options.enc_cfg.no_bom); + assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn nocc_1() { + let (options, _) = parse_args(&[ + "--autoprogram", + "--out", + "ttxt", + "--mp4vidtrack", + "--latin1", + ]); + + assert!(options.demux_cfg.ts_autoprogram); + assert!(options.mp4vidtrack); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn nocc_2() { + let (options, _) = parse_args(&[ + "--autoprogram", + "--out", + "ttxt", + "--latin1", + "--ucla", + "--xds", + ]); + + assert!(options.demux_cfg.ts_autoprogram); + assert!(options.ucla); + assert!(options.transcript_settings.xds); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn options_1() { + let (options, _) = parse_args(&["--input", "ts"]); + + assert_eq!(options.demux_cfg.auto_stream, CcxStreamMode::Transport); + } + + #[test] + fn options_2() { + let (options, _) = parse_args(&["--out", "dvdraw"]); + assert_eq!(options.write_format, CcxOutputFormat::Dvdraw); + } + + #[test] + fn options_3() { + let (options, _) = parse_args(&["--goptime"]); + assert_eq!(options.use_gop_as_pts, 1); + } + + #[test] + fn options_4() { + let (options, _) = parse_args(&["--no-goptime"]); + assert_eq!(options.use_gop_as_pts, -1); + } + + #[test] + fn options_5() { + let (options, _) = parse_args(&["--fixpadding"]); + assert!(options.fix_padding); + } + + #[test] + fn options_6() { + let (_, _) = parse_args(&["--90090"]); + + unsafe { + assert_eq!(MPEG_CLOCK_FREQ as i64, 90090); + } + } + + #[test] + fn options_7() { + let (options, _) = parse_args(&["--myth"]); + assert_eq!(options.auto_myth, 1); + } + + #[test] + fn options_8() { + let (options, _) = parse_args(&["--program-number", "1"]); + assert_eq!(options.demux_cfg.ts_forced_program, 1); + } + + #[test] + fn options_9() { + let (options, _) = parse_args(&[ + "--datastreamtype", + "2", + "--streamtype", + "2", + "--no-autotimeref", + ]); + + assert!(options.noautotimeref); + assert_eq!(options.demux_cfg.ts_datastreamtype, 2); + assert_eq!(options.demux_cfg.ts_forced_streamtype, 2); + } + #[test] + fn options_10() { + let (options, _) = parse_args(&["--unicode", "--no-typesetting"]); + assert!(options.notypesetting); + + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Unicode); + } + + #[test] + fn options_11() { + let (options, _) = parse_args(&["--utf8", "--trim"]); + assert!(options.enc_cfg.trim_subs); + + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Utf8); + } + + #[test] + fn options_12() { + let (options, _) = parse_args(&["--capfile", "Cargo.toml"]); + + assert!(options.enc_cfg.sentence_cap); + assert_eq!(options.sentence_cap_file.unwrap(), "Cargo.toml"); + } + + #[test] + fn options_13() { + let (options, _) = parse_args(&["--unixts", "5", "--out", "txt"]); + + unsafe { + assert_eq!(UTC_REFVALUE, 5); + } + + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + } + + #[test] + fn options_14() { + let (options, _) = parse_args(&["--datets", "--out", "txt"]); + + assert_eq!(options.date, CcxOutputDateFormat::Date); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + } + + #[test] + fn options_15() { + let (options, _) = parse_args(&["--sects", "--out", "txt"]); + + assert_eq!(options.date, CcxOutputDateFormat::Seconds); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + } + + #[test] + fn options_16() { + let (options, _) = parse_args(&["--lf", "--out", "txt"]); + + assert!(options.enc_cfg.line_terminator_lf); + assert_eq!(options.write_format, CcxOutputFormat::Transcript); + } + + #[test] + fn options_17() { + let (options, _) = parse_args(&["--autodash", "--trim"]); + + assert!(options.enc_cfg.autodash); + assert!(options.enc_cfg.trim_subs); + } + + #[test] + fn options_18() { + let (options, _) = parse_args(&["--bufferinput"]); + + assert!(options.buffer_input); + } + + #[test] + fn options_19() { + let (options, _) = parse_args(&["--no-bufferinput"]); + + assert!(!options.buffer_input); + } + + #[test] + fn options_20() { + let (_, _) = parse_args(&["--buffersize", "1M"]); + + unsafe { + assert_eq!(FILEBUFFERSIZE, 1024 * 1024); + } + } + + #[test] + fn options_21() { + let (options, _) = parse_args(&["--dru"]); + + assert_eq!(options.settings_608.direct_rollup, 1); + } + + #[test] + fn options_22() { + let (options, _) = parse_args(&["--no-rollup"]); + + assert!(options.no_rollup); + } + + #[test] + fn options_23() { + let (options, _) = parse_args(&["--ru1"]); + + assert_eq!(options.settings_608.force_rollup, 1); + } + + #[test] + fn options_24() { + let (options, _) = parse_args(&["--delay", "200"]); + + assert_eq!(options.subs_delay, 200); + } + + #[test] + fn options_25() { + let (options, _) = parse_args(&["--startat", "4", "--endat", "7"]); + + assert_eq!(options.extraction_start.ss, 4); + } + + #[test] + fn options_26() { + let (options, _) = parse_args(&["--no-codec", "dvbsub"]); + + assert_eq!(options.demux_cfg.nocodec, CcxCodeType::Dvb); + } + + #[test] + fn options_27() { + let (options, _) = parse_args(&["--debug"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Verbose); + } + + #[test] + fn options_28() { + let (options, _) = parse_args(&["--608"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Decoder608); + } + + #[test] + fn options_29() { + let (options, _) = parse_args(&["--708"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Parse); + } + + #[test] + fn options_30() { + let (options, _) = parse_args(&["--goppts"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Time); + } + + #[test] + fn options_31() { + let (options, _) = parse_args(&["--xdsdebug"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::DecoderXds); + } + + #[test] + fn options_32() { + let (options, _) = parse_args(&["--vides"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Vides); + } + + #[test] + fn options_33() { + let (options, _) = parse_args(&["--cbraw"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Cbraw); + } + + #[test] + fn options_34() { + let (options, _) = parse_args(&["--no-sync"]); + + assert!(options.nosync); + } + + #[test] + fn options_35() { + let (options, _) = parse_args(&["--fullbin"]); + + assert!(options.fullbin); + } + + #[test] + fn options_36() { + let (options, _) = parse_args(&["--parsedebug"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Parse); + } + + #[test] + fn options_37() { + let (options, _) = parse_args(&["--parsePAT"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Pat); + } + + #[test] + fn options_38() { + let (options, _) = parse_args(&["--parsePMT"]); + + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Pmt); + } + + #[test] + fn options_39() { + let (options, _) = parse_args(&["--investigate-packets"]); + + assert!(options.investigate_packets); + } + + #[test] + fn options_40() { + let (options, _) = parse_args(&["--mp4vidtrack"]); + + assert!(options.mp4vidtrack); + } + + #[test] + fn options_41() { + let (options, _) = parse_args(&["--wtvmpeg2"]); + + assert!(options.wtvmpeg2); + } + + #[test] + fn options_42() { + let (options, _) = parse_args(&["--hauppauge"]); + + assert!(options.hauppauge_mode); + } + + #[test] + fn options_43() { + let (options, _) = parse_args(&["--xmltv", "1", "--out", "null"]); + + assert_eq!(options.xmltv, 1); + assert_eq!(options.write_format, CcxOutputFormat::Null); + } + + #[test] + fn options_44() { + let (options, _) = parse_args(&["--codec", "dvbsub", "--out", "spupng"]); + + assert_eq!(options.demux_cfg.codec, CcxCodeType::Dvb); + assert_eq!(options.write_format, CcxOutputFormat::Spupng); + } + + #[test] + fn options_45() { + let (options, _) = parse_args(&[ + "--startcreditsnotbefore", + "1", + "--startcreditstext", + "CCextractor Start credit Testing", + ]); + + assert_eq!(options.enc_cfg.startcreditsnotbefore.ss, 1); + assert_eq!( + options.enc_cfg.start_credits_text, + "CCextractor Start credit Testing" + ); + } + + #[test] + fn options_46() { + let (options, _) = parse_args(&[ + "--startcreditsnotafter", + "2", + "--startcreditstext", + "CCextractor Start credit Testing", + ]); + + assert_eq!(options.enc_cfg.startcreditsnotafter.ss, 2); + assert_eq!( + options.enc_cfg.start_credits_text, + "CCextractor Start credit Testing" + ); + } + + #[test] + fn options_47() { + let (options, _) = parse_args(&[ + "--startcreditsforatleast", + "1", + "--startcreditstext", + "CCextractor Start credit Testing", + ]); + + assert_eq!(options.enc_cfg.startcreditsforatleast.ss, 1); + assert_eq!( + options.enc_cfg.start_credits_text, + "CCextractor Start credit Testing" + ); + } + + #[test] + fn options_48() { + let (options, _) = parse_args(&[ + "--startcreditsforatmost", + "2", + "--startcreditstext", + "CCextractor Start credit Testing", + ]); + + assert_eq!(options.enc_cfg.startcreditsforatmost.ss, 2); + assert_eq!( + options.enc_cfg.start_credits_text, + "CCextractor Start credit Testing" + ); + } + + #[test] + fn options_49() { + let (options, _) = parse_args(&[ + "--endcreditsforatleast", + "3", + "--endcreditstext", + "CCextractor Start credit Testing", + ]); + + assert_eq!(options.enc_cfg.endcreditsforatleast.ss, 3); + assert_eq!( + options.enc_cfg.end_credits_text, + "CCextractor Start credit Testing" + ); + } + + #[test] + fn options_50() { + let (options, _) = parse_args(&[ + "--endcreditsforatmost", + "2", + "--endcreditstext", + "CCextractor Start credit Testing", + ]); + + assert_eq!(options.enc_cfg.endcreditsforatmost.ss, 2); + assert_eq!( + options.enc_cfg.end_credits_text, + "CCextractor Start credit Testing" + ); + } + + #[test] + fn options_51() { + let (options, tlt_config) = parse_args(&["--tverbose"]); + + assert!(tlt_config.verbose); + assert_eq!(options.debug_mask, CcxDebugMessageTypes::Teletext); + } + + #[test] + fn teletext_1() { + let (options, _) = parse_args(&[ + "--autoprogram", + "--out", + "srt", + "--latin1", + "--datapid", + "2310", + ]); + + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.demux_cfg.ts_cappids[0], 2310); + assert_eq!(options.demux_cfg.nb_ts_cappid, 1); + assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } + + #[test] + fn teletext_2() { + let (options, tlt_config) = parse_args(&[ + "--autoprogram", + "--out", + "srt", + "--latin1", + "--teletext", + "--tpage", + "398", + ]); + + assert!(options.demux_cfg.ts_autoprogram); + assert_eq!(options.demux_cfg.codec, CcxCodeType::Teletext); + assert_eq!(tlt_config.page, 398); + assert_eq!(options.write_format, CcxOutputFormat::Srt); + assert_eq!(options.enc_cfg.encoding, CcxEncodingType::Latin1); + } +} diff --git a/src/rust/src/utils.rs b/src/rust/src/utils.rs index 7f26dd57b..39067251d 100644 --- a/src/rust/src/utils.rs +++ b/src/rust/src/utils.rs @@ -15,7 +15,43 @@ pub fn is_false>(val: T) -> bool { /// # Safety /// The pointer returned has to be deallocated using from_raw() at some point pub unsafe fn string_to_c_char(a: &str) -> *mut ::std::os::raw::c_char { + if a.is_empty() { + return string_null(); + } let s = ffi::CString::new(a).unwrap(); s.into_raw() } + +/// # Safety +/// The pointer returned has to be deallocated using from_raw() at some point +pub fn string_null() -> *mut c_char { + std::ptr::null_mut() +} + +use std::ffi::CString; +use std::os::raw::c_char; + +pub fn string_to_c_chars(strs: Vec) -> *mut *mut c_char { + let mut cstr_vec: Vec = vec![]; + for s in strs { + let cstr = CString::new(s.as_str()).unwrap(); + cstr_vec.push(cstr); + } + cstr_vec.shrink_to_fit(); + + let mut c_char_vec: Vec<*const c_char> = vec![]; + for s in &cstr_vec { + if s.as_bytes().is_empty() { + c_char_vec.push(string_null()); + continue; + } + c_char_vec.push(s.as_ptr()); + } + let ptr = c_char_vec.as_ptr(); + + std::mem::forget(cstr_vec); + std::mem::forget(c_char_vec); + + ptr as *mut *mut c_char +} diff --git a/src/rust/wrapper.h b/src/rust/wrapper.h index b429ed1e3..7205fac4a 100644 --- a/src/rust/wrapper.h +++ b/src/rust/wrapper.h @@ -1,3 +1,6 @@ +#include "../lib_ccx/ccx_common_option.h" +#include "../lib_ccx/ccx_common_constants.h" +#include "../lib_ccx/ccx_common_timing.h" #include "../lib_ccx/ccx_decoders_708.h" #include "../lib_ccx/ccx_decoders_common.h" #include "../lib_ccx/ccx_dtvcc.h" diff --git a/windows/.gitignore b/windows/.gitignore index 6b7a5ab6f..d79cf0674 100644 --- a/windows/.gitignore +++ b/windows/.gitignore @@ -1,2 +1,2 @@ -ccextractor +ccextractor vcpkg_installed \ No newline at end of file diff --git a/windows/ccextractor.vcxproj b/windows/ccextractor.vcxproj index 74f29588f..296793c4e 100644 --- a/windows/ccextractor.vcxproj +++ b/windows/ccextractor.vcxproj @@ -1,346 +1,346 @@ - - - - - Debug-Full - x64 - - - Release-Full - x64 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - {0F0063C4-BCBC-4379-A6D5-84A5669C940A} - ccextractor - Win32Proj - 10.0.22621.0 - - - true - x64-windows-static - true - - - - Application - v143 - - - Application - v143 - - - - - - - - - - - - - <_ProjectFileVersion>12.0.21005.1 - - - true - ccextractorwinfull - - $(ProjectDir)\libs\include;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; - "C:\Program Files\GPAC\sdk\include"; - $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\zlib;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\libpng16;$(IncludePath) - - $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; - "C:\Program Files\GPAC\sdk\lib";$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) - - - false - ccextractorwinfull - - $(ProjectDir)\libs\include;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; - "C:\Program Files\GPAC\sdk\include"; - $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\zlib;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\libpng16;$(IncludePath) - - $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; - "C:\Program Files\GPAC\sdk\lib";$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) - - - - Disabled - - ..\src\thirdparty\freetype\include;..\src;..\src\thirdparty\win_spec_incld;..\src\lib_ccx;..\src\thirdparty\lib_hash;..\src\lib_ccx\zvbi;..\src\thirdparty\protobuf-c;..\src\thirdparty\win_iconv;..\src\thirdparty\;..\src;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; - "C:\Program Files\GPAC\sdk\include";%(AdditionalIncludeDirectories) - - SEGMENT_BY_FILE_TIME;ENABLE_HARDSUBX;FT2_BUILD_LIBRARY;GPAC_DISABLE_VTT;GPAC_DISABLE_OD_DUMP;ENABLE_OCR;WIN32;_DEBUG;_CONSOLE;_FILE_OFFSET_BITS=64;GPAC_DISABLE_REMOTERY;GPAC_DISABLE_ZLIB;%(PreprocessorDefinitions) - EnableFastChecks - MultiThreadedDebug - - - Level3 - ProgramDatabase - - - - ccx_rust.lib;UserEnv.lib;Crypt32.lib;WS2_32.Lib;ntdll.lib;Bcrypt.lib;Mfplat.lib;Mfuuid.lib;Secur32.lib;Strmiids.lib;Ole32.lib;User32.lib;libcrypto.lib;libcurl.lib;avcodec.lib;avformat.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;leptonica-1.83.1.lib;tesseract53.lib;gif.lib;archive.lib;avdevice.lib;bz2.lib;charset.lib;iconv.lib;jpeg.lib;libpng16.lib;libsharpyuv.lib;libssl.lib;libwebp.lib;libwebpdecoder.lib;libwebpdemux.lib;libwebpmux.lib;libxml2.lib;lz4.lib;lzma.lib;openjp2.lib;tiff.lib;turbojpeg.lib;zlib.lib;zstd.lib;libgpac.lib;%(AdditionalDependencies) - NotSet - true - Console - - $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; - "C:\Program Files\GPAC\sdk\lib";%(AdditionalLibraryDirectories) - - - call pre-build.bat - call rust.bat - - - - xcopy /y "$(ProjectDir)\dll\vcruntime140.dll" "$(OutDir)" - xcopy /y "$(ProjectDir)\dll\vcruntime140_1.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\libgpac.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\OpenSVCDecoder.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\postproc-57.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\swresample-4.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avfilter-9.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\swscale-7.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avdevice-60.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avcodec-60.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avformat-60.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avutil-58.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\libsslMD.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\libcryptoMD.dll" "$(OutDir)" - - - - - Disabled - - ..\src\thirdparty\freetype\include;..\src;..\src\thirdparty\win_spec_incld;..\src\lib_ccx;..\src\thirdparty\lib_hash;..\src\lib_ccx\zvbi;..\src\thirdparty\protobuf-c;..\src\thirdparty\win_iconv;..\src\thirdparty\;..\src;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; - "C:\Program Files\GPAC\sdk\include";%(AdditionalIncludeDirectories) - - ENABLE_HARDSUBX;FT2_BUILD_LIBRARY;GPAC_DISABLE_VTT;GPAC_DISABLE_OD_DUMP;VERSION_FILE_PRESENT;ENABLE_OCR;WIN32;NDEBUG;_CONSOLE;_FILE_OFFSET_BITS=64;GPAC_DISABLE_REMOTERY;GPAC_DISABLE_ZLIB;%(PreprocessorDefinitions) - MultiThreaded - - - Level3 - ProgramDatabase - - - - ccx_rust.lib;UserEnv.lib;Crypt32.lib;WS2_32.Lib;ntdll.lib;Bcrypt.lib;Mfplat.lib;Mfuuid.lib;Secur32.lib;Strmiids.lib;Ole32.lib;User32.lib;libcrypto.lib;libcurl.lib;avcodec.lib;avformat.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;leptonica-1.83.1.lib;tesseract53.lib;gif.lib;archive.lib;avdevice.lib;bz2.lib;charset.lib;iconv.lib;jpeg.lib;libpng16.lib;libsharpyuv.lib;libssl.lib;libwebp.lib;libwebpdecoder.lib;libwebpdemux.lib;libwebpmux.lib;libxml2.lib;lz4.lib;lzma.lib;openjp2.lib;tiff.lib;turbojpeg.lib;zlib.lib;zstd.lib;libgpac.lib;%(AdditionalDependencies) - true - Console - true - true - - $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; - "C:\Program Files\GPAC\sdk\lib";%(AdditionalLibraryDirectories) - - - call pre-build.bat - call rust.bat -r - - - - xcopy /y "$(ProjectDir)\dll\vcruntime140.dll" "$(OutDir)" - xcopy /y "$(ProjectDir)\dll\vcruntime140_1.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\libgpac.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\OpenSVCDecoder.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\postproc-57.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\swresample-4.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avfilter-9.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\swscale-7.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avdevice-60.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avcodec-60.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avformat-60.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avutil-58.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\libsslMD.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\libcryptoMD.dll" "$(OutDir)" - - - - - + + + + + Debug-Full + x64 + + + Release-Full + x64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {0F0063C4-BCBC-4379-A6D5-84A5669C940A} + ccextractor + Win32Proj + 10.0.22621.0 + + + true + x64-windows-static + true + + + + Application + v143 + + + Application + v143 + + + + + + + + + + + + + <_ProjectFileVersion>12.0.21005.1 + + + true + ccextractorwinfull + + $(ProjectDir)\libs\include;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; + "C:\Program Files\GPAC\sdk\include"; + $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\zlib;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\libpng16;$(IncludePath) + + $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; + "C:\Program Files\GPAC\sdk\lib";$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + + + false + ccextractorwinfull + + $(ProjectDir)\libs\include;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; + "C:\Program Files\GPAC\sdk\include"; + $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\zlib;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include\libpng16;$(IncludePath) + + $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; + "C:\Program Files\GPAC\sdk\lib";$(VC_LibraryPath_x64);$(WindowsSDK_LibraryPath_x64) + + + + Disabled + + ..\src\thirdparty\freetype\include;..\src;..\src\thirdparty\win_spec_incld;..\src\lib_ccx;..\src\thirdparty\lib_hash;..\src\lib_ccx\zvbi;..\src\thirdparty\protobuf-c;..\src\thirdparty\win_iconv;..\src\thirdparty\;..\src;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; + "C:\Program Files\GPAC\sdk\include";%(AdditionalIncludeDirectories) + + SEGMENT_BY_FILE_TIME;ENABLE_HARDSUBX;FT2_BUILD_LIBRARY;GPAC_DISABLE_VTT;GPAC_DISABLE_OD_DUMP;ENABLE_OCR;WIN32;_DEBUG;_CONSOLE;_FILE_OFFSET_BITS=64;GPAC_DISABLE_REMOTERY;GPAC_DISABLE_ZLIB;%(PreprocessorDefinitions) + EnableFastChecks + MultiThreadedDebug + + + Level3 + ProgramDatabase + + + + ccx_rust.lib;UserEnv.lib;Crypt32.lib;WS2_32.Lib;ntdll.lib;Bcrypt.lib;Mfplat.lib;Mfuuid.lib;Secur32.lib;Strmiids.lib;Ole32.lib;User32.lib;libcrypto.lib;libcurl.lib;avcodec.lib;avformat.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;leptonica-1.83.1.lib;tesseract53.lib;gif.lib;archive.lib;avdevice.lib;bz2.lib;charset.lib;iconv.lib;jpeg.lib;libpng16.lib;libsharpyuv.lib;libssl.lib;libwebp.lib;libwebpdecoder.lib;libwebpdemux.lib;libwebpmux.lib;libxml2.lib;lz4.lib;lzma.lib;openjp2.lib;tiff.lib;turbojpeg.lib;zlib.lib;zstd.lib;libgpac.lib;%(AdditionalDependencies) + NotSet + true + Console + + $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; + "C:\Program Files\GPAC\sdk\lib";%(AdditionalLibraryDirectories) + + + call pre-build.bat + call rust.bat "--profile=release-with-debug" + + + + xcopy /y "$(ProjectDir)\dll\vcruntime140.dll" "$(OutDir)" + xcopy /y "$(ProjectDir)\dll\vcruntime140_1.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\libgpac.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\OpenSVCDecoder.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\postproc-57.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\swresample-4.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avfilter-9.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\swscale-7.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avdevice-60.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avcodec-60.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avformat-60.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avutil-58.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\libsslMD.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\libcryptoMD.dll" "$(OutDir)" + + + + + Disabled + + ..\src\thirdparty\freetype\include;..\src;..\src\thirdparty\win_spec_incld;..\src\lib_ccx;..\src\thirdparty\lib_hash;..\src\lib_ccx\zvbi;..\src\thirdparty\protobuf-c;..\src\thirdparty\win_iconv;..\src\thirdparty\;..\src;$(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\include; + "C:\Program Files\GPAC\sdk\include";%(AdditionalIncludeDirectories) + + ENABLE_HARDSUBX;FT2_BUILD_LIBRARY;GPAC_DISABLE_VTT;GPAC_DISABLE_OD_DUMP;VERSION_FILE_PRESENT;ENABLE_OCR;WIN32;NDEBUG;_CONSOLE;_FILE_OFFSET_BITS=64;GPAC_DISABLE_REMOTERY;GPAC_DISABLE_ZLIB;%(PreprocessorDefinitions) + MultiThreaded + + + Level3 + ProgramDatabase + + + + ccx_rust.lib;UserEnv.lib;Crypt32.lib;WS2_32.Lib;ntdll.lib;Bcrypt.lib;Mfplat.lib;Mfuuid.lib;Secur32.lib;Strmiids.lib;Ole32.lib;User32.lib;libcrypto.lib;libcurl.lib;avcodec.lib;avformat.lib;avutil.lib;avfilter.lib;swscale.lib;swresample.lib;leptonica-1.83.1.lib;tesseract53.lib;gif.lib;archive.lib;avdevice.lib;bz2.lib;charset.lib;iconv.lib;jpeg.lib;libpng16.lib;libsharpyuv.lib;libssl.lib;libwebp.lib;libwebpdecoder.lib;libwebpdemux.lib;libwebpmux.lib;libxml2.lib;lz4.lib;lzma.lib;openjp2.lib;tiff.lib;turbojpeg.lib;zlib.lib;zstd.lib;libgpac.lib;%(AdditionalDependencies) + true + Console + true + true + + $(VCPKG_ROOT)\installed\$(VCPKG_DEFAULT_TRIPLET)\lib; + "C:\Program Files\GPAC\sdk\lib";%(AdditionalLibraryDirectories) + + + call pre-build.bat + call rust.bat -r + + + + xcopy /y "$(ProjectDir)\dll\vcruntime140.dll" "$(OutDir)" + xcopy /y "$(ProjectDir)\dll\vcruntime140_1.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\libgpac.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\OpenSVCDecoder.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\postproc-57.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\swresample-4.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avfilter-9.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\swscale-7.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avdevice-60.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avcodec-60.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avformat-60.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\avutil-58.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\libsslMD.dll" "$(OutDir)" + xcopy /y "C:\Program Files\GPAC\libcryptoMD.dll" "$(OutDir)" + + + + + \ No newline at end of file diff --git a/windows/ccextractor.vcxproj.filters b/windows/ccextractor.vcxproj.filters index 8a7029ba0..b7afa5a48 100644 --- a/windows/ccextractor.vcxproj.filters +++ b/windows/ccextractor.vcxproj.filters @@ -1,624 +1,624 @@ - - - - - {93995380-89BD-4b04-88EB-625FBE52EBFB} - h;hpp;hxx;hm;inl;inc;xsd - - - {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} - rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx - - - {4FC737F1-C7A5-4376-A066-2A32D752A2FF} - cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx - - - {53ddf332-3341-4811-853d-37995dbfcda8} - - - {230ece07-6169-4824-8439-c941bcd01da5} - - - {ecb47d6d-60ec-4fda-8afd-31833767e830} - - - {89f2f715-e745-4927-93f4-36411a9e8362} - - - {5867ee79-2e93-4616-85d1-9c9ba3fca8c0} - - - {e76a99a9-8a06-4ffb-afa1-f87c057f8448} - - - {955fdac2-43c6-4968-8fce-7d40bad9c712} - - - {0e9c939f-a37c-4d06-a611-4c262b620763} - - - {720b7c91-968e-4dd8-a8df-6d99f9c9b52d} - - - {2b841054-ad06-429d-9b44-fabd29fbeef0} - - - {d86f4f94-df28-4fed-a455-c54bc521c86a} - - - {288f48c3-470a-45ad-a70a-8f062c51aeb1} - - - {140e6ccb-2042-4ecc-9cba-42a04a5b0803} - - - {dcb7e1ec-51eb-4f69-93db-b10133e98402} - - - {de4fb954-0fd9-407a-8803-4dec4640296b} - - - {1870287a-d318-4ef0-9bd1-11c965a1474c} - - - {964b0a18-918d-4b08-ba92-5a84e3c48b43} - - - {19f7e936-6581-4cd7-a5f9-5c4c3ed159c1} - - - {7ef5506d-d575-4661-84a1-dbbb99780e32} - - - {3c0bdb0a-e3f8-4fc4-a27b-b795ed32c965} - - - {fce551b0-7304-4677-8e66-61b475fdb924} - - - {6e23dd8b-166b-4a80-92e2-194e7b6e6c86} - - - {fcf59c1c-8b84-484a-8fb4-a9a301e23610} - - - {489d2c0b-43e8-4d51-8304-f4e59ee8d253} - - - {c08646ce-ae7b-4747-a1e9-9fad759d9ad2} - - - {2c6ca4e0-5f31-46b1-8714-93fb2c28c174} - - - {5af060a0-c719-4d1b-913f-71c4452fbe10} - - - - - Header Files - - - Header Files\ccx_common - - - Header Files\ccx_common - - - Header Files\ccx_common - - - Header Files\ccx_common - - - Header Files\ccx_common - - - Header Files\ccx_common - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_encoders - - - Header Files\lib_ccx\ccx_encoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files\lib_ccx\ccx_decoders - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files\lib_ccx - - - Header Files\lib_ccx - - - Header Files\lib_ccx - - - Header Files - - - Header Files\lib_ccx\ccx_encoders - - - Header Files\lib_ccx\ccx_encoders - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - - - + + + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hpp;hxx;hm;inl;inc;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx + + + {53ddf332-3341-4811-853d-37995dbfcda8} + + + {230ece07-6169-4824-8439-c941bcd01da5} + + + {ecb47d6d-60ec-4fda-8afd-31833767e830} + + + {89f2f715-e745-4927-93f4-36411a9e8362} + + + {5867ee79-2e93-4616-85d1-9c9ba3fca8c0} + + + {e76a99a9-8a06-4ffb-afa1-f87c057f8448} + + + {955fdac2-43c6-4968-8fce-7d40bad9c712} + + + {0e9c939f-a37c-4d06-a611-4c262b620763} + + + {720b7c91-968e-4dd8-a8df-6d99f9c9b52d} + + + {2b841054-ad06-429d-9b44-fabd29fbeef0} + + + {d86f4f94-df28-4fed-a455-c54bc521c86a} + + + {288f48c3-470a-45ad-a70a-8f062c51aeb1} + + + {140e6ccb-2042-4ecc-9cba-42a04a5b0803} + + + {dcb7e1ec-51eb-4f69-93db-b10133e98402} + + + {de4fb954-0fd9-407a-8803-4dec4640296b} + + + {1870287a-d318-4ef0-9bd1-11c965a1474c} + + + {964b0a18-918d-4b08-ba92-5a84e3c48b43} + + + {19f7e936-6581-4cd7-a5f9-5c4c3ed159c1} + + + {7ef5506d-d575-4661-84a1-dbbb99780e32} + + + {3c0bdb0a-e3f8-4fc4-a27b-b795ed32c965} + + + {fce551b0-7304-4677-8e66-61b475fdb924} + + + {6e23dd8b-166b-4a80-92e2-194e7b6e6c86} + + + {fcf59c1c-8b84-484a-8fb4-a9a301e23610} + + + {489d2c0b-43e8-4d51-8304-f4e59ee8d253} + + + {c08646ce-ae7b-4747-a1e9-9fad759d9ad2} + + + {2c6ca4e0-5f31-46b1-8714-93fb2c28c174} + + + {5af060a0-c719-4d1b-913f-71c4452fbe10} + + + + + Header Files + + + Header Files\ccx_common + + + Header Files\ccx_common + + + Header Files\ccx_common + + + Header Files\ccx_common + + + Header Files\ccx_common + + + Header Files\ccx_common + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_encoders + + + Header Files\lib_ccx\ccx_encoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files\lib_ccx\ccx_decoders + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files\lib_ccx + + + Header Files\lib_ccx + + + Header Files\lib_ccx + + + Header Files + + + Header Files\lib_ccx\ccx_encoders + + + Header Files\lib_ccx\ccx_encoders + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Source Files\ccx_decoders + + + Source Files\ccx_decoders + + + Source Files\ccx_decoders + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + \ No newline at end of file diff --git a/windows/rust.bat b/windows/rust.bat index 92e31009b..dcb59b763 100644 --- a/windows/rust.bat +++ b/windows/rust.bat @@ -6,5 +6,5 @@ cd ..\..\windows IF "%~1"=="-r" ( copy x86_64-pc-windows-msvc\release\ccx_rust.lib .\ccx_rust.lib ) ELSE ( -copy x86_64-pc-windows-msvc\debug\ccx_rust.lib .\ccx_rust.lib +copy x86_64-pc-windows-msvc\release-with-debug\ccx_rust.lib .\ccx_rust.lib ) \ No newline at end of file diff --git a/windows/vcpkg.json b/windows/vcpkg.json index caef96365..fbc32071c 100644 --- a/windows/vcpkg.json +++ b/windows/vcpkg.json @@ -1,10 +1,10 @@ -{ - "name": "ccextractor", - "version": "1.0.0", - "dependencies": [ - "leptonica", - "tesseract", - "ffmpeg" - ], - "builtin-baseline": "fba75d09065fcc76a25dcf386b1d00d33f5175af" +{ + "name": "ccextractor", + "version": "1.0.0", + "dependencies": [ + "leptonica", + "tesseract", + "ffmpeg" + ], + "builtin-baseline": "fba75d09065fcc76a25dcf386b1d00d33f5175af" } \ No newline at end of file