diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 4bb225cfb..cd3349ecb 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Install dependencies run: sudo apt update && sudo apt-get install libgpac-dev libtesseract-dev - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build run: ./build working-directory: ./linux @@ -38,7 +38,7 @@ jobs: run: mkdir ./linux/artifacts - name: Copy release artifact run: cp ./linux/ccextractor ./linux/artifacts/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: CCExtractor Linux build path: ./linux/artifacts @@ -47,7 +47,7 @@ jobs: steps: - name: Install dependencies run: sudo apt update && sudo apt-get install libgpac-dev - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: run autogen run: ./autogen.sh working-directory: ./linux @@ -65,7 +65,7 @@ jobs: steps: - name: Install dependencies run: sudo apt update && sudo apt-get install libgpac-dev - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cmake run: mkdir build && cd build && cmake ../src - name: build @@ -76,7 +76,7 @@ jobs: cmake_ocr_hardsubx: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: sudo apt update && sudo apt install libgpac-dev libtesseract-dev libavformat-dev libavdevice-dev libswscale-dev yasm - name: cmake @@ -94,9 +94,9 @@ jobs: steps: - name: Install dependencies run: sudo apt update && sudo apt-get install libgpac-dev - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | src/rust/.cargo/registry diff --git a/.github/workflows/build_mac.yml b/.github/workflows/build_mac.yml index 219704e6a..c654aceac 100644 --- a/.github/workflows/build_mac.yml +++ b/.github/workflows/build_mac.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Install dependencies run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: build run: ./build.command working-directory: ./mac @@ -38,14 +38,14 @@ jobs: run: mkdir ./mac/artifacts - name: Copy release artifact run: cp ./mac/ccextractor ./mac/artifacts/ - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: CCExtractor mac build path: ./mac/artifacts build_autoconf: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: brew install pkg-config autoconf automake libtool gpac - name: run autogen @@ -63,10 +63,10 @@ jobs: cmake: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: dependencies run: brew install gpac - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cmake run: mkdir build && cd build && cmake ../src - name: build @@ -77,7 +77,7 @@ jobs: cmake_ocr_hardsubx: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Install dependencies run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg - name: cmake @@ -93,9 +93,9 @@ jobs: build_rust: runs-on: macos-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | src/rust/.cargo/registry diff --git a/.github/workflows/build_windows.yml b/.github/workflows/build_windows.yml index 37c793a92..1297de86c 100644 --- a/.github/workflows/build_windows.yml +++ b/.github/workflows/build_windows.yml @@ -3,6 +3,8 @@ name: Build CCExtractor on Windows env: RUSTFLAGS: -Ctarget-feature=+crt-static VCPKG_DEFAULT_TRIPLET: x64-windows-static + VCPKG_DEFAULT_BINARY_CACHE: C:\vcpkg\.cache + VCPKG_COMMIT: fba75d09065fcc76a25dcf386b1d00d33f5175af on: workflow_dispatch: @@ -22,65 +24,83 @@ on: jobs: build_release: - runs-on: windows-2019 + runs-on: windows-2022 steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v1.3.1 - - name: Install llvm and clang - run: choco install llvm gpac + with: + msbuild-architecture: x64 + - name: Install gpac + run: choco install gpac --version 2.4.0 + - name: Setup vcpkg + run: mkdir C:\vcpkg\.cache + - name: Cache vcpkg + id: cache + uses: actions/cache@v3 + with: + path: | + C:\vcpkg\.cache + key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }} + - name: Build vcpkg + run: | + git clone https://github.com/microsoft/vcpkg + ./vcpkg/bootstrap-vcpkg.bat + - name: Install dependencies + run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/ + working-directory: windows - uses: actions-rs/toolchain@v1 with: toolchain: stable override: true - name: Install Win 10 SDK uses: ilammy/msvc-dev-cmd@v1 - - name: Setup Vcpkg - id: vcpkg - uses: friendlyanon/setup-vcpkg@v1 - with: - committish: "2023.08.09" - cache-version: "3" - ignore-reserve-cache-error: true - - name: Install dependencies - run: cd vcpkg && vcpkg integrate install && vcpkg install leptonica tesseract ffmpeg --triplet x64-windows-static - name: build Release-Full env: LIBCLANG_PATH: "C:\\Program Files\\LLVM\\lib" LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config" CARGO_TARGET_DIR: "..\\..\\windows" BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0 - VCPKG_ROOT: ${{ github.workspace }}\vcpkg + VCPKG_ROOT: ${{ github.workspace }}/vcpkg run: msbuild ccextractor.sln /p:Configuration=Release-Full /p:Platform=x64 working-directory: ./windows - name: Display version information run: ./ccextractorwinfull.exe --version working-directory: ./windows/x64/Release-Full - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: CCExtractor Windows Release build path: | ./windows/x64/Release-Full/ccextractorwinfull.exe ./windows/x64/Release-Full/*.dll build_debug: - runs-on: windows-2019 + runs-on: windows-2022 steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Setup MSBuild.exe uses: microsoft/setup-msbuild@v1.3.1 - - name: Install llvm and clang - run: choco install llvm gpac - - name: Setup Vcpkg - id: vcpkg - uses: friendlyanon/setup-vcpkg@v1 with: - committish: "2023.08.09" - cache-version: "3" - ignore-reserve-cache-error: true + msbuild-architecture: x64 + - name: Install gpac + run: choco install gpac --version 2.4.0 + - name: Setup vcpkg + run: mkdir C:\vcpkg\.cache + - name: Cache vcpkg + id: cache + uses: actions/cache@v3 + with: + path: | + C:\vcpkg\.cache + key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }} + - name: Build vcpkg + run: | + git clone https://github.com/microsoft/vcpkg + ./vcpkg/bootstrap-vcpkg.bat - name: Install dependencies - run: cd vcpkg && vcpkg integrate install && vcpkg install leptonica tesseract ffmpeg --triplet x64-windows-static + run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/ + working-directory: windows - uses: actions-rs/toolchain@v1 with: toolchain: stable @@ -93,13 +113,14 @@ jobs: LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config" CARGO_TARGET_DIR: "..\\..\\windows" BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0 - VCPKG_ROOT: ${{ github.workspace }}\vcpkg + VCPKG_ROOT: ${{ github.workspace }}/vcpkg run: msbuild ccextractor.sln /p:Configuration=Debug-Full /p:Platform=x64 working-directory: ./windows - name: Display version information + continue-on-error: true run: ./ccextractorwinfull.exe --version working-directory: ./windows/x64/Debug-Full - - uses: actions/upload-artifact@v3 + - uses: actions/upload-artifact@v4 with: name: CCExtractor Windows Debug build path: | diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 0cc02cb51..7a3d84f1d 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -19,7 +19,7 @@ jobs: format: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: Format code run: | find src/ -type f -not -path "src/thirdparty/*" -not -path "src/lib_ccx/zvbi/*" -name '*.c' -not -path "src/GUI/icon_data.c" | xargs clang-format -i @@ -30,9 +30,9 @@ jobs: run: working-directory: ./src/rust steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 - name: cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: | src/rust/.cargo/registry diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2a2d2f147..7fe41c218 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -10,13 +10,13 @@ jobs: runs-on: windows-latest steps: - name: Check out repository - uses: actions/checkout@v3 + uses: actions/checkout@v4 - name: Get the version id: get_version run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/} shell: bash - name: Setup MSBuild.exe - uses: microsoft/setup-msbuild@v1.3.1 + uses: microsoft/setup-msbuild@v2.0.0 - name: Install llvm and clang uses: egor-tensin/setup-clang@v1 with: @@ -62,20 +62,20 @@ jobs: run: wix build -ext "$HOME\.wix\extensions\WixToolset.UI.wixext\4.0.0-preview.0\tools\WixToolset.UI.wixext.dll" -d "AppVersion=${{ steps.get_version.outputs.VERSION }}.0.0" -o CCExtractor.msi installer.wxs working-directory: ./windows - name: Upload as asset - uses: AButler/upload-release-assets@v2.0 + uses: AButler/upload-release-assets@v3.0 with: files: './windows/CCExtractor.msi;./windows/CCExtractor_win_portable.zip' repo-token: ${{ secrets.GITHUB_TOKEN }} create_linux_package: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: path: ./ccextractor - name: Create .tar.gz without git and windows folders run: tar -pczf ./ccextractor_minimal.tar.gz --exclude "ccextractor/windows" --exclude "ccextractor/.git" ccextractor - name: Upload as asset - uses: AButler/upload-release-assets@v2.0 + uses: AButler/upload-release-assets@v3.0 with: files: './ccextractor_minimal.tar.gz' repo-token: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index a1ef235b5..3f8a2ed82 100644 --- a/.gitignore +++ b/.gitignore @@ -149,7 +149,8 @@ src/rust/CMakeCache.txt src/rust/Makefile src/rust/cmake_install.cmake src/rust/target/ +src/rust/lib_ccxr/target/ windows/ccx_rust.lib windows/*/debug/* windows/*/CACHEDIR.TAG -windows/.rustc_info.json \ No newline at end of file +windows/.rustc_info.json diff --git a/docs/CHANGES.TXT b/docs/CHANGES.TXT index f68ff499f..133ffbbff 100644 --- a/docs/CHANGES.TXT +++ b/docs/CHANGES.TXT @@ -1,5 +1,9 @@ 0.95 (to be released) ----------------- +- New: Create `lib_ccxr` and `libccxr_exports` (#1621) +- Fix: Unexpected behavior of get_write_interval (#1609) +- Update: Bump rsmpeg to latest version for ffmpeg bindings (#1600) +- New: Add SCC support for CEA-708 decoder (#1595) - Fix: respect `-stdout` even if multiple CC tracks are present in a Matroska input file (#1453) - Fix: crash in Rust decoder on ATSC1.0 TS Files (#1407) - Removed the --with-gui flag for linux/configure and mac/configure (use the Flutter GUI instead) @@ -21,6 +25,7 @@ - Fix: Repeated values for enums - Cleanup: Remove the (unmaintained) Nuklear GUI code - Cleanup: Reduce the amount of Windows build options in the project file +- Fix: infinite loop in MP4 file type detector. - Fix: fatal out of memory error extracting from a VOB PS 0.94 (2021-12-14) diff --git a/docs/COMPILATION.MD b/docs/COMPILATION.MD index 7189f71f7..a919164ac 100644 --- a/docs/COMPILATION.MD +++ b/docs/COMPILATION.MD @@ -20,15 +20,15 @@ Debian: sudo apt-get install -y libgpac-dev libglew-dev libglfw3-dev cmake gcc libcurl4-gnutls-dev tesseract-ocr libtesseract-dev libleptonica-dev clang libclang-dev ``` -RHEL: +RHEL/Fedora: ```bash -yum install -y glew-devel glfw-devel cmake gcc libcurl-devel tesseract-devel leptonica-devel clang +yum install -y glew-devel glfw-devel cmake gcc libcurl-devel tesseract-devel leptonica-devel clang gpac-devel ``` Arch: ```bash -sudo paru -S glew glfw curl tesseract leptonica cmake gcc clang +sudo paru -S glew glfw curl tesseract leptonica cmake gcc clang gpac ``` Rust 1.54 or above is also required. [Install Rust](https://www.rust-lang.org/tools/install). Check specific compilation methods below, on how to compile without rust. @@ -57,7 +57,7 @@ cd ccextractor/linux ./build -without-rust # compile with debug info -./build -debug # same as ./build_debug +./build -debug # same as ./builddebug # compile with hardsubx [Optional] You need to set these environment variables correctly according to your machine, diff --git a/linux/build b/linux/build index 2d5f5652f..385a8d61b 100755 --- a/linux/build +++ b/linux/build @@ -85,7 +85,7 @@ SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c ../src/thirdparty/freetype/type42/type42.c ../src/thirdparty/freetype/winfonts/winfnt.c" BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG $SRC_HASH $SRC_PROTOBUF $SRC_UTF8PROC $SRC_FREETYPE" -BLD_LINKER="$BLD_LINKER -lm -zmuldefs -l tesseract -l lept -lpthread -ldl -lgpac" +BLD_LINKER="$BLD_LINKER -lm -zmuldefs -l tesseract -l leptonica -lpthread -ldl -lgpac" echo "Running pre-build script..." ./pre-build.sh diff --git a/linux/build-static.sh b/linux/build-static.sh deleted file mode 100755 index c7920a0ad..000000000 --- a/linux/build-static.sh +++ /dev/null @@ -1,107 +0,0 @@ -#!/usr/bin/env -S sh -ex - -#################################################################### -# setup by tracey apr 2012 -# updated version dec 2016 -# see: http://www.ccextractor.org/doku.php -#################################################################### - - -# build it static! -# simplest way is with linux alpine -# hop onto box with docker on it and cd to dir of the file you are staring at -# You will get a static-compiled binary and english language library file in the end. -if [ ! -e /tmp/cc/ccextractor-README.txt ]; then - rm -rf /tmp/cc; - mkdir -p -m777 /tmp/cc; - mkdir -p -m777 ../lib/tessdata/; - cp ccextractor-README.txt /tmp/cc/; - sudo docker run -v /tmp/cc:/tmp/cc --rm -it alpine:latest /tmp/cc/ccextractor-README.txt; - # NOTE: _AFTER_ testing/validating, you can promote it from "ccextractor.next" to "ccextractor"... ;-) - cp /tmp/cc/*traineddata ../lib/tessdata/; - chmod go-w ../lib/tessdata/; - exit 0; -fi - -# NOW we are inside docker container... -cd /tmp/cc; - - -# we want tesseract (for OCR) -echo ' -http://dl-cdn.alpinelinux.org/alpine/v3.5/main -http://dl-cdn.alpinelinux.org/alpine/v3.5/community -' >| /etc/apk/repositories; -apk update; apk upgrade; - -apk add --update bash zsh alpine-sdk perl; - -# (needed by various static builds below) -# Even though we're going to (re)builid tesseract from source statically, get its dependencies setup by -# installing it now, too. -apk add autoconf automake libtool tesseract-ocr-dev; - - -# Now comes the not-so-fun parts... Many packages _only_ provide .so files in their distros -- not the .a -# needed files for building something with it statically. Step through them now... - - -# libgif -wget https://sourceforge.net/projects/giflib/files/giflib-5.1.4.tar.gz; -zcat giflib*tar.gz | tar xf -; -cd giflib*/; -./configure --disable-shared --enable-static; make; make install; -hash -r; -cd -; - - -# libwebp -git clone https://github.com/webmproject/libwebp; -cd libwebp; -./autogen.sh; -./configure --disable-shared --enable-static; make; make install; -cd -; - - -# leptonica -wget http://www.leptonica.org/source/leptonica-1.73.tar.gz; -zcat leptonica*tar.gz | tar xf -; -cd leptonica*/; -./configure --disable-shared --enable-static; make; make install; -hash -r; -cd -; - - -# tesseract -git clone https://github.com/tesseract-ocr/tesseract; -cd tesseract; -./autogen.sh; -./configure --disable-shared --enable-static; make; make install; -cd -; - - -# ccextractor -- build static -git clone https://github.com/CCExtractor/ccextractor; -cd ccextractor/linux/; -perl -i -pe 's/O3 /O3 -static /' Makefile; -set +e; # this _will_ FAIL at the end.. -make ENABLE_OCR=yes; -set -e; -# I confess hand-compiling (cherrypicking which .a to use when there are 2, etc.) is fragile... -# But it was the _only_ way I could get a fully static build after hours of thrashing... -gcc -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -O3 -std=gnu99 -s -DENABLE_OCR -DPNG_NO_CONFIG_H -I/usr/local/include/tesseract -I/usr/local/include/leptonica objs/*.o -o ccextractor \ - --static -lm -lgpac \ - /usr/local/lib/libtesseract.a \ - /usr/local/lib/liblept.a \ - /usr/local/lib/libgif.a \ - /usr/local/lib/libwebp.a \ - /usr/lib/libjpeg.a \ - /usr/lib/libtiff.a \ - /usr/lib/libgomp.a \ - -lstdc++; - -cp ccextractor /tmp/cc/ccextractor.next; -cd -; - -# get english lang trained data -wget https://github.com/tesseract-ocr/tessdata/raw/master/eng.traineddata; diff --git a/linux/build_appimage.sh b/linux/build_appimage.sh new file mode 100755 index 000000000..d586c4eb0 --- /dev/null +++ b/linux/build_appimage.sh @@ -0,0 +1,63 @@ +#!/bin/bash + +set -x +set -e + +# store the path of where the script is +OLD_CWD=$(readlink -f .) + +# store repo root as variable +REPO_ROOT=$(dirname $OLD_CWD) + +# Make a temp directory for building stuff which will be cleaned automatically +BUILD_DIR="$OLD_CWD/temp" + +# Check if temp directory exist, and if so then remove contents from it +# if not then create temp directory +if [ -d "$BUILD_DIR" ]; then + rm -r "$BUILD_DIR/*" | true +else + mkdir -p "$BUILD_DIR" +fi + +# make sure to clean up build dir, even if errors occur +cleanup() { + if [ -d "$BUILD_DIR" ]; then + rm -rf "$BUILD_DIR" + fi +} + +# Automatically trigger Cleanup function +trap cleanup EXIT + +# switch to build dir +pushd "$BUILD_DIR" + +# configure build files with CMake +# we need to explicitly set the install prefix, as CMake's default is /usr/local for some reason... +cmake "$REPO_ROOT/src" + +# build project and install files into AppDir +make -j$(nproc) ENABLE_OCR=yes + +# download linuxdeploy tool +wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage + +# make them executable +chmod +x linuxdeploy*.AppImage + +# Create AppDir +mkdir -p "$BUILD_DIR/AppDir" + +# Link of CCExtractor image of any of these resolution(8x8, 16x16, 20x20, 22x22, 24x24, 28x28, 32x32, 36x36, 42x42, +# 48x48, 64x64, 72x72, 96x96, 128x128, 160x160, 192x192, 256x256, 384x384, 480x480, 512x512) in png extension +PNG_LINK="https://ccextractor.org/images/ccextractor.png" + +# Download the image and put it in AppDir +wget "$PNG_LINK" -P AppDir + +# now, build AppImage using linuxdeploy +./linuxdeploy-x86_64.AppImage --appdir=AppDir -e ccextractor --create-desktop-file --output appimage -i AppDir/ccextractor.png + +# Move resulted AppImage binary to base directory +mv ccextractor*.AppImage "$OLD_CWD" diff --git a/src/lib_ccx/ccx_common_timing.c b/src/lib_ccx/ccx_common_timing.c index d1fca4282..1fd123cb9 100644 --- a/src/lib_ccx/ccx_common_timing.c +++ b/src/lib_ccx/ccx_common_timing.c @@ -295,6 +295,34 @@ LLONG get_fts_max(struct ccx_common_timing_ctx *ctx) return ctx->fts_max + ctx->fts_global; } +/** + * SCC Time formatting + */ +size_t print_scc_time(struct ccx_boundary_time time, char *buf) +{ + char *fmt = "%02u:%02u:%02u;%02u"; + double frame; + + frame = ((double)(time.time_in_ms - 1000 * (time.ss + 60 * (time.mm + 60 * time.hh))) * 29.97 / 1000); + + return (size_t)sprintf(buf + time.set, fmt, time.hh, time.mm, time.ss, (unsigned)frame); +} + +struct ccx_boundary_time get_time(LLONG time) +{ + if (time < 0) // Avoid loss of data warning with abs() + time = -time; + + struct ccx_boundary_time result; + result.hh = (unsigned)(time / 1000 / 60 / 60); + result.mm = (unsigned)(time / 1000 / 60 - 60 * result.hh); + result.ss = (unsigned)(time / 1000 - 60 * (result.mm + 60 * result.hh)); + result.time_in_ms = time; + result.set = (time < 0 ? 1 : 0); + + return result; +} + /** * Fill buffer with a time string using specified format * @param fmt has to contain 4 format specifiers for h, m, s and ms respectively diff --git a/src/lib_ccx/ccx_common_timing.h b/src/lib_ccx/ccx_common_timing.h index f54f4302c..1f157173c 100644 --- a/src/lib_ccx/ccx_common_timing.h +++ b/src/lib_ccx/ccx_common_timing.h @@ -77,6 +77,8 @@ struct ccx_common_timing_ctx *init_timing_ctx(struct ccx_common_timing_settings_ void set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts); void add_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts); +struct ccx_boundary_time get_time(LLONG mstime); +size_t print_scc_time(struct ccx_boundary_time time, char *buf); int set_fts(struct ccx_common_timing_ctx *ctx); LLONG get_fts(struct ccx_common_timing_ctx *ctx, int current_field); LLONG get_fts_max(struct ccx_common_timing_ctx *ctx); diff --git a/src/lib_ccx/ccx_decoders_708.h b/src/lib_ccx/ccx_decoders_708.h index 8cbd01f8b..0fe0dde7d 100644 --- a/src/lib_ccx/ccx_decoders_708.h +++ b/src/lib_ccx/ccx_decoders_708.h @@ -302,6 +302,7 @@ typedef struct dtvcc_tv_screen LLONG time_ms_hide; unsigned int cc_count; int service_number; + int old_cc_time_end; } dtvcc_tv_screen; /** diff --git a/src/lib_ccx/ccx_decoders_708_encoding.c b/src/lib_ccx/ccx_decoders_708_encoding.c index 5cf9c74f6..b3d9508d4 100644 --- a/src/lib_ccx/ccx_decoders_708_encoding.c +++ b/src/lib_ccx/ccx_decoders_708_encoding.c @@ -12,6 +12,8 @@ EIA-708, SO INTERNALLY WE USE THIS TABLE (FOR CONVENIENCE) A0-FF -> Group G1 as is - non-English characters and symbols */ +#if defined(DISABLE_RUST) + unsigned char dtvcc_get_internal_from_G0(unsigned char g0_char) { return g0_char; @@ -43,3 +45,5 @@ unsigned char dtvcc_get_internal_from_G3(unsigned char g3_char) // Rest unmapped, so we return a blank space return 0x20; } + +#endif diff --git a/src/lib_ccx/ccx_decoders_708_encoding.h b/src/lib_ccx/ccx_decoders_708_encoding.h index 0318a0f1c..ef7ca2a84 100644 --- a/src/lib_ccx/ccx_decoders_708_encoding.h +++ b/src/lib_ccx/ccx_decoders_708_encoding.h @@ -1,11 +1,18 @@ #ifndef _CCX_DECODERS_708_ENCODING_H_ #define _CCX_DECODERS_708_ENCODING_H_ -#define CCX_DTVCC_MUSICAL_NOTE_CHAR 9836 // Unicode Character 'BEAMED SIXTEENTH NOTES' +#define CCX_DTVCC_MUSICAL_NOTE_CHAR 9836 // Unicode Character 'BEAMED SIXTEENTH NOTES' +#ifndef DISABLE_RUST +extern unsigned char dtvcc_get_internal_from_G0(unsigned char g0_char); +extern unsigned char dtvcc_get_internal_from_G1(unsigned char g1_char); +extern unsigned char dtvcc_get_internal_from_G2(unsigned char g2_char); +extern unsigned char dtvcc_get_internal_from_G3(unsigned char g3_char); +#else unsigned char dtvcc_get_internal_from_G0(unsigned char g0_char); unsigned char dtvcc_get_internal_from_G1(unsigned char g1_char); unsigned char dtvcc_get_internal_from_G2(unsigned char g2_char); unsigned char dtvcc_get_internal_from_G3(unsigned char g3_char); +#endif #endif /*_CCX_DECODERS_708_ENCODING_H_*/ diff --git a/src/lib_ccx/ccx_decoders_708_output.c b/src/lib_ccx/ccx_decoders_708_output.c index c1bf3faa2..5697ad242 100644 --- a/src/lib_ccx/ccx_decoders_708_output.c +++ b/src/lib_ccx/ccx_decoders_708_output.c @@ -367,6 +367,155 @@ void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf)); } +unsigned char adjust_odd_parity(const unsigned char value) +{ + unsigned int i, ones = 0; + for (i = 0; i < 8; i++) + { + if ((value & (1 << i)) != 0) + { + ones += 1; + } + } + if (ones % 2 == 0) + { + // make the number of ones always odd + return value | 0b10000000; + } + return value; +} + +void dtvcc_write_scc_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder) +{ + char *buf = (char *)encoder->buffer; + // 18 characters long + 2 new lines + memset(buf, 0, 20); + sprintf(buf, "Scenarist_SCC V1.0\n\n"); + + write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf)); +} + +int count_captions_lines_scc(dtvcc_tv_screen *tv) +{ + int count = 0; + for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++) + { + if (!dtvcc_is_row_empty(tv, i)) + { + count++; + } + } + + return count; +} + +/** This function is designed to assign appropriate SSC labels for positioning subtitles based on their length. + * In some scenarios where the video stream provides lengthy subtitles that cannot fit within a single line. + * Single-line subtitle can be placed in 15th row(most bottom row) + * 2 line length subtitles can be placed in 14th and 15th row + * 3 line length subtitles can be placed in 13th, 14th and 15th row + */ +void add_needed_scc_labels(char *buf, int total_subtitle_count, int current_subtitle_count) +{ + switch (total_subtitle_count) + { + case 1: + // row 15, column 00 + sprintf(buf + strlen(buf), " 94e0 94e0"); + break; + case 2: + // 9440: row 14, column 00 | 94e0: row 15, column 00 + sprintf(buf + strlen(buf), current_subtitle_count == 1 ? " 9440 9440" : " 94e0 94e0"); + break; + default: + // 13e0: row 13, column 04 | 9440: row 14, column 00 | 94e0: row 15, column 00 + sprintf(buf + strlen(buf), current_subtitle_count == 1 ? " 13e0 13e0" : (current_subtitle_count == 2 ? " 9440 9440" : " 94e0 94e0")); + } +} + +void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder) +{ + dtvcc_tv_screen *tv = decoder->tv; + + if (dtvcc_is_screen_empty(tv, encoder)) + return; + + if (tv->time_ms_show + encoder->subs_delay < 0) + return; + + if (tv->cc_count == 2) + dtvcc_write_scc_header(tv, encoder); + + char *buf = (char *)encoder->buffer; + struct ccx_boundary_time time_show = get_time(tv->time_ms_show + encoder->subs_delay); + // when hiding subtract a frame (1 frame = 34 ms) + struct ccx_boundary_time time_end = get_time(tv->time_ms_hide + encoder->subs_delay - 34); + + if (tv->old_cc_time_end > time_show.time_in_ms) + { + // Correct the frame delay + time_show.time_in_ms -= 1000 / 29.97; + print_scc_time(time_show, buf); + sprintf(buf + strlen(buf), "\t942c 942c"); + time_show.time_in_ms += 1000 / 29.97; + // Clear the buffer and start pop on caption + sprintf(buf + strlen(buf), "94ae 94ae 9420 9420"); + } + else if (tv->old_cc_time_end < time_show.time_in_ms) + { + // Clear the screen for new caption + struct ccx_boundary_time time_to_display = get_time(tv->old_cc_time_end); + print_scc_time(time_to_display, buf); + sprintf(buf + strlen(buf), "\t942c 942c \n\n"); + // Correct the frame delay + time_show.time_in_ms -= 1000 / 29.97; + // Clear the buffer and start pop on caption in new time + print_scc_time(time_show, buf); + sprintf(buf + strlen(buf), "\t94ae 94ae 9420 9420"); + time_show.time_in_ms += 1000 / 29.97; + } + else + { + time_show.time_in_ms -= 1000 / 29.97; + print_scc_time(time_show, buf); + sprintf(buf + strlen(buf), "\t942c 942c 94ae 94ae 9420 9420"); + time_show.time_in_ms += 1000 / 29.97; + } + + int total_subtitle_count = count_captions_lines_scc(tv); + int current_subtitle_count = 0; + + for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++) + { + if (!dtvcc_is_row_empty(tv, i)) + { + current_subtitle_count++; + add_needed_scc_labels(buf, total_subtitle_count, current_subtitle_count); + + int first, last, bytes_written = 0; + dtvcc_get_write_interval(tv, i, &first, &last); + for (int j = first; j <= last; j++) + { + if (bytes_written % 2 == 0) + sprintf(buf + strlen(buf), " "); + sprintf(buf + strlen(buf), "%x", adjust_odd_parity(tv->chars[i][j].sym)); + bytes_written += 1; + } + // if byte pair are not even then make it even by adding 0x80 as padding + if (bytes_written % 2 == 1) + sprintf(buf + strlen(buf), "80 "); + else + sprintf(buf + strlen(buf), " "); + } + } + + // Display caption (942f 942f) + sprintf(buf + strlen(buf), "942f 942f \n\n"); + write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf)); + + tv->old_cc_time_end = time_end.time_in_ms; +} + void dtvcc_write(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder) { switch (encoder->write_format) @@ -382,6 +531,9 @@ void dtvcc_write(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struc case CCX_OF_SAMI: dtvcc_write_sami(writer, decoder, encoder); break; + case CCX_OF_SCC: + dtvcc_write_scc(writer, decoder, encoder); + break; case CCX_OF_MCC: printf("REALLY BAD... [%s:%d]\n", __FILE__, __LINE__); break; diff --git a/src/lib_ccx/ccx_decoders_708_output.h b/src/lib_ccx/ccx_decoders_708_output.h index 3b9f6759f..63544df79 100644 --- a/src/lib_ccx/ccx_decoders_708_output.h +++ b/src/lib_ccx/ccx_decoders_708_output.h @@ -8,11 +8,11 @@ void dtvcc_write_done(dtvcc_tv_screen *tv, struct encoder_ctx *encoder); void dtvcc_writer_init(dtvcc_writer_ctx *writer, - char *base_filename, - int program_number, - int service_number, - enum ccx_output_format write_format, - struct encoder_cfg *cfg); + char *base_filename, + int program_number, + int service_number, + enum ccx_output_format write_format, + struct encoder_cfg *cfg); void dtvcc_writer_cleanup(dtvcc_writer_ctx *writer); void dtvcc_writer_output(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder); @@ -30,6 +30,9 @@ void dtvcc_write_transcript(dtvcc_writer_ctx *writer, dtvcc_service_decoder *dec void dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder); void dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder); void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder); +void dtvcc_write_scc_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder); +void add_needed_scc_labels(char *buf, int total_subtitle_count, int current_subtitle_count); +void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder); void dtvcc_write(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder); -#endif /*_CCX_DECODERS_708_OUTPUT_H_*/ \ No newline at end of file +#endif /*_CCX_DECODERS_708_OUTPUT_H_*/ diff --git a/src/lib_ccx/mp4.c b/src/lib_ccx/mp4.c index 1b5c45119..05df43fe0 100644 --- a/src/lib_ccx/mp4.c +++ b/src/lib_ccx/mp4.c @@ -51,6 +51,16 @@ static int process_avc_sample(struct lib_ccx_ctx *ctx, u32 timescale, GF_AVCConf { u32 nal_length; + if (i + c->nal_unit_size > s->dataLength) + { + mprint("Corrupted packet detected in process_avc_sample. dataLength " + "%u is less than index %u + nal_unit_size %u. Ignoring.\n", + s->dataLength, i, c->nal_unit_size); + // The packet is likely corrupted, it's unsafe to read this many bytes + // even to detect the length of the next `nal`. Ignoring this error, + // hopefully the outer loop in `process_avc_track` can recover. + return status; + } switch (c->nal_unit_size) { case 1: @@ -63,15 +73,28 @@ static int process_avc_sample(struct lib_ccx_ctx *ctx, u32 timescale, GF_AVCConf nal_length = bswap32(*(long *)&s->data[i]); break; } + const u32 previous_index = i; i += c->nal_unit_size; + if (i + nal_length <= previous_index || i + nal_length > s->dataLength) + { + mprint("Corrupted sample detected in process_avc_sample. dataLength %u " + "is less than index %u + nal_unit_size %u + nal_length %u. Ignoring.\n", + s->dataLength, previous_index, c->nal_unit_size, nal_length); + // The packet is likely corrupted, it's unsafe to procell nal_length bytes + // because they are past the sample end. Ignoring this error, hopefully + // the outer loop in `process_avc_track` can recover. + return status; + } s_nalu_stats.total += 1; - s_nalu_stats.type[s->data[i] & 0x1F] += 1; - temp_debug = 0; if (nal_length > 0) + { + // s->data[i] is only relevant and safe to access here. + s_nalu_stats.type[s->data[i] & 0x1F] += 1; do_NAL(enc_ctx, dec_ctx, (unsigned char *)&(s->data[i]), nal_length, sub); + } i += nal_length; } // outer for assert(i == s->dataLength); diff --git a/src/lib_ccx/params.c b/src/lib_ccx/params.c index 1c06588d8..eb1562e50 100644 --- a/src/lib_ccx/params.c +++ b/src/lib_ccx/params.c @@ -356,58 +356,45 @@ void print_usage(void) mprint(" -o outputfilename: Use -o parameters to define output filename if you don't\n"); mprint(" like the default ones (same as infile plus _1 or _2 when\n"); mprint(" needed and file extension, e.g. .srt).\n"); - mprint(" -stdout: Write output to stdout (console) instead of file. If\n"); + mprint(" --stdout: Write output to stdout (console) instead of file. If\n"); mprint(" stdout is used, then -o can't be used. Also\n"); - mprint(" -stdout will redirect all messages to stderr (error).\n"); - mprint(" -pesheader: Dump the PES Header to stdout (console). This is\n"); + mprint(" --stdout will redirect all messages to stderr (error).\n"); + mprint(" --pesheader: Dump the PES Header to stdout (console). This is\n"); mprint(" used for debugging purposes to see the contents\n"); mprint(" of each PES packet header.\n"); - mprint(" -debugdvbsub: Write the DVB subtitle debug traces to console.\n"); - mprint(" -ignoreptsjumps: Ignore PTS jumps (default).\n"); - mprint(" -fixptsjumps: fix pts jumps. Use this parameter if you\n"); + mprint(" --debugdvbsub: Write the DVB subtitle debug traces to console.\n"); + mprint(" --ignoreptsjumps: Ignore PTS jumps (default).\n"); + mprint(" --fixptsjumps: fix pts jumps. Use this parameter if you\n"); mprint(" experience timeline resets/jumps in the output.\n"); - mprint(" -stdin: Reads input from stdin (console) instead of file.\n"); + mprint(" --stdin: Reads input from stdin (console) instead of file.\n"); mprint(" Alternatively, - can be used instead of -stdin\n"); - mprint("You can pass as many input files as you need. They will be processed in order.\n"); - mprint("If a file name is suffixed by +, ccextractor will try to follow a numerical\n"); - mprint("sequence. For example, DVD001.VOB+ means DVD001.VOB, DVD002.VOB and so on\n"); - mprint("until there are no more files.\n"); - mprint("Output will be one single file (either raw or srt). Use this if you made your\n"); - mprint("recording in several cuts (to skip commercials for example) but you want one\n"); - mprint("subtitle file with contiguous timing.\n\n"); mprint("Output file segmentation:\n"); - mprint(" -outinterval x output in interval of x seconds\n"); - mprint(" --segmentonkeyonly -key: When segmenting files, do it only after a I frame\n"); + mprint(" --outinterval x output in interval of x seconds\n"); + mprint(" --segmentonkeyonly: When segmenting files, do it only after a I frame\n"); mprint(" trying to behave like FFmpeg\n\n"); mprint("Network support:\n"); - mprint(" -udp port: Read the input via UDP (listening in the specified port)\n"); - mprint(" instead of reading a file.\n\n"); - mprint(" -udp [host:]port: Read the input via UDP (listening in the specified\n"); - mprint(" port) instead of reading a file. Host can be a\n"); - mprint(" hostname or IPv4 address. If host is not specified\n"); - mprint(" then listens on the local host.\n\n"); - mprint(" -udp [src@host:]port: Read the input via UDP (listening in the specified\n"); + mprint(" --udp [[src@]host:]port: Read the input via UDP (listening in the specified\n"); mprint(" port) instead of reading a file. Host and src can be a\n"); mprint(" hostname or IPv4 address. If host is not specified\n"); mprint(" then listens on the local host.\n\n"); - mprint(" -sendto host[:port]: Sends data in BIN format to the server\n"); + mprint(" --sendto host[:port]: Sends data in BIN format to the server\n"); mprint(" according to the CCExtractor's protocol over\n"); mprint(" TCP. For IPv6 use [address]:port\n"); - mprint(" -tcp port: Reads the input data in BIN format according to\n"); + mprint(" --tcp port: Reads the input data in BIN format according to\n"); mprint(" CCExtractor's protocol, listening specified port on the\n"); mprint(" local host\n"); - mprint(" -tcppassword password: Sets server password for new connections to\n"); + mprint(" --tcp-password password: Sets server password for new connections to\n"); mprint(" tcp server\n"); - mprint(" -tcpdesc description: Sends to the server short description about\n"); + mprint(" --tcp-description description: Sends to the server short description about\n"); mprint(" captions e.g. channel name or file name\n"); mprint("Options that affect what will be processed:\n"); - mprint(" -1, -2, -12: Output Field 1 data, Field 2 data, or both\n"); - mprint(" (DEFAULT is -1)\n"); + mprint(" --output-field 1 / 2 / both: Output Field 1 data, Field 2 data, or both\n"); + mprint(" (DEFAULT is 1)\n"); mprint("Use --append to prevent overwriting of existing files. The output will be\n"); mprint(" appended instead.\n"); - mprint(" -cc2: When in srt/sami mode, process captions in channel 2\n"); - mprint(" instead of channel 1. Alternatively, -CC2 can also be used.\n"); - mprint("-svc --service N1[cs1],N2[cs2]...:\n"); + mprint(" --cc2: When in srt/sami mode, process captions in channel 2\n"); + mprint(" instead of channel 1. Alternatively, --CC2 can also be used.\n"); + mprint(" --service N1[cs1],N2[cs2]...:\n"); mprint(" Enable CEA-708 (DTVCC) captions processing for the listed\n"); mprint(" services. The parameter is a comma delimited list\n"); mprint(" of services numbers, such as \"1,2\" to process the\n"); @@ -421,9 +408,6 @@ void print_usage(void) mprint(" UTF-8 using iconv. See iconv documentation to check if\n"); mprint(" required encoding/charset is supported.\n"); mprint("\n"); - mprint("In general, if you want English subtitles you don't need to use these options\n"); - mprint("as they are broadcast in field 1, channel 1. If you want the second language\n"); - mprint("(usually Spanish) you may need to try -2, or -cc2, or both.\n\n"); mprint("Input formats:\n"); mprint(" With the exception of McPoodle's raw format, which is just the closed\n"); mprint(" caption data with no other info, CCExtractor can usually detect the\n"); @@ -444,9 +428,9 @@ void print_usage(void) #ifdef WTV_DEBUG mprint(" hex -> Hexadecimal dump as generated by wtvccdump.\n"); #endif - mprint(" -ts, -ps, -es, -mp4, -wtv, -mkv and -asf/--dvr-ms can be used as shorts.\n\n"); + mprint(" --ts, --ps, --es, --mp4, --wtv, --mkv and --asf/--dvr-ms can be used as shorts.\n\n"); mprint("Output formats:\n\n"); - mprint(" -out=format\n\n"); + mprint(" --out=format\n\n"); mprint(" where format is one of these:\n"); mprint(" srt -> SubRip (default, so not actually needed).\n"); mprint(" ass/ssa -> SubStation Alpha.\n"); @@ -477,27 +461,27 @@ void print_usage(void) mprint(" report -> Prints to stdout information about captions\n"); mprint(" in specified input. Don't produce any file\n"); mprint(" output\n\n"); - mprint(" -srt, -dvdraw, -sami, -webvtt, -txt, -ttxt and -null can be used as shorts.\n\n"); + mprint(" --srt, --dvdraw, --sami, --webvtt, --txt, --ttxt and --null can be used as shorts.\n\n"); mprint("Options that affect how input files will be processed.\n"); - mprint(" -gt --goptime: Use GOP for timing instead of PTS. This only applies\n"); + mprint(" --goptime: Use GOP for timing instead of PTS. This only applies\n"); mprint(" to Program or Transport Streams with MPEG2 data and\n"); mprint(" overrides the default PTS timing.\n"); mprint(" GOP timing is always used for Elementary Streams.\n"); - mprint(" -nogt --nogoptime: Never use GOP timing (use PTS), even if ccextractor\n"); + mprint(" --no-goptime: Never use GOP timing (use PTS), even if ccextractor\n"); mprint(" detects GOP timing is the reasonable choice.\n"); - mprint(" -fp --fixpadding: Fix padding - some cards (or providers, or whatever)\n"); + mprint(" --fixpadding: Fix padding - some cards (or providers, or whatever)\n"); mprint(" seem to send 0000 as CC padding instead of 8080. If you\n"); mprint(" get bad timing, this might solve it.\n"); - mprint(" -90090: Use 90090 (instead of 90000) as MPEG clock frequency.\n"); + mprint(" --90090: Use 90090 (instead of 90000) as MPEG clock frequency.\n"); mprint(" (reported to be needed at least by Panasonic DMR-ES15\n"); mprint(" DVD Recorder)\n"); - mprint(" -ve --videoedited: By default, ccextractor will process input files in\n"); + mprint(" --videoedited: By default, ccextractor will process input files in\n"); mprint(" sequence as if they were all one large file (i.e.\n"); mprint(" split by a generic, non video-aware tool. If you\n"); mprint(" are processing video hat was split with a editing\n"); - mprint(" tool, use -ve so ccextractor doesn't try to rebuild\n"); + mprint(" tool, use --ve so ccextractor doesn't try to rebuild\n"); mprint(" the original timing.\n"); mprint(" -s --stream [secs]: Consider the file as a continuous stream that is\n"); mprint(" growing as ccextractor processes it, so don't try\n"); @@ -510,80 +494,73 @@ void print_usage(void) mprint(" but not kill ccextractor externally.\n"); mprint(" Note: If -s is used then only one input file is\n"); mprint(" allowed.\n"); - mprint(" -poc --usepicorder: Use the pic_order_cnt_lsb in AVC/H.264 data streams\n"); + mprint(" --usepicorder: Use the pic_order_cnt_lsb in AVC/H.264 data streams\n"); mprint(" to order the CC information. The default way is to\n"); mprint(" use the PTS information. Use this switch only when\n"); mprint(" needed.\n"); - mprint(" -myth: Force MythTV code branch.\n"); - mprint(" -nomyth: Disable MythTV code branch.\n"); + mprint(" --myth: Force MythTV code branch.\n"); + mprint(" --no-myth: Disable MythTV code branch.\n"); mprint(" The MythTV branch is needed for analog captures where\n"); mprint(" the closed caption data is stored in the VBI, such as\n"); mprint(" those with bttv cards (Hauppage 250 for example). This\n"); mprint(" is detected automatically so you don't need to worry\n"); mprint(" about this unless autodetection doesn't work for you.\n"); - mprint(" -wtvconvertfix: This switch works around a bug in Windows 7's built in\n"); + mprint(" --wtvconvertfix: This switch works around a bug in Windows 7's built in\n"); mprint(" software to convert *.wtv to *.dvr-ms. For analog NTSC\n"); mprint(" recordings the CC information is marked as digital\n"); mprint(" captions. Use this switch only when needed.\n"); - mprint(" -wtvmpeg2: Read the captions from the MPEG2 video stream rather\n"); + mprint(" --wtvmpeg2: Read the captions from the MPEG2 video stream rather\n"); mprint(" than the captions stream in WTV files\n"); - mprint(" -pn --program-number: In TS mode, specifically select a program to process.\n"); + mprint(" --program-number: In TS mode, specifically select a program to process.\n"); mprint(" Not needed if the TS only has one. If this parameter\n"); mprint(" is not specified and CCExtractor detects more than one\n"); mprint(" program in the input, it will list the programs found\n"); mprint(" and terminate without doing anything, unless\n"); - mprint(" -autoprogram (see below) is used.\n"); - mprint(" -autoprogram: If there's more than one program in the stream, just use\n"); + mprint(" --autoprogram (see below) is used.\n"); + mprint(" --autoprogram: If there's more than one program in the stream, just use\n"); mprint(" the first one we find that contains a suitable stream.\n"); - mprint(" -multiprogram: Uses multiple programs from the same input stream.\n"); - mprint(" -datapid: Don't try to find out the stream for caption/teletext\n"); + mprint(" --multiprogram: Uses multiple programs from the same input stream.\n"); + mprint(" --datapid: Don't try to find out the stream for caption/teletext\n"); mprint(" data, just use this one instead.\n"); - mprint(" -datastreamtype: Instead of selecting the stream by its PID, select it\n"); + mprint(" --datastreamtype: Instead of selecting the stream by its PID, select it\n"); mprint(" by its type (pick the stream that has this type in\n"); mprint(" the PMT)\n"); - mprint(" -streamtype: Assume the data is of this type, don't autodetect. This\n"); - mprint(" parameter may be needed if -datapid or -datastreamtype\n"); + mprint(" --streamtype: Assume the data is of this type, don't autodetect. This\n"); + mprint(" parameter may be needed if --datapid or -datastreamtype\n"); mprint(" is used and CCExtractor cannot determine how to process\n"); mprint(" the stream. The value will usually be 2 (MPEG video) or\n"); mprint(" 6 (MPEG private data).\n"); - mprint(" -haup --hauppauge: If the video was recorder using a Hauppauge card, it\n"); + mprint(" --hauppauge: If the video was recorder using a Hauppauge card, it\n"); mprint(" might need special processing. This parameter will\n"); mprint(" force the special treatment.\n"); - mprint(" -mp4vidtrack: In MP4 files the closed caption data can be embedded in\n"); + mprint(" --mp4vidtrack: In MP4 files the closed caption data can be embedded in\n"); mprint(" the video track or in a dedicated CC track. If a\n"); mprint(" dedicated track is detected it will be processed instead\n"); mprint(" of the video track. If you need to force the video track\n"); mprint(" to be processed instead use this option.\n"); - mprint(" -noautotimeref: Some streams come with broadcast date information. When\n"); + mprint(" --no-autotimeref: Some streams come with broadcast date information. When\n"); mprint(" such data is available, CCExtractor will set its time\n"); mprint(" reference to the received data. Use this parameter if\n"); mprint(" you prefer your own reference. Note: Current this only\n"); mprint(" affects Teletext in timed transcript with -datets.\n"); - mprint(" --noscte20: Ignore SCTE-20 data if present.\n"); + mprint(" --no-scte20: Ignore SCTE-20 data if present.\n"); mprint(" --webvtt-create-css: Create a separate file for CSS instead of inline.\n"); - mprint(" -deblev: Enable debug so the calculated distance for each two\n"); + mprint(" --deblev: Enable debug so the calculated distance for each two\n"); mprint(" strings is displayed. The output includes both strings,\n"); mprint(" the calculated distance, the maximum allowed distance,\n"); mprint(" and whether the strings are ultimately considered \n"); mprint(" equivalent or not, i.e. the calculated distance is \n"); mprint(" less or equal than the max allowed..\n"); - mprint("-anvid --analyzevideo Analyze the video stream even if it's not used for\n"); + mprint(" --analyzevideo Analyze the video stream even if it's not used for\n"); mprint(" subtitles. This allows to provide video information.\n"); mprint(" --timestamp-map Enable the X-TIMESTAMP-MAP header for WebVTT (HLS)\n"); - mprint("Levenshtein distance:\n\n"); - mprint(" When processing teletext files CCExtractor tries to correct typos by\n"); - mprint(" comparing consecutive lines. If line N+1 is almost identical to line N except\n"); - mprint(" for minor changes (plus next characters) then it assumes that line N that a\n"); - mprint(" typo that was corrected in N+1. This is currently implemented in teletext\n"); - mprint(" because it's where samples files that could benefit from this were available.\n"); - mprint(" You can adjust, or disable, the algorithm settings with the following\n"); - mprint(" parameters.\n\n"); - mprint(" -nolevdist: Don't attempt to correct typos with Levenshtein distance.\n"); - mprint(" -levdistmincnt value: Minimum distance we always allow regardless\n"); + mprint("Levenshtein distance:\n"); + mprint(" --no-levdist: Don't attempt to correct typos with Levenshtein distance.\n"); + mprint(" --levdistmincnt value: Minimum distance we always allow regardless\n"); mprint(" of the length of the strings.Default 2. \n"); mprint(" This means that if the calculated distance \n"); mprint(" is 0,1 or 2, we consider the strings to be equivalent.\n"); - mprint(" -levdistmaxpct value: Maximum distance we allow, as a percentage of\n"); + mprint(" --levdistmaxpct value: Maximum distance we allow, as a percentage of\n"); mprint(" the shortest string length. Default 10%.\n"); mprint(" For example, consider a comparison of one string of \n"); mprint(" 30 characters and one of 60 characters. We want to \n"); @@ -596,31 +573,31 @@ void print_usage(void) mprint(" to 3 between the first 30 characters.\n"); mprint("\n"); mprint("Options that affect what kind of output will be produced:\n"); - mprint(" -chapters: (Experimental) Produces a chapter file from MP4 files.\n"); + mprint(" --chapters: (Experimental) Produces a chapter file from MP4 files.\n"); mprint(" Note that this must only be used with MP4 files,\n"); mprint(" for other files it will simply generate subtitles file.\n"); - mprint(" -bom: Append a BOM (Byte Order Mark) to output files.\n"); + mprint(" --bom: Append a BOM (Byte Order Mark) to output files.\n"); mprint(" Note that most text processing tools in linux will not\n"); mprint(" like BOM.\n"); - mprint(" -nobom: Do not append a BOM (Byte Order Mark) to output\n"); + mprint(" --no-bom: Do not append a BOM (Byte Order Mark) to output\n"); mprint(" files. Note that this may break files when using\n"); mprint(" Windows. This is the default in non-Windows builds.\n"); - mprint(" -unicode: Encode subtitles in Unicode instead of Latin-1.\n"); - mprint(" -utf8: Encode subtitles in UTF-8 (no longer needed.\n"); + mprint(" --unicode: Encode subtitles in Unicode instead of Latin-1.\n"); + mprint(" --utf8: Encode subtitles in UTF-8 (no longer needed.\n"); mprint(" because UTF-8 is now the default).\n"); - mprint(" -latin1: Encode subtitles in Latin-1\n"); - mprint(" -nofc --nofontcolor: For .srt/.sami/.vtt, don't add font color tags.\n"); - mprint(" --nohtmlescape: For .srt/.sami/.vtt, don't covert html unsafe character\n"); - mprint("-nots --notypesetting: For .srt/.sami/.vtt, don't add typesetting tags.\n"); - mprint(" -trim: Trim lines.\n"); - mprint(" -dc --defaultcolor: Select a different default color (instead of\n"); + mprint(" --latin1: Encode subtitles in Latin-1\n"); + mprint(" --no-fontcolor: For .srt/.sami/.vtt, don't add font color tags.\n"); + mprint(" --no-htmlescape: For .srt/.sami/.vtt, don't covert html unsafe character\n"); + mprint(" --no-typesetting: For .srt/.sami/.vtt, don't add typesetting tags.\n"); + mprint(" --trim: Trim lines.\n"); + mprint(" --defaultcolor: Select a different default color (instead of\n"); mprint(" white). This causes all output in .srt/.smi/.vtt\n"); mprint(" files to have a font tag, which makes the files\n"); mprint(" larger. Add the color you want in RGB, such as\n"); - mprint(" -dc #FF0000 for red.\n"); - mprint(" -sc --sentencecap: Sentence capitalization. Use if you hate\n"); + mprint(" --dc #FF0000 for red.\n"); + mprint(" --sentencecap: Sentence capitalization. Use if you hate\n"); mprint(" ALL CAPS in subtitles.\n"); - mprint(" --capfile -caf file: Add the contents of 'file' to the list of words\n"); + mprint(" --capfile file: Add the contents of 'file' to the list of words\n"); mprint(" that must be capitalized. For example, if file\n"); mprint(" is a plain text file that contains\n\n"); mprint(" Tony\n"); @@ -633,58 +610,58 @@ void print_usage(void) mprint("--profanity-file : Add the contents of to the list of words that.\n"); mprint(" must be censored. The content of , follows the\n"); mprint(" same syntax as for the capitalization file\n"); - mprint("-sbs --splitbysentence: Split output text so each frame contains a complete\n"); + mprint(" --splitbysentence: Split output text so each frame contains a complete\n"); mprint(" sentence. Timings are adjusted based on number of\n"); - mprint(" characters\n."); - mprint(" -unixts REF: For timed transcripts that have an absolute date\n"); + mprint(" characters\n"); + mprint(" --unixts REF: For timed transcripts that have an absolute date\n"); mprint(" instead of a timestamp relative to the file start), use\n"); mprint(" this time reference (UNIX timestamp). 0 => Use current\n"); mprint(" system time.\n"); mprint(" ccextractor will automatically switch to transport\n"); mprint(" stream UTC timestamps when available.\n"); - mprint(" -datets: In transcripts, write time as YYYYMMDDHHMMss,ms.\n"); - mprint(" -sects: In transcripts, write time as ss,ms\n"); - mprint(" -UCLA: Transcripts are generated with a specific format\n"); + mprint(" --datets: In transcripts, write time as YYYYMMDDHHMMss,ms.\n"); + mprint(" --sects: In transcripts, write time as ss,ms\n"); + mprint(" --UCLA: Transcripts are generated with a specific format\n"); mprint(" that is convenient for a specific project, feel\n"); mprint(" free to play with it but be aware that this format\n"); mprint(" is really live - don't rely on its output format\n"); mprint(" not changing between versions.\n"); - mprint(" -latrusmap Map Latin symbols to Cyrillic ones in special cases\n"); + mprint(" --latrusmap Map Latin symbols to Cyrillic ones in special cases\n"); mprint(" of Russian Teletext files (issue #1086)\n"); - mprint(" -xds: In timed transcripts, all XDS information will be saved\n"); + mprint(" --xds: In timed transcripts, all XDS information will be saved\n"); mprint(" to the output file.\n"); - mprint(" -lf: Use LF (UNIX) instead of CRLF (DOS, Windows) as line\n"); + mprint(" --lf: Use LF (UNIX) instead of CRLF (DOS, Windows) as line\n"); mprint(" terminator.\n"); - mprint(" -df: For MCC Files, force dropframe frame count.\n"); - mprint(" -autodash: Based on position on screen, attempt to determine\n"); + mprint(" --df: For MCC Files, force dropframe frame count.\n"); + mprint(" --autodash: Based on position on screen, attempt to determine\n"); mprint(" the different speakers and a dash (-) when each\n"); - mprint(" of them talks (.srt/.vtt only, -trim required).\n"); - mprint(" -xmltv mode: produce an XMLTV file containing the EPG data from\n"); + mprint(" of them talks (.srt/.vtt only, --trim required).\n"); + mprint(" --xmltv mode: produce an XMLTV file containing the EPG data from\n"); mprint(" the source TS file. Mode: 1 = full output\n"); mprint(" 2 = live output. 3 = both\n"); - mprint(" -xmltvliveinterval x: interval of x seconds between writing live mode xmltv output.\n"); - mprint("-xmltvoutputinterval x: interval of x seconds between writing full file xmltv output.\n"); - mprint(" -xmltvonlycurrent: Only print current events for xmltv output.\n"); - mprint(" -sem: Create a .sem file for each output file that is open\n"); + mprint(" --xmltvliveinterval x: interval of x seconds between writing live mode xmltv output.\n"); + mprint("--xmltvoutputinterval x: interval of x seconds between writing full file xmltv output.\n"); + mprint(" --xmltvonlycurrent: Only print current events for xmltv output.\n"); + mprint(" --sem: Create a .sem file for each output file that is open\n"); mprint(" and delete it on file close.\n"); - mprint(" -dvblang: For DVB subtitles, select which language's caption\n"); + mprint(" --dvblang: For DVB subtitles, select which language's caption\n"); mprint(" stream will be processed. e.g. 'eng' for English.\n"); mprint(" If there are multiple languages, only this specified\n"); mprint(" language stream will be processed (default).\n"); - mprint(" -ocrlang: Manually select the name of the Tesseract .traineddata\n"); + mprint(" --ocrlang: Manually select the name of the Tesseract .traineddata\n"); mprint(" file. Helpful if you want to OCR a caption stream of\n"); mprint(" one language with the data of another language.\n"); - mprint(" e.g. '-dvblang chs -ocrlang chi_tra' will decode the\n"); + mprint(" e.g. '-dvblang chs --ocrlang chi_tra' will decode the\n"); mprint(" Chinese (Simplified) caption stream but perform OCR\n"); mprint(" using the Chinese (Traditional) trained data\n"); mprint(" This option is also helpful when the traineddata file\n"); mprint(" has non standard names that don't follow ISO specs\n"); - mprint(" -quant mode: How to quantize the bitmap before passing it to tesseract\n"); + mprint(" --quant mode: How to quantize the bitmap before passing it to tesseract\n"); mprint(" for OCR'ing.\n"); mprint(" 0: Don't quantize at all.\n"); mprint(" 1: Use CCExtractor's internal function (default).\n"); mprint(" 2: Reduce distinct color count in image for faster results.\n"); - mprint(" -oem: Select the OEM mode for Tesseract.\n"); + mprint(" --oem: Select the OEM mode for Tesseract.\n"); mprint(" Available modes :\n"); mprint(" 0: OEM_TESSERACT_ONLY - the fastest mode.\n"); mprint(" 1: OEM_LSTM_ONLY - use LSTM algorithm for recognition.\n"); @@ -692,108 +669,89 @@ void print_usage(void) mprint(" Default value depends on the tesseract version linked :\n"); mprint(" Tesseract v3 : default mode is 0,\n"); mprint(" Tesseract v4 : default mode is 1.\n"); - mprint(" -mkvlang: For MKV subtitles, select which language's caption\n"); + mprint(" --mkvlang: For MKV subtitles, select which language's caption\n"); mprint(" stream will be processed. e.g. 'eng' for English.\n"); mprint(" Language codes can be either the 3 letters bibliographic\n"); mprint(" ISO-639-2 form (like \"fre\" for french) or a language\n"); mprint(" code followed by a dash and a country code for specialities\n"); mprint(" in languages (like \"fre-ca\" for Canadian French).\n"); - mprint(" -nospupngocr When processing DVB don't use the OCR to write the text as\n"); + mprint(" --no-spupngocr When processing DVB don't use the OCR to write the text as\n"); mprint(" comments in the XML file.\n"); - mprint(" -font: Specify the full path of the font that is to be used when\n"); + mprint(" --font: Specify the full path of the font that is to be used when\n"); mprint(" generating SPUPNG files. If not specified, you need to\n"); mprint(" have the default font installed (Helvetica for macOS, Calibri\n"); - mprint(" for Windows, and Noto for other operating systems at their\n)"); - mprint(" default location\n)"); - mprint(" -italics: Specify the full path of the italics font that is to be used when\n"); + mprint(" for Windows, and Noto for other operating systems at their)\n"); + mprint(" default location)\n"); + mprint(" --italics: Specify the full path of the italics font that is to be used when\n"); mprint(" generating SPUPNG files. If not specified, you need to\n"); mprint(" have the default font installed (Helvetica Oblique for macOS, Calibri Italic\n"); - mprint(" for Windows, and NotoSans Italic for other operating systems at their\n)"); - mprint(" default location\n)"); + mprint(" for Windows, and NotoSans Italic for other operating systems at their)\n"); + mprint(" default location)\n"); mprint("\n"); mprint("Options that affect how ccextractor reads and writes (buffering):\n"); - mprint(" -bi --bufferinput: Forces input buffering.\n"); - mprint(" -nobi -nobufferinput: Disables input buffering.\n"); - mprint(" -bs --buffersize val: Specify a size for reading, in bytes (suffix with K or\n"); + mprint(" --bufferinput: Forces input buffering.\n"); + mprint(" --no-bufferinput: Disables input buffering.\n"); + mprint(" --buffersize val: Specify a size for reading, in bytes (suffix with K or\n"); mprint(" or M for kilobytes and megabytes). Default is 16M.\n"); - mprint(" -koc: keep-output-close. If used then CCExtractor will close\n"); + mprint(" --koc: keep-output-close. If used then CCExtractor will close\n"); mprint(" the output file after writing each subtitle frame and\n"); mprint(" attempt to create it again when needed.\n"); - mprint(" -ff --forceflush: Flush the file buffer whenever content is written.\n"); + mprint(" --forceflush: Flush the file buffer whenever content is written.\n"); mprint("\n"); mprint("Options that affect the built-in 608 closed caption decoder:\n"); - mprint(" -dru: Direct Roll-Up. When in roll-up mode, write character by\n"); + mprint(" --dru: Direct Roll-Up. When in roll-up mode, write character by\n"); mprint(" character instead of line by line. Note that this\n"); mprint(" produces (much) larger files.\n"); - mprint(" -noru --norollup: If you hate the repeated lines caused by the roll-up\n"); + mprint(" --no-rollup: If you hate the repeated lines caused by the roll-up\n"); mprint(" emulation, you can have ccextractor write only one\n"); mprint(" line at a time, getting rid of these repeated lines.\n"); - mprint(" -ru1 / ru2 / ru3: roll-up captions can consist of 2, 3 or 4 visible\n"); + mprint(" --ru1 / ru2 / ru3: roll-up captions can consist of 2, 3 or 4 visible\n"); mprint(" lines at any time (the number of lines is part of\n"); mprint(" the transmission). If having 3 or 4 lines annoys\n"); - mprint(" you you can use -ru to force the decoder to always\n"); + mprint(" you you can use --ru to force the decoder to always\n"); mprint(" use 1, 2 or 3 lines. Note that 1 line is not\n"); mprint(" a real mode rollup mode, so CCExtractor does what\n"); mprint(" it can.\n"); - mprint(" In -ru1 the start timestamp is actually the timestamp\n"); + mprint(" In --ru1 the start timestamp is actually the timestamp\n"); mprint(" of the first character received which is possibly more\n"); mprint(" accurate.\n"); mprint("\n"); mprint("Options that affect timing:\n"); - mprint(" -delay ms: For srt/sami/webvtt, add this number of milliseconds to\n"); - mprint(" all times. For example, -delay 400 makes subtitles\n"); + mprint(" --delay ms: For srt/sami/webvtt, add this number of milliseconds to\n"); + mprint(" all times. For example, --delay 400 makes subtitles\n"); mprint(" appear 400ms late. You can also use negative numbers\n"); mprint(" to make subs appear early.\n"); - mprint("Notes on times: -startat and -endat times are used first, then -delay.\n"); - mprint("So if you use -srt -startat 3:00 -endat 5:00 -delay 120000, ccextractor will\n"); - mprint("generate a .srt file, with only data from 3:00 to 5:00 in the input file(s)\n"); - mprint("and then add that (huge) delay, which would make the final file start at\n"); - mprint("5:00 and end at 7:00.\n\n"); mprint("Options that affect what segment of the input file(s) to process:\n"); - mprint(" -startat time: Only write caption information that starts after the\n"); + mprint(" --startat time: Only write caption information that starts after the\n"); mprint(" given time.\n"); mprint(" Time can be seconds, MM:SS or HH:MM:SS.\n"); - mprint(" For example, -startat 3:00 means 'start writing from\n"); + mprint(" For example, --startat 3:00 means 'start writing from\n"); mprint(" minute 3.\n"); - mprint(" -endat time: Stop processing after the given time (same format as\n"); + mprint(" --endat time: Stop processing after the given time (same format as\n"); mprint(" -startat).\n"); - mprint(" The -startat and -endat options are honored in all\n"); + mprint(" The --startat and --endat options are honored in all\n"); mprint(" output formats. In all formats with timing information\n"); mprint(" the times are unchanged.\n"); - mprint("-scr --screenfuls num: Write 'num' screenfuls and terminate processing.\n\n"); + mprint(" --screenfuls num: Write 'num' screenfuls and terminate processing.\n\n"); mprint("Options that affect which codec is to be used have to be searched in input\n"); - mprint(" If codec type is not selected then first elementary stream suitable for \n" - " subtitle is selected, please consider -teletext -noteletext override this\n" - " option.\n" - " -codec dvbsub select the dvb subtitle from all elementary stream,\n" + mprint(" --codec dvbsub select the dvb subtitle from all elementary stream,\n" " if stream of dvb subtitle type is not found then \n" " nothing is selected and no subtitle is generated\n" - " -nocodec dvbsub ignore dvb subtitle and follow default behaviour\n" - " -codec teletext select the teletext subtitle from elementary stream\n" - " -nocodec teletext ignore teletext subtitle\n" - " NOTE: option given in form -foo=bar ,-foo = bar and --foo=bar are invalid\n" - " valid option are only in form -foo bar\n" - " nocodec and codec parameter must not be same if found to be same \n" - " then parameter of nocodec is ignored, this flag should be passed \n" - " once, more then one are not supported yet and last parameter would \n" - " taken in consideration\n"); + " --no-codec dvbsub ignore dvb subtitle and follow default behaviour\n" + " --codec teletext select the teletext subtitle from elementary stream\n" + " --no-codec teletext ignore teletext subtitle\n"); mprint("Adding start and end credits:\n"); - mprint(" CCExtractor can _try_ to add a custom message (for credits for example) at\n"); - mprint(" the start and end of the file, looking for a window where there are no\n"); - mprint(" captions. If there is no such window, then no text will be added.\n"); - mprint(" The start window must be between the times given and must have enough time\n"); - mprint(" to display the message for at least the specified time.\n"); mprint(" --startcreditstext txt: Write this text as start credits. If there are\n"); mprint(" several lines, separate them with the\n"); mprint(" characters \\n, for example Line1\\nLine 2.\n"); @@ -816,29 +774,29 @@ void print_usage(void) mprint("Options that affect debug data:\n"); - mprint(" -debug: Show lots of debugging output.\n"); - mprint(" -608: Print debug traces from the EIA-608 decoder.\n"); + mprint(" --debug: Show lots of debugging output.\n"); + mprint(" --608: Print debug traces from the EIA-608 decoder.\n"); mprint(" If you need to submit a bug report, please send\n"); mprint(" the output from this option.\n"); - mprint(" -708: Print debug information from the (currently\n"); + mprint(" --708: Print debug information from the (currently\n"); mprint(" in development) EIA-708 (DTV) decoder.\n"); - mprint(" -goppts: Enable lots of time stamp output.\n"); - mprint(" -xdsdebug: Enable XDS debug data (lots of it).\n"); - mprint(" -vides: Print debug info about the analysed elementary\n"); + mprint(" --goppts: Enable lots of time stamp output.\n"); + mprint(" --xdsdebug: Enable XDS debug data (lots of it).\n"); + mprint(" --vides: Print debug info about the analysed elementary\n"); mprint(" video stream.\n"); - mprint(" -cbraw: Print debug trace with the raw 608/708 data with\n"); + mprint(" --cbraw: Print debug trace with the raw 608/708 data with\n"); mprint(" time stamps.\n"); - mprint(" -nosync: Disable the syncing code. Only useful for debugging\n"); + mprint(" --no-sync: Disable the syncing code. Only useful for debugging\n"); mprint(" purposes.\n"); - mprint(" -fullbin: Disable the removal of trailing padding blocks\n"); + mprint(" --fullbin: Disable the removal of trailing padding blocks\n"); mprint(" when exporting to bin format. Only useful for\n"); mprint(" for debugging purposes.\n"); - mprint(" -parsedebug: Print debug info about the parsed container\n"); + mprint(" --parsedebug: Print debug info about the parsed container\n"); mprint(" file. (Only for TS/ASF files at the moment.)\n"); - mprint(" -parsePAT: Print Program Association Table dump.\n"); - mprint(" -parsePMT: Print Program Map Table dump.\n"); - mprint(" -dumpdef: Hex-dump defective TS packets.\n"); - mprint(" -investigate_packets: If no CC packets are detected based on the PMT, try\n"); + mprint(" --parsePAT: Print Program Association Table dump.\n"); + mprint(" --parsePMT: Print Program Map Table dump.\n"); + mprint(" --dumpdef: Hex-dump defective TS packets.\n"); + mprint(" --investigate-packets: If no CC packets are detected based on the PMT, try\n"); mprint(" to find data in all packets by scanning.\n"); #ifdef ENABLE_SHARING mprint(" -sharing-debug: Print extracted CC sharing service messages\n"); @@ -847,14 +805,14 @@ void print_usage(void) mprint("Teletext related options:\n"); - mprint(" -tpage page: Use this page for subtitles (if this parameter\n"); + mprint(" --tpage page: Use this page for subtitles (if this parameter\n"); mprint(" is not used, try to autodetect). In Spain the\n"); mprint(" page is always 888, may vary in other countries.\n"); - mprint(" -tverbose: Enable verbose mode in the teletext decoder.\n\n"); - mprint(" -teletext: Force teletext mode even if teletext is not detected.\n"); - mprint(" If used, you should also pass -datapid to specify\n"); + mprint(" --tverbose: Enable verbose mode in the teletext decoder.\n\n"); + mprint(" --teletext: Force teletext mode even if teletext is not detected.\n"); + mprint(" If used, you should also pass --datapid to specify\n"); mprint(" the stream ID you want to process.\n"); - mprint(" -noteletext: Disable teletext processing. This might be needed\n"); + mprint(" --no-teletext: Disable teletext processing. This might be needed\n"); mprint(" for video streams that have both teletext packets\n"); mprint(" and CEA-608/708 packets (if teletext is processed\n"); mprint(" then CEA-608/708 processing is disabled).\n"); @@ -862,7 +820,7 @@ void print_usage(void) mprint("Transcript customizing options:\n"); - mprint(" -customtxt format: Use the passed format to customize the (Timed) Transcript\n"); + mprint(" --customtxt format: Use the passed format to customize the (Timed) Transcript\n"); mprint(" output. The format must be like this: 1100100 (7 digits).\n"); mprint(" These indicate whether the next things should be\n"); mprint(" displayed or not in the (timed) transcript. They\n"); @@ -880,117 +838,164 @@ void print_usage(void) mprint(" 1111001 is the default setting for -ucla\n"); mprint(" Make sure you use this parameter after others that might\n"); mprint(" affect these settings (-out, -ucla, -xds, -txt, \n"); - mprint(" -ttxt ...)\n"); + mprint(" --ttxt ...)\n"); mprint("\n"); mprint("Communication with other programs and console output:\n"); - mprint(" --gui_mode_reports: Report progress and interesting events to stderr\n"); + mprint(" --gui-mode-reports: Report progress and interesting events to stderr\n"); mprint(" in a easy to parse format. This is intended to be\n"); mprint(" used by other programs. See docs directory for.\n"); mprint(" details.\n"); - mprint(" --no_progress_bar: Suppress the output of the progress bar\n"); - mprint(" -quiet: Don't write any message.\n"); + mprint(" --no-progress-bar: Suppress the output of the progress bar\n"); + mprint(" --quiet: Don't write any message.\n"); mprint("\n"); #ifdef ENABLE_SHARING mprint("Sharing extracted captions via TCP:\n"); - mprint(" -enable-sharing: Enables real-time sharing of extracted captions\n"); - mprint(" -sharing-url: Set url for sharing service in nanomsg format. Default: \"tcp://*:3269\"\n"); + mprint(" --enable-sharing: Enables real-time sharing of extracted captions\n"); + mprint(" --sharing-url: Set url for sharing service in nanomsg format. Default: \"tcp://*:3269\"\n"); mprint("\n"); mprint("CCTranslate application integration:\n"); - mprint(" -translate: Enable Translation tool and set target languages\n"); - mprint(" in csv format (e.g. -translate ru,fr,it\n"); - mprint(" -translate-auth: Set Translation Service authorization data to make translation possible\n"); + mprint(" --translate: Enable Translation tool and set target languages\n"); + mprint(" in csv format (e.g. --translate ru,fr,it\n"); + mprint(" --translate-auth: Set Translation Service authorization data to make translation possible\n"); mprint(" In case of Google Translate API - API Key\n"); #endif // ENABLE_SHARING - - mprint("Notes on the CEA-708 decoder: While it is starting to be useful, it's\n"); - mprint("a work in progress. A number of things don't work yet in the decoder\n"); - mprint("itself, and many of the auxiliary tools (case conversion to name one)\n"); - mprint("won't do anything yet. Feel free to submit samples that cause problems\n"); - mprint("and feature requests.\n"); - mprint("\n"); - mprint("Notes on spupng output format:\n"); - mprint("One .xml file is created per output field. A set of .png files are created in\n"); - mprint("a directory with the same base name as the corresponding .xml file(s), but with\n"); - 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("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("will create the files:\n"); - mprint(" /tmp/output_1.xml\n"); - mprint(" /tmp/output_1.d/sub0000.png\n"); - mprint(" /tmp/output_1.d/sub0001.png\n"); - mprint(" ...\n"); - mprint(" /tmp/output_2.xml\n"); - mprint(" /tmp/output_2.d/sub0000.png\n"); - mprint(" /tmp/output_2.d/sub0001.png\n"); - mprint(" ...\n"); - mprint("\n"); mprint("Burned-in subtitle extraction:\n"); - mprint(" -hardsubx : Enable the burned-in subtitle extraction subsystem.\n"); + mprint(" --hardsubx : Enable the burned-in subtitle extraction subsystem.\n"); mprint("\n"); - mprint(" NOTE: The following options will work only if -hardsubx is \n"); - mprint(" specified before them:-\n"); + mprint(" NOTE: This is needed to use the below burned-in \n"); + mprint(" subtitle extractor options\n"); mprint("\n"); - mprint(" -tickertext : Search for burned-in ticker text at the bottom of\n"); + mprint(" --tickertext : Search for burned-in ticker text at the bottom of\n"); mprint(" the screen.\n"); mprint("\n"); - mprint(" -ocr_mode : Set the OCR mode to either frame-wise, word-wise\n"); + mprint(" --ocr-mode : Set the OCR mode to either frame-wise, word-wise\n"); mprint(" or letter wise.\n"); - mprint(" e.g. -ocr_mode frame (default), -ocr_mode word, \n"); - mprint(" -ocr_mode letter\n"); + mprint(" e.g. --ocr-mode frame (default), --ocr-mode word, \n"); + mprint(" --ocr-mode letter\n"); mprint("\n"); - mprint(" -subcolor : Specify the color of the subtitles\n"); + mprint(" --subcolor : Specify the color of the subtitles\n"); mprint(" Possible values are in the set \n"); mprint(" {white,yellow,green,cyan,blue,magenta,red}.\n"); mprint(" Alternatively, a custom hue value between 1 and 360 \n"); mprint(" may also be specified.\n"); - mprint(" e.g. -subcolor white or -subcolor 270 (for violet).\n"); + mprint(" e.g. --subcolor white or --subcolor 270 (for violet).\n"); mprint(" Refer to an HSV color chart for values.\n"); mprint("\n"); - mprint(" -min_sub_duration : Specify the minimum duration that a subtitle line \n"); + mprint(" --min-sub-duration : Specify the minimum duration that a subtitle line \n"); mprint(" must exist on the screen.\n"); mprint(" The value is specified in seconds.\n"); mprint(" A lower value gives better results, but takes more \n"); mprint(" processing time.\n"); mprint(" The recommended value is 0.5 (default).\n"); - mprint(" e.g. -min_sub_duration 1.0 (for a duration of 1 second)\n"); + mprint(" e.g. --min-sub-duration 1.0 (for a duration of 1 second)\n"); mprint("\n"); - mprint(" -detect_italics : Specify whether italics are to be detected from the \n"); + mprint(" --detect-italics : Specify whether italics are to be detected from the \n"); mprint(" OCR text.\n"); mprint(" Italic detection automatically enforces the OCR mode \n"); mprint(" to be word-wise"); mprint("\n"); - mprint(" -conf_thresh : Specify the classifier confidence threshold between\n"); + mprint(" --conf-thresh : Specify the classifier confidence threshold between\n"); mprint(" 1 and 100.\n"); mprint(" Try and use a threshold which works for you if you get \n"); mprint(" a lot of garbage text.\n"); - mprint(" e.g. -conf_thresh 50\n"); + mprint(" e.g. --conf-thresh 50\n"); mprint("\n"); - mprint(" -whiteness_thresh : For white subtitles only, specify the luminance \n"); + mprint(" --whiteness-thresh : For white subtitles only, specify the luminance \n"); mprint(" threshold between 1 and 100\n"); mprint(" This threshold is content dependent, and adjusting\n"); mprint(" values may give you better results\n"); mprint(" Recommended values are in the range 80 to 100.\n"); mprint(" The default value is 95\n"); mprint("\n"); - mprint(" -hcc : This option will be used if the file should have both\n"); + mprint(" --hcc : This option will be used if the file should have both\n"); mprint(" closed captions and burned in subtitles\n"); mprint(" An example command for burned-in subtitle extraction is as follows:\n"); - mprint(" ccextractor video.mp4 -hardsubx -subcolor white -detect_italics \n"); - mprint(" -whiteness_thresh 90 -conf_thresh 60\n"); + mprint(" ccextractor video.mp4 --hardsubx -subcolor white --detect-italics \n"); + mprint(" --whiteness-thresh 90 --conf-thresh 60\n"); mprint("\n"); mprint("\n --version : Display current CCExtractor version and detailed information.\n"); + mprint("\n"); + mprint("Notes on File name related options:\n"); + mprint(" You can pass as many input files as you need. They will be processed in order.\n"); + mprint(" If a file name is suffixed by +, ccextractor will try to follow a numerical\n"); + mprint(" sequence. For example, DVD001.VOB+ means DVD001.VOB, DVD002.VOB and so on\n"); + mprint(" until there are no more files.\n"); + mprint(" Output will be one single file (either raw or srt). Use this if you made your\n"); + mprint(" recording in several cuts (to skip commercials for example) but you want one\n"); + mprint(" subtitle file with contiguous timing.\n"); + mprint("\n"); + mprint("Notes on Options that affect what will be processed:\n"); + mprint(" In general, if you want English subtitles you don't need to use these options\n"); + mprint(" as they are broadcast in field 1, channel 1. If you want the second language\n"); + mprint(" (usually Spanish) you may need to try -2, or -cc2, or both.\n"); + mprint("\n"); + mprint("Notes on Levenshtein distance:\n"); + mprint(" When processing teletext files CCExtractor tries to correct typos by\n"); + mprint(" comparing consecutive lines. If line N+1 is almost identical to line N except\n"); + mprint(" for minor changes (plus next characters) then it assumes that line N that a\n"); + mprint(" typo that was corrected in N+1. This is currently implemented in teletext\n"); + mprint(" because it's where samples files that could benefit from this were available.\n"); + mprint(" You can adjust, or disable, the algorithm settings with the following\n"); + mprint(" parameters.\n"); + mprint("\n"); + mprint("Notes on times:\n" + " --startat and --endat times are used first, then -delay.\n"); + mprint(" So if you use --srt -startat 3:00 --endat 5:00 --delay 120000, ccextractor will\n"); + mprint(" generate a .srt file, with only data from 3:00 to 5:00 in the input file(s)\n"); + mprint(" and then add that (huge) delay, which would make the final file start at\n"); + mprint(" 5:00 and end at 7:00.\n"); + mprint("\n"); + mprint("Notes on codec options:\n"); + mprint(" If codec type is not selected then first elementary stream suitable for \n" + " subtitle is selected, please consider --teletext -noteletext override this\n" + " option.\n"); + mprint(" no-codec and codec parameter must not be same if found to be same \n" + " then parameter of no-codec is ignored, this flag should be passed \n" + " once, more then one are not supported yet and last parameter would \n" + " taken in consideration\n"); + mprint("\n"); + mprint("Notes on adding credits:\n"); + mprint(" CCExtractor can _try_ to add a custom message (for credits for example) at\n"); + mprint(" the start and end of the file, looking for a window where there are no\n"); + mprint(" captions. If there is no such window, then no text will be added.\n"); + mprint(" The start window must be between the times given and must have enough time\n"); + mprint(" to display the message for at least the specified time.\n"); + mprint("\n"); + mprint("Notes on the CEA-708 decoder:\n" + " While it is starting to be useful, it's\n"); + mprint(" a work in progress. A number of things don't work yet in the decoder\n"); + mprint(" itself, and many of the auxiliary tools (case conversion to name one)\n"); + mprint(" won't do anything yet. Feel free to submit samples that cause problems\n"); + mprint(" and feature requests.\n"); + mprint("\n"); + mprint("Notes on spupng output format:\n"); + mprint(" One .xml file is created per output field. A set of .png files are created in\n"); + mprint(" a directory with the same base name as the corresponding .xml file(s), but with\n"); + 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(" 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(" will create the files:\n"); + mprint(" /tmp/output_1.xml\n"); + mprint(" /tmp/output_1.d/sub0000.png\n"); + mprint(" /tmp/output_1.d/sub0001.png\n"); + mprint(" ...\n"); + mprint(" /tmp/output_2.xml\n"); + mprint(" /tmp/output_2.d/sub0000.png\n"); + mprint(" /tmp/output_2.d/sub0001.png\n"); + mprint(" ..."); + mprint("\n"); } unsigned char sha256_buf[16384]; @@ -1238,7 +1243,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) version(argv[0]); return EXIT_WITH_HELP; } - if (strcmp(argv[i], "-") == 0 || strcmp(argv[i], "-stdin") == 0) + if (strcmp(argv[i], "-") == 0 || strcmp(argv[i], "--stdin") == 0) { #ifdef WIN32 setmode(fileno(stdin), O_BINARY); @@ -1268,21 +1273,21 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } #ifdef ENABLE_HARDSUBX - // Parse -hardsubx and related parameters - if (strcmp(argv[i], "-hardsubx") == 0) + // Parse --hardsubx and related parameters + if (strcmp(argv[i], "--hardsubx") == 0) { opt->hardsubx = 1; continue; } if (opt->hardsubx == 1) { - if (strcmp(argv[i], "-hcc") == 0) + if (strcmp(argv[i], "--hcc") == 0) { // if extraction of both burned in and non burned in subs opt->hardsubx_and_common = 1; continue; } - if (strcmp(argv[i], "-ocr_mode") == 0) + if (strcmp(argv[i], "--ocr-mode") == 0) { if (i < argc - 1) { @@ -1312,7 +1317,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) fatal(EXIT_MALFORMED_PARAMETER, "-ocr_mode has no argument.\nValid values are {frame,word,letter}\n"); } } - if (strcmp(argv[i], "-subcolor") == 0 || strcmp(argv[i], "-sub_color") == 0) + if (strcmp(argv[i], "--subcolor") == 0) { if (i < argc - 1) { @@ -1362,7 +1367,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) opt->hardsubx_hue = atof(str); if (opt->hardsubx_hue <= 0.0 || opt->hardsubx_hue > 360.0) { - fatal(EXIT_MALFORMED_PARAMETER, "-subcolor has either 0 or an invalid hue value supplied.\nIf you want to detect red subtitles, pass '-subcolor red' or a slightly higher hue value (e.g. 0.1)\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--subcolor has either 0 or an invalid hue value supplied.\nIf you want to detect red subtitles, pass '-subcolor red' or a slightly higher hue value (e.g. 0.1)\n"); } } @@ -1370,10 +1375,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-subcolor has no argument.\nValid values are {white,yellow,green,cyan,blue,magenta,red} or a custom hue value between 0 and 360\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--subcolor has no argument.\nValid values are {white,yellow,green,cyan,blue,magenta,red} or a custom hue value between 0 and 360\n"); } } - if (strcmp(argv[i], "-min_sub_duration") == 0) + if (strcmp(argv[i], "--min-sub-duration") == 0) { if (i < argc - 1) { @@ -1384,22 +1389,22 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) opt->hardsubx_min_sub_duration = atof(str); if (opt->hardsubx_min_sub_duration == 0.0) { - fatal(EXIT_MALFORMED_PARAMETER, "-min_sub_duration has either 0 or an invalid value supplied\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--min-sub-duration has either 0 or an invalid value supplied\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-min_sub_duration has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--min-sub-duration has no argument.\n"); } } - if (strcmp(argv[i], "-detect_italics") == 0) + if (strcmp(argv[i], "--detect-italics") == 0) { opt->hardsubx_detect_italics = 1; continue; } - if (strcmp(argv[i], "-conf_thresh") == 0) + if (strcmp(argv[i], "--conf-thresh") == 0) { if (i < argc - 1) { @@ -1410,17 +1415,17 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) opt->hardsubx_conf_thresh = atof(str); if (opt->hardsubx_conf_thresh <= 0.0 || opt->hardsubx_conf_thresh > 100.0) { - fatal(EXIT_MALFORMED_PARAMETER, "-conf_thresh has either 0 or an invalid value supplied\nValid values are in (0.0,100.0)\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--conf-thresh has either 0 or an invalid value supplied\nValid values are in (0.0,100.0)\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-conf_thresh has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--conf-thresh has no argument.\n"); } } - if (strcmp(argv[i], "-whiteness_thresh") == 0 || strcmp(argv[i], "-lum_thresh") == 0) + if (strcmp(argv[i], "--whiteness-thresh") == 0 || strcmp(argv[i], "--lum-thresh") == 0) { if (i < argc - 1) { @@ -1431,40 +1436,40 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) opt->hardsubx_lum_thresh = atof(str); if (opt->hardsubx_lum_thresh <= 0.0 || opt->hardsubx_conf_thresh > 100.0) { - fatal(EXIT_MALFORMED_PARAMETER, "-whiteness_thresh has either 0 or an invalid value supplied\nValid values are in (0.0,100.0)\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--whiteness-thresh has either 0 or an invalid value supplied\nValid values are in (0.0,100.0)\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-whiteness_thresh has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--whiteness-thresh has no argument.\n"); } } } #endif // ENABLE_HARDSUBX - if (strcmp(argv[i], "-chapters") == 0) + if (strcmp(argv[i], "--chapters") == 0) { opt->extract_chapters = 1; continue; } - if (strcmp(argv[i], "-bi") == 0 || strcmp(argv[i], "--bufferinput") == 0) + if (strcmp(argv[i], "--bufferinput") == 0) { opt->buffer_input = 1; continue; } - if (strcmp(argv[i], "-nobi") == 0 || strcmp(argv[i], "--nobufferinput") == 0) + if (strcmp(argv[i], "--no-bufferinput") == 0) { opt->buffer_input = 0; continue; } - if (strcmp(argv[i], "-koc") == 0) + if (strcmp(argv[i], "--koc") == 0) { opt->keep_output_closed = 1; continue; } - if (strcmp(argv[i], "-ff") == 0 || strcmp(argv[i], "--forceflush") == 0) + if (strcmp(argv[i], "--forceflush") == 0) { opt->force_flush = 1; continue; @@ -1474,7 +1479,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) opt->append_mode = 1; continue; } - if (strcmp(argv[i], "-bs") == 0 || strcmp(argv[i], "--buffersize") == 0) + if (strcmp(argv[i], "--buffersize") == 0) { if (i < argc - 1) { @@ -1493,69 +1498,69 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) fatal(EXIT_MALFORMED_PARAMETER, "--buffersize has no argument.\n"); } } - if (strcmp(argv[i], "-dru") == 0) + if (strcmp(argv[i], "--dru") == 0) { opt->settings_608.direct_rollup = 1; continue; } - if (strcmp(argv[i], "-nofc") == 0 || strcmp(argv[i], "--nofontcolor") == 0) + if (strcmp(argv[i], "--no-fontcolor") == 0) { opt->nofontcolor = 1; continue; } - if (strcmp(argv[i], "--nohtmlescape") == 0) + if (strcmp(argv[i], "--no-htmlescape") == 0) { opt->nohtmlescape = 1; continue; } - if (strcmp(argv[i], "-bom") == 0) + if (strcmp(argv[i], "--bom") == 0) { opt->enc_cfg.no_bom = 0; continue; } - if (strcmp(argv[i], "-nobom") == 0) + if (strcmp(argv[i], "--no-bom") == 0) { opt->enc_cfg.no_bom = 1; continue; } - if (strcmp(argv[i], "-sem") == 0) + if (strcmp(argv[i], "--sem") == 0) { opt->enc_cfg.with_semaphore = 1; continue; } - if (strcmp(argv[i], "-nots") == 0 || strcmp(argv[i], "--notypesetting") == 0) + if (strcmp(argv[i], "--no-typesetting") == 0) { opt->notypesetting = 1; continue; } - if (strcmp(argv[i], "--timestamp-map") == 0 || strcmp(argv[i], "-tm") == 0) + if (strcmp(argv[i], "--timestamp-map") == 0) { opt->timestamp_map = 1; continue; } /* Input file formats */ - if (strcmp(argv[i], "-es") == 0 || - strcmp(argv[i], "-ts") == 0 || - strcmp(argv[i], "-ps") == 0 || - strcmp(argv[i], "-asf") == 0 || - strcmp(argv[i], "-wtv") == 0 || - strcmp(argv[i], "-mp4") == 0 || - strcmp(argv[i], "-mkv") == 0 || + if (strcmp(argv[i], "--es") == 0 || + strcmp(argv[i], "--ts") == 0 || + strcmp(argv[i], "--ps") == 0 || + strcmp(argv[i], "--asf") == 0 || + strcmp(argv[i], "--wtv") == 0 || + strcmp(argv[i], "--mp4") == 0 || + strcmp(argv[i], "--mkv") == 0 || strcmp(argv[i], "--dvr-ms") == 0) { set_input_format(opt, argv[i]); continue; } - if (strncmp(argv[i], "-in=", 4) == 0) + if (strncmp(argv[i], "--in=", 5) == 0) { - set_input_format(opt, argv[i] + 4); + set_input_format(opt, argv[i] + 5); continue; } /*user specified subtitle to be selected */ - if (strcmp(argv[i], "-codec") == 0) + if (strcmp(argv[i], "--codec") == 0) { if (i < argc - 1) { @@ -1578,12 +1583,12 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-codec has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--codec has no argument.\n"); } } /*user specified subtitle to be selected */ - if (strcmp(argv[i], "-nocodec") == 0) + if (strcmp(argv[i], "--no-codec") == 0) { if (i < argc - 1) { @@ -1599,18 +1604,18 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "Invalid option for nocodec.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "Invalid option for no-codec.\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-nocodec has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--no-codec has no argument.\n"); } } - if (strcmp(argv[i], "-dvblang") == 0) + if (strcmp(argv[i], "--dvblang") == 0) { if (i < argc - 1) { @@ -1627,11 +1632,11 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-dvblang has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--dvblang has no argument.\n"); } } - if (strcmp(argv[i], "-ocrlang") == 0) + if (strcmp(argv[i], "--ocrlang") == 0) { if (i++ < argc - 1) { @@ -1640,10 +1645,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-ocrlang has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--ocrlang has no argument.\n"); } } - if (strcmp(argv[i], "-quant") == 0) + if (strcmp(argv[i], "--quant") == 0) { if (i < argc - 1) { @@ -1653,15 +1658,15 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-quant has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--quant has no argument.\n"); } } - if (strcmp(argv[i], "-nospupngocr") == 0) + if (strcmp(argv[i], "--no-spupngocr") == 0) { opt->enc_cfg.nospupngocr = 1; continue; } - if (strcmp(argv[i], "-oem") == 0) + if (strcmp(argv[i], "--oem") == 0) { if (i < argc - 1) { @@ -1672,17 +1677,17 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) opt->ocr_oem = atoi(str); if (opt->ocr_oem < 0 || opt->ocr_oem > 2) { - fatal(EXIT_MALFORMED_PARAMETER, "-oem must be 0, 1 or 2\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--oem must be 0, 1 or 2\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-oem has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--oem has no argument.\n"); } } - if (strcmp(argv[i], "-mkvlang") == 0) + if (strcmp(argv[i], "--mkvlang") == 0) { if (i < argc - 1) { @@ -1696,24 +1701,24 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-mkvlang has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--mkvlang has no argument.\n"); } } /* Output file formats */ - if (strcmp(argv[i], "-srt") == 0 || - strcmp(argv[i], "-mcc") == 0 || strcmp(argv[i], "-dvdraw") == 0 || - strcmp(argv[i], "-smi") == 0 || strcmp(argv[i], "-sami") == 0 || - strcmp(argv[i], "-txt") == 0 || strcmp(argv[i], "--transcript") == 0 || - strcmp(argv[i], "-ttxt") == 0 || strcmp(argv[i], "--timedtranscript") == 0 || - strcmp(argv[i], "-webvtt") == 0 || strcmp(argv[i], "-null") == 0) + if (strcmp(argv[i], "--srt") == 0 || + strcmp(argv[i], "--mcc") == 0 || strcmp(argv[i], "--dvdraw") == 0 || + strcmp(argv[i], "--smi") == 0 || strcmp(argv[i], "--sami") == 0 || + strcmp(argv[i], "--txt") == 0 || strcmp(argv[i], "--transcript") == 0 || + strcmp(argv[i], "--ttxt") == 0 || strcmp(argv[i], "--timedtranscript") == 0 || + strcmp(argv[i], "--webvtt") == 0 || strcmp(argv[i], "--null") == 0) { set_output_format(opt, argv[i]); continue; } - if (strncmp(argv[i], "-out=", 5) == 0) + if (strncmp(argv[i], "--out=", 6) == 0) { - set_output_format(opt, argv[i] + 5); + set_output_format(opt, argv[i] + 6); continue; } @@ -1842,38 +1847,32 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } /* More stuff */ - if (strcmp(argv[i], "-ve") == 0 || strcmp(argv[i], "--videoedited") == 0) + if (strcmp(argv[i], "--videoedited") == 0) { opt->binary_concat = 0; continue; } - if (strcmp(argv[i], "-12") == 0) - { - opt->is_608_enabled = 1; - opt->extract = 12; - continue; - } - if (strcmp(argv[i], "-gt") == 0 || strcmp(argv[i], "--goptime") == 0) + if (strcmp(argv[i], "--goptime") == 0) { opt->use_gop_as_pts = 1; continue; } - if (strcmp(argv[i], "-nogt") == 0 || strcmp(argv[i], "--nogoptime") == 0) + if (strcmp(argv[i], "--no-goptime") == 0) { opt->use_gop_as_pts = -1; // Don't use even if we would want to continue; } - if (strcmp(argv[i], "-fp") == 0 || strcmp(argv[i], "--fixpadding") == 0) + if (strcmp(argv[i], "--fixpadding") == 0) { opt->fix_padding = 1; continue; } - if (strcmp(argv[i], "-90090") == 0) + if (strcmp(argv[i], "--90090") == 0) { MPEG_CLOCK_FREQ = 90090; continue; } - if (strcmp(argv[i], "--noscte20") == 0) + if (strcmp(argv[i], "--no-scte20") == 0) { opt->noscte20 = 1; continue; @@ -1883,34 +1882,34 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) opt->webvtt_create_css = 1; continue; } - if (strcmp(argv[i], "-noru") == 0 || strcmp(argv[i], "--norollup") == 0) + if (strcmp(argv[i], "--no-rollup") == 0) { opt->no_rollup = 1; opt->settings_608.no_rollup = 1; opt->settings_dtvcc.no_rollup = 1; continue; } - if (strcmp(argv[i], "-ru1") == 0) + if (strcmp(argv[i], "--ru1") == 0) { opt->settings_608.force_rollup = 1; continue; } - if (strcmp(argv[i], "-ru2") == 0) + if (strcmp(argv[i], "--ru2") == 0) { opt->settings_608.force_rollup = 2; continue; } - if (strcmp(argv[i], "-ru3") == 0) + if (strcmp(argv[i], "--ru3") == 0) { opt->settings_608.force_rollup = 3; continue; } - if (strcmp(argv[i], "-trim") == 0) + if (strcmp(argv[i], "--trim") == 0) { opt->enc_cfg.trim_subs = 1; continue; } - if (strcmp(argv[i], "-outinterval") == 0) + if (strcmp(argv[i], "--outinterval") == 0) { if (i < argc - 1) { @@ -1923,35 +1922,35 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) fatal(EXIT_MALFORMED_PARAMETER, "-outinterval has no argument.\n"); } } - if (strcmp(argv[i], "--segmentonkeyonly") == 0 || strcmp(argv[i], "-key") == 0) + if (strcmp(argv[i], "--segmentonkeyonly") == 0) { opt->segment_on_key_frames_only = 1; opt->analyze_video_stream = 1; continue; } - if (strcmp(argv[i], "--gui_mode_reports") == 0) + if (strcmp(argv[i], "--gui-mode-reports") == 0) { opt->gui_mode_reports = 1; continue; } - if (strcmp(argv[i], "--no_progress_bar") == 0) + if (strcmp(argv[i], "--no-progress-bar") == 0) { opt->no_progress_bar = 1; continue; } - if (strcmp(argv[i], "--splitbysentence") == 0 || strcmp(argv[i], "-sbs") == 0) + if (strcmp(argv[i], "--splitbysentence") == 0) { opt->enc_cfg.splitbysentence = 1; continue; } - if (strcmp(argv[i], "--sentencecap") == 0 || strcmp(argv[i], "-sc") == 0) + if (strcmp(argv[i], "--sentencecap") == 0) { opt->enc_cfg.sentence_cap = 1; continue; } - if ((strcmp(argv[i], "--capfile") == 0 || strcmp(argv[i], "-caf") == 0) && i < argc - 1) + if (strcmp(argv[i], "--capfile") == 0 && i < argc - 1) { opt->enc_cfg.sentence_cap = 1; opt->sentence_cap_file = argv[i + 1]; @@ -1975,7 +1974,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) continue; } - if (strcmp(argv[i], "--program-number") == 0 || strcmp(argv[i], "-pn") == 0) + if (strcmp(argv[i], "--program-number") == 0) { if (i < argc - 1 && isanumber(argv[i + 1])) { @@ -1990,12 +1989,12 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) continue; } } - if (strcmp(argv[i], "-autoprogram") == 0) + if (strcmp(argv[i], "--autoprogram") == 0) { opt->demux_cfg.ts_autoprogram = 1; continue; } - if (strcmp(argv[i], "-multiprogram") == 0) + if (strcmp(argv[i], "--multiprogram") == 0) { opt->multiprogram = 1; opt->demux_cfg.ts_allprogram = CCX_TRUE; @@ -2015,7 +2014,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) continue; } } - if (strcmp(argv[i], "--defaultcolor") == 0 || strcmp(argv[i], "-dc") == 0) + if (strcmp(argv[i], "--defaultcolor") == 0) { if (i < argc - 1) { @@ -2033,23 +2032,23 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) fatal(EXIT_MALFORMED_PARAMETER, "--defaultcolor has no argument.\n"); } } - if (strcmp(argv[i], "-delay") == 0) + if (strcmp(argv[i], "--delay") == 0) { if (i < argc - 1) { i++; if (parsedelay(opt, argv[i])) { - fatal(EXIT_MALFORMED_PARAMETER, "-delay only accept integers (such as -300 or 300)\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--delay only accept integers (such as --300 or 300)\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-delay has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--delay has no argument.\n"); } } - if (strcmp(argv[i], "-scr") == 0 || strcmp(argv[i], "--screenfuls") == 0) + if (strcmp(argv[i], "--screenfuls") == 0) { if (i < argc - 1) { @@ -2066,111 +2065,118 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) fatal(EXIT_MALFORMED_PARAMETER, "--screenfuls has no argument.\n"); } } - if (strcmp(argv[i], "-startat") == 0) + if (strcmp(argv[i], "--startat") == 0) { if (i < argc - 1) { i++; if (stringztoms(argv[i], &opt->extraction_start) == -1) { - fatal(EXIT_MALFORMED_PARAMETER, "-startat only accepts SS, MM:SS or HH:MM:SS\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--startat only accepts SS, MM:SS or HH:MM:SS\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-startat has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--startat has no argument.\n"); } } - if (strcmp(argv[i], "-endat") == 0) + if (strcmp(argv[i], "--endat") == 0) { if (i < argc - 1) { i++; if (stringztoms(argv[i], &opt->extraction_end) == -1) { - fatal(EXIT_MALFORMED_PARAMETER, "-endat only accepts SS, MM:SS or HH:MM:SS\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--endat only accepts SS, MM:SS or HH:MM:SS\n"); } continue; } else { - fatal(EXIT_MALFORMED_PARAMETER, "-endat has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--endat has no argument.\n"); } } - if (strcmp(argv[i], "-1") == 0) + if (strcmp(argv[i], "--output-field") == 0) { - opt->is_608_enabled = 1; - opt->extract = 1; - continue; - } - if (strcmp(argv[i], "-2") == 0) - { - opt->is_608_enabled = 1; - opt->extract = 2; - continue; + if (i < argc - 1) + { + i++; + opt->extract = strcmp(argv[i], "both") == 0 ? 12 : atoi_hex(argv[i]); + if (opt->extract != 1 && opt->extract != 2 && opt->extract != 12) + { + fatal(EXIT_MALFORMED_PARAMETER, "--output-field only accepts 1 or 2 or both.\n"); + } + opt->is_608_enabled = 1; + continue; + } + else + { + fatal(EXIT_MALFORMED_PARAMETER, "--output-field has no argument.\n"); + } } - if (strcmp(argv[i], "-cc2") == 0 || strcmp(argv[i], "-CC2") == 0) + + if (strcmp(argv[i], "--cc2") == 0 || strcmp(argv[i], "--CC2") == 0) { opt->cc_channel = 2; continue; } - if (strcmp(argv[i], "-stdout") == 0) + if (strcmp(argv[i], "--stdout") == 0) { - if (opt->messages_target == 1) // Only change this if still stdout. -quiet could set it to 0 for example + if (opt->messages_target == 1) // Only change this if still stdout. --quiet could set it to 0 for example { opt->messages_target = 2; // stderr } opt->cc_to_stdout = 1; continue; } - if (strcmp(argv[i], "-pesheader") == 0) + if (strcmp(argv[i], "--pesheader") == 0) { opt->pes_header_to_stdout = 1; continue; } - if (strcmp(argv[i], "-debugdvbsub") == 0) + if (strcmp(argv[i], "--debugdvbsub") == 0) { opt->debug_mask |= CCX_DMT_DVB; continue; } - if (strcmp(argv[i], "-ignoreptsjumps") == 0) + if (strcmp(argv[i], "--ignoreptsjumps") == 0) { opt->ignore_pts_jumps = 1; continue; } - // -ignoreptsjumps counterpart - if (strcmp(argv[i], "-fixptsjumps") == 0) + // --ignoreptsjumps counterpart + if (strcmp(argv[i], "--fixptsjumps") == 0) { opt->ignore_pts_jumps = 0; continue; } - if (strcmp(argv[i], "-quiet") == 0) + if (strcmp(argv[i], "--quiet") == 0) { opt->messages_target = 0; continue; } - if (strcmp(argv[i], "-debug") == 0) + if (strcmp(argv[i], "--debug") == 0) { opt->debug_mask |= CCX_DMT_VERBOSE; continue; } - if (strcmp(argv[i], "-608") == 0) + if (strcmp(argv[i], "--608") == 0) { opt->debug_mask |= CCX_DMT_DECODER_608; continue; } - if (strcmp(argv[i], "-deblev") == 0) + if (strcmp(argv[i], "--deblev") == 0) { opt->debug_mask |= CCX_DMT_LEVENSHTEIN; continue; } - if (strcmp(argv[i], "-nolevdist") == 0) + if (strcmp(argv[i], "--no-levdist") == 0) { opt->dolevdist = 0; continue; } - if (strcmp(argv[i], "-levdistmincnt") == 0) + if (strcmp(argv[i], "--levdistmincnt") == 0) { if (i < argc - 1) { @@ -2180,10 +2186,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-levdistmincnt has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--levdistmincnt has no argument.\n"); } } - if (strcmp(argv[i], "-levdistmaxpct") == 0) + if (strcmp(argv[i], "--levdistmaxpct") == 0) { if (i < argc - 1) { @@ -2193,10 +2199,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-levdistmaxpct has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--levdistmaxpct has no argument.\n"); } } - if (strcmp(argv[i], "-708") == 0) + if (strcmp(argv[i], "--708") == 0) { opt->debug_mask |= CCX_DMT_708; #ifndef DISABLE_RUST @@ -2204,133 +2210,133 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) #endif continue; } - if (strcmp(argv[i], "-goppts") == 0) + if (strcmp(argv[i], "--goppts") == 0) { opt->debug_mask |= CCX_DMT_TIME; continue; } - if (strcmp(argv[i], "-vides") == 0) + if (strcmp(argv[i], "--vides") == 0) { opt->debug_mask |= CCX_DMT_VIDES; opt->analyze_video_stream = 1; continue; } - if (strcmp(argv[i], "-anvid") == 0 || strcmp(argv[i], "--analyzevideo") == 0) + if (strcmp(argv[i], "--analyzevideo") == 0) { opt->analyze_video_stream = 1; continue; } - if (strcmp(argv[i], "-xds") == 0) + if (strcmp(argv[i], "--xds") == 0) { - // XDS can be set regardless of -UCLA (isFinal) usage. + // XDS can be set regardless of --UCLA (isFinal) usage. opt->transcript_settings.xds = 1; continue; } - if (strcmp(argv[i], "-xdsdebug") == 0) + if (strcmp(argv[i], "--xdsdebug") == 0) { opt->transcript_settings.xds = 1; opt->debug_mask |= CCX_DMT_DECODER_XDS; continue; } - if (strcmp(argv[i], "-parsedebug") == 0) + if (strcmp(argv[i], "--parsedebug") == 0) { opt->debug_mask |= CCX_DMT_PARSE; continue; } - if (strcmp(argv[i], "-parsePAT") == 0 || strcmp(argv[i], "-parsepat") == 0) + if (strcmp(argv[i], "--parsePAT") == 0 || strcmp(argv[i], "--parsepat") == 0) { opt->debug_mask |= CCX_DMT_PAT; continue; } - if (strcmp(argv[i], "-parsePMT") == 0 || strcmp(argv[i], "-parsepmt") == 0) + if (strcmp(argv[i], "--parsePMT") == 0 || strcmp(argv[i], "--parsepmt") == 0) { opt->debug_mask |= CCX_DMT_PMT; continue; } - if (strcmp(argv[i], "-dumpdef") == 0) + if (strcmp(argv[i], "--dumpdef") == 0) { opt->debug_mask |= CCX_DMT_DUMPDEF; continue; } - if (strcmp(argv[i], "-investigate_packets") == 0) + if (strcmp(argv[i], "--investigate-packets") == 0) { opt->investigate_packets = 1; continue; } - if (strcmp(argv[i], "-cbraw") == 0) + if (strcmp(argv[i], "--cbraw") == 0) { opt->debug_mask |= CCX_DMT_CBRAW; continue; } - if (strcmp(argv[i], "-tverbose") == 0) + if (strcmp(argv[i], "--tverbose") == 0) { opt->debug_mask |= CCX_DMT_TELETEXT; tlt_config.verbose = 1; continue; } #ifdef ENABLE_SHARING - if (strcmp(argv[i], "-sharing-debug") == 0) + if (strcmp(argv[i], "--sharing-debug") == 0) { opt->debug_mask |= CCX_DMT_SHARE; continue; } #endif // ENABLE_SHARING - if (strcmp(argv[i], "-fullbin") == 0) + if (strcmp(argv[i], "--fullbin") == 0) { opt->fullbin = 1; continue; } - if (strcmp(argv[i], "-nosync") == 0) + if (strcmp(argv[i], "--no-sync") == 0) { opt->nosync = 1; continue; } - if (strcmp(argv[i], "-haup") == 0 || strcmp(argv[i], "--hauppauge") == 0) + if (strcmp(argv[i], "--hauppauge") == 0) { opt->hauppauge_mode = 1; continue; } - if (strcmp(argv[i], "-mp4vidtrack") == 0) + if (strcmp(argv[i], "--mp4vidtrack") == 0) { opt->mp4vidtrack = 1; continue; } - if (strstr(argv[i], "-unicode") != NULL) + if (strstr(argv[i], "--unicode") != NULL) { opt->enc_cfg.encoding = CCX_ENC_UNICODE; continue; } - if (strstr(argv[i], "-utf8") != NULL) + if (strstr(argv[i], "--utf8") != NULL) { opt->enc_cfg.encoding = CCX_ENC_UTF_8; continue; } - if (strstr(argv[i], "-latin1") != NULL) + if (strstr(argv[i], "--latin1") != NULL) { opt->enc_cfg.encoding = CCX_ENC_LATIN_1; continue; } - if (strcmp(argv[i], "-poc") == 0 || strcmp(argv[i], "--usepicorder") == 0) + if (strcmp(argv[i], "--usepicorder") == 0) { opt->usepicorder = 1; continue; } - if (strstr(argv[i], "-myth") != NULL) + if (strstr(argv[i], "--myth") != NULL) { opt->auto_myth = 1; continue; } - if (strstr(argv[i], "-nomyth") != NULL) + if (strstr(argv[i], "--no-myth") != NULL) { opt->auto_myth = 0; continue; } - if (strstr(argv[i], "-wtvconvertfix") != NULL) + if (strstr(argv[i], "--wtvconvertfix") != NULL) { opt->wtvconvertfix = 1; continue; } - if (strstr(argv[i], "-wtvmpeg2") != NULL) + if (strstr(argv[i], "--wtvmpeg2") != NULL) { opt->wtvmpeg2 = 1; continue; @@ -2348,7 +2354,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) fatal(EXIT_MALFORMED_PARAMETER, "-o has no argument.\n"); } } - if (strcmp(argv[i], "-svc") == 0 || strcmp(argv[i], "--service") == 0) + if (strcmp(argv[i], "--service") == 0) { if (i < argc - 1) { @@ -2362,7 +2368,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) fatal(EXIT_MALFORMED_PARAMETER, "--service has no argument.\n"); } } - if (strcmp(argv[i], "-datapid") == 0) + if (strcmp(argv[i], "--datapid") == 0) { if (i < argc - 1) { @@ -2373,10 +2379,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-datapid has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--datapid has no argument.\n"); } } - if (strcmp(argv[i], "-datastreamtype") == 0) + if (strcmp(argv[i], "--datastreamtype") == 0) { if (i < argc - 1) { @@ -2386,10 +2392,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-datastreamtype has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--datastreamtype has no argument.\n"); } } - if (strcmp(argv[i], "-streamtype") == 0) + if (strcmp(argv[i], "--streamtype") == 0) { if (i < argc - 1) { @@ -2399,12 +2405,12 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-streamtype has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--streamtype has no argument.\n"); } } /* Teletext stuff */ - if (strcmp(argv[i], "-tpage") == 0) + if (strcmp(argv[i], "--tpage") == 0) { if (i < argc - 1) { @@ -2415,12 +2421,12 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-tpage has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--tpage has no argument.\n"); } } /* Red Hen/ UCLA Specific stuff */ - if (strcmp(argv[i], "-UCLA") == 0 || strcmp(argv[i], "-ucla") == 0) + if (strcmp(argv[i], "--UCLA") == 0 || strcmp(argv[i], "--ucla") == 0) { opt->ucla = 1; opt->millis_separator = '.'; @@ -2436,37 +2442,37 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } continue; } - if (strcmp(argv[i], "-latrusmap") == 0) + if (strcmp(argv[i], "--latrusmap") == 0) { tlt_config.latrusmap = 1; continue; } - if (strcmp(argv[i], "-tickertext") == 0 || strcmp(argv[i], "-tickertape") == 0) + if (strcmp(argv[i], "--tickertext") == 0 || strcmp(argv[i], "--tickertape") == 0) { opt->tickertext = 1; continue; } - if (strcmp(argv[i], "-lf") == 0 || strcmp(argv[i], "-LF") == 0) + if (strcmp(argv[i], "--lf") == 0 || strcmp(argv[i], "--LF") == 0) { opt->enc_cfg.line_terminator_lf = 1; continue; } - if (strcmp(argv[i], "-df") == 0 || strcmp(argv[i], "-DF") == 0) + if (strcmp(argv[i], "--df") == 0 || strcmp(argv[i], "--DF") == 0) { opt->enc_cfg.force_dropframe = 1; continue; } - if (strcmp(argv[i], "-noautotimeref") == 0) + if (strcmp(argv[i], "--no-autotimeref") == 0) { opt->noautotimeref = 1; continue; } - if (strcmp(argv[i], "-autodash") == 0 || strcmp(argv[i], "-sem") == 0) + if (strcmp(argv[i], "--autodash") == 0 || strcmp(argv[i], "--sem") == 0) { opt->enc_cfg.autodash = 1; continue; } - if (strcmp(argv[i], "-xmltv") == 0) + if (strcmp(argv[i], "--xmltv") == 0) { if (i < argc - 1 && isanumber(argv[i + 1])) { @@ -2480,7 +2486,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) continue; } } - if (strcmp(argv[i], "-xmltvliveinterval") == 0) + if (strcmp(argv[i], "--xmltvliveinterval") == 0) { if (i < argc - 1 && isanumber(argv[i + 1])) { @@ -2494,7 +2500,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) continue; } } - if (strcmp(argv[i], "-xmltvoutputinterval") == 0) + if (strcmp(argv[i], "--xmltvoutputinterval") == 0) { if (i < argc - 1 && isanumber(argv[i + 1])) { @@ -2508,13 +2514,13 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) continue; } } - if (strcmp(argv[i], "-xmltvonlycurrent") == 0) + if (strcmp(argv[i], "--xmltvonlycurrent") == 0) { opt->xmltvonlycurrent = 1; i++; // why do we skip next? continue; } - if (strcmp(argv[i], "-unixts") == 0) + if (strcmp(argv[i], "--unixts") == 0) { if (i < argc - 1) { @@ -2533,31 +2539,31 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-unixts has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--unixts has no argument.\n"); } } - if (strcmp(argv[i], "-sects") == 0) + if (strcmp(argv[i], "--sects") == 0) { opt->date_format = ODF_SECONDS; continue; } - if (strcmp(argv[i], "-datets") == 0) + if (strcmp(argv[i], "--datets") == 0) { opt->date_format = ODF_DATE; continue; } - if (strcmp(argv[i], "-teletext") == 0) + if (strcmp(argv[i], "--teletext") == 0) { opt->demux_cfg.codec = CCX_CODEC_TELETEXT; continue; } - if (strcmp(argv[i], "-noteletext") == 0) + if (strcmp(argv[i], "--no-teletext") == 0) { opt->demux_cfg.nocodec = CCX_CODEC_TELETEXT; continue; } /* Custom transcript */ - if (strcmp(argv[i], "-customtxt") == 0) + if (strcmp(argv[i], "--customtxt") == 0) { if (i < argc - 1) { @@ -2582,7 +2588,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_INCOMPATIBLE_PARAMETERS, "customtxt cannot be set after -UCLA is used!\n"); + fatal(EXIT_INCOMPATIBLE_PARAMETERS, "customtxt cannot be set after --UCLA is used!\n"); } } else @@ -2594,12 +2600,12 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-customtxt has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--customtxt has no argument.\n"); } } /* Network stuff */ - if (strcmp(argv[i], "-udp") == 0) + if (strcmp(argv[i], "--udp") == 0) { if (i < argc - 1) { @@ -2609,7 +2615,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) char *colon = strchr(argv[i], ':'); if (at && !colon) { - fatal(EXIT_MALFORMED_PARAMETER, "If -udp contains an '@', it must also contain a ':'\n"); + fatal(EXIT_MALFORMED_PARAMETER, "If --udp contains an '@', it must also contain a ':'\n"); } else if (at && colon) { @@ -2636,10 +2642,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-udp has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--udp has no argument.\n"); } } - if (strcmp(argv[i], "-sendto") == 0) + if (strcmp(argv[i], "--sendto") == 0) { if (i < argc - 1) { @@ -2688,10 +2694,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-sendto has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--sendto has no argument.\n"); } } - if (strcmp(argv[i], "-tcp") == 0) + if (strcmp(argv[i], "--tcp") == 0) { if (i < argc - 1) { @@ -2706,10 +2712,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-tcp has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--tcp has no argument.\n"); } } - if (strcmp(argv[i], "-tcppassword") == 0) + if (strcmp(argv[i], "--tcp-password") == 0) { if (i < argc - 1) { @@ -2719,10 +2725,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-tcppassword has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--tcp-password has no argument.\n"); } } - if (strcmp(argv[i], "-tcpdesc") == 0) + if (strcmp(argv[i], "--tcp-description") == 0) { if (i < argc - 1) { @@ -2732,11 +2738,11 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-tcpdesc has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--tcpdesc has no argument.\n"); } } - if (strcmp(argv[i], "-font") == 0) + if (strcmp(argv[i], "--font") == 0) { if (i < argc - 1) { @@ -2746,10 +2752,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-font has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--font has no argument.\n"); } } - if (strcmp(argv[i], "-italics") == 0 && i < argc - 1) + if (strcmp(argv[i], "--italics") == 0 && i < argc - 1) { opt->enc_cfg.render_font_italics = argv[i + 1]; i++; @@ -2757,7 +2763,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } #ifdef WITH_LIBCURL - if (strcmp(argv[i], "-curlposturl") == 0) + if (strcmp(argv[i], "--curlposturl") == 0) { if (i < argc - 1) { @@ -2767,18 +2773,18 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-curlposturl has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--curlposturl has no argument.\n"); } } #endif // WITH_LIBCURL #ifdef ENABLE_SHARING - if (strcmp(argv[i], "-enable-sharing") == 0) + if (strcmp(argv[i], "--enable-sharing") == 0) { opt->sharing_enabled = 1; continue; } - if (strcmp(argv[i], "-sharing-url") == 0) + if (strcmp(argv[i], "--sharing-url") == 0) { if (i < argc - 1) { @@ -2788,10 +2794,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-sharing-url has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--sharing-url has no argument.\n"); } } - if (strcmp(argv[i], "-translate") == 0) + if (strcmp(argv[i], "--translate") == 0) { if (i < argc - 1) { @@ -2803,10 +2809,10 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-translate has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--translate has no argument.\n"); } } - if (strcmp(argv[i], "-translate-auth") == 0) + if (strcmp(argv[i], "--translate-auth") == 0) { if (i < argc - 1) { @@ -2816,7 +2822,7 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[]) } else { - fatal(EXIT_MALFORMED_PARAMETER, "-translate-auth has no argument.\n"); + fatal(EXIT_MALFORMED_PARAMETER, "--translate-auth has no argument.\n"); } } #endif // ENABLE_SHARING diff --git a/src/lib_ccx/stream_functions.c b/src/lib_ccx/stream_functions.c index dd1728d95..14d2372e8 100644 --- a/src/lib_ccx/stream_functions.c +++ b/src/lib_ccx/stream_functions.c @@ -99,7 +99,7 @@ void detect_stream_type(struct ccx_demuxer *ctx) while (idx < ctx->startbytes_avail - 8) { // Check if we have a valid box - if (isValidMP4Box(ctx->startbytes, idx, &nextBoxLocation, &boxScore)) + if (isValidMP4Box(ctx->startbytes, idx, &nextBoxLocation, &boxScore) && nextBoxLocation > idx) { idx = nextBoxLocation; // If the box is valid, a new box should be found on the next location... Not somewhere in between. if (boxScore > 7) diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index ce4977a95..9b59367c1 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -59,6 +59,29 @@ dependencies = [ "which", ] +[[package]] +name = "bindgen" +version = "0.69.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00dc851838a2120612785d195287475a3ac45514741da670b735818822129a0" +dependencies = [ + "bitflags 2.4.0", + "cexpr", + "clang-sys", + "itertools", + "lazy_static", + "lazycell", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.52", + "which", +] + [[package]] name = "bitflags" version = "1.3.2" @@ -90,10 +113,11 @@ dependencies = [ name = "ccx_rust" version = "0.1.0" dependencies = [ - "bindgen", + "bindgen 0.64.0", "env_logger", "iconv", "leptonica-sys", + "lib_ccxr", "log", "palette", "rsmpeg", @@ -221,6 +245,15 @@ dependencies = [ "libc", ] +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + [[package]] name = "lazy_static" version = "1.4.0" @@ -239,11 +272,15 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eff3f1dc2f0112411228f8db99ca8a6a1157537a7887b28b1c91fdc4051fb326" dependencies = [ - "bindgen", + "bindgen 0.64.0", "pkg-config", "vcpkg", ] +[[package]] +name = "lib_ccxr" +version = "0.1.0" + [[package]] name = "libc" version = "0.2.147" @@ -375,7 +412,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.52", ] [[package]] @@ -393,20 +430,30 @@ version = "0.3.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" +[[package]] +name = "prettyplease" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a41cf62165e97c7f814d2221421dbb9afcbcdb0a88068e5ea206e19951c2cbb5" +dependencies = [ + "proc-macro2", + "syn 2.0.52", +] + [[package]] name = "proc-macro2" -version = "1.0.66" +version = "1.0.78" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18fb31db3f9bddb2ea821cde30a9f70117e3f119938b5ee630b7403aa6e2ead9" +checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.33" +version = "1.0.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "291ec9ab5efd934aaf503a6466c5d5251535d108ee747472c3977cc5acc868ef" dependencies = [ "proc-macro2", ] @@ -457,9 +504,9 @@ checksum = "dbb5fb1acd8a1a18b3dd5be62d25485eb770e05afb408a9627d14d451bae12da" [[package]] name = "rsmpeg" -version = "0.14.1+ffmpeg.6.0" +version = "0.14.2+ffmpeg.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "055510ebd8693dfe70570352b19a9147314da95e7435477526b976fc83b04455" +checksum = "927012cd6ae43519f519741f4a69602ce3a47cf84750784da124dffd03527cc0" dependencies = [ "libc", "paste", @@ -488,11 +535,11 @@ dependencies = [ [[package]] name = "rusty_ffmpeg" -version = "0.13.1+ffmpeg.6.0" +version = "0.13.3+ffmpeg.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e7c726b08ea3ed199e21f6f4b3a1c23fc56a154be731b4445e4ef9ee004cffc" +checksum = "716adffa5f909c8533611b1dab9ab5666bece35687845865b75ed6a990fc239c" dependencies = [ - "bindgen", + "bindgen 0.69.4", "camino", "libc", "once_cell", @@ -517,7 +564,7 @@ checksum = "4eca7ac642d82aa35b60049a6eccb4be6be75e599bd2e9adb5f875a737654af2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.52", ] [[package]] @@ -545,9 +592,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.32" +version = "2.0.52" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "239814284fd6f1a4ffe4ca893952cdd93c224b6a1571c9a9eadd670295c0c9e2" +checksum = "b699d15b36d1f02c3e7c69f8ffef53de37aefae075d8488d4ba1a7788d574a07" dependencies = [ "proc-macro2", "quote", @@ -569,7 +616,7 @@ version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bd33f6f216124cfaf0fa86c2c0cdf04da39b6257bd78c5e44fa4fa98c3a5857b" dependencies = [ - "bindgen", + "bindgen 0.64.0", "leptonica-sys", "pkg-config", "vcpkg", @@ -592,7 +639,7 @@ checksum = "49922ecae66cc8a249b77e68d1d0623c1b2c514f0060c27cdc68bd62a1219d35" dependencies = [ "proc-macro2", "quote", - "syn 2.0.32", + "syn 2.0.52", ] [[package]] diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 027d76460..93ca96dd4 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -15,13 +15,15 @@ log = "0.4.0" env_logger = "0.8.4" iconv = "0.1.1" palette = "0.6.0" -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} +rsmpeg = { version = "0.14.2", 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 } +lib_ccxr = { path = "lib_ccxr" } [build-dependencies] bindgen = "0.64.0" [features] hardsubx_ocr = ["rsmpeg", "tesseract-sys", "leptonica-sys"] - diff --git a/src/rust/build.rs b/src/rust/build.rs index f8ecc04c8..03e6157f6 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -59,6 +59,8 @@ fn main() { } let bindings = builder + .derive_default(true) + .no_default("dtvcc_pen_attribs|dtvcc_pen_color|dtvcc_symbol") // Finish the builder and generate the bindings. .generate() // Unwrap the Result and panic on failure. diff --git a/src/rust/lib_ccxr/Cargo.lock b/src/rust/lib_ccxr/Cargo.lock new file mode 100644 index 000000000..7532d4515 --- /dev/null +++ b/src/rust/lib_ccxr/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "lib_ccxr" +version = "0.1.0" diff --git a/src/rust/lib_ccxr/Cargo.toml b/src/rust/lib_ccxr/Cargo.toml new file mode 100644 index 000000000..ca3612505 --- /dev/null +++ b/src/rust/lib_ccxr/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "lib_ccxr" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] + +[features] +default = ["enable_sharing", "wtv_debug", "enable_ffmpeg", "debug", "with_libcurl"] +enable_sharing = [] +wtv_debug = [] +enable_ffmpeg = [] +debug_out = [] +debug = [] +with_libcurl = [] diff --git a/src/rust/lib_ccxr/src/lib.rs b/src/rust/lib_ccxr/src/lib.rs new file mode 100644 index 000000000..812d1edf2 --- /dev/null +++ b/src/rust/lib_ccxr/src/lib.rs @@ -0,0 +1 @@ +pub mod util; diff --git a/src/rust/lib_ccxr/src/util/mod.rs b/src/rust/lib_ccxr/src/util/mod.rs new file mode 100644 index 000000000..daf5935ac --- /dev/null +++ b/src/rust/lib_ccxr/src/util/mod.rs @@ -0,0 +1 @@ +//! Provides basic utilities used throughout the program. diff --git a/src/rust/src/decoder/encoding.rs b/src/rust/src/decoder/encoding.rs new file mode 100644 index 000000000..2671cedd9 --- /dev/null +++ b/src/rust/src/decoder/encoding.rs @@ -0,0 +1,45 @@ +/// 256 BYTES IS ENOUGH FOR ALL THE SUPPORTED CHARACTERS IN +/// EIA-708, SO INTERNALLY WE USE THIS TABLE (FOR CONVENIENCE) +/// +/// 00-1F -> Characters that are in the G2 group in 20-3F, +/// except for 06, which is used for the closed captions +/// sign "CC" which is defined in group G3 as 00. (this +/// is by the article 33). +/// 20-7F -> Group G0 as is - corresponds to the ASCII code +/// 80-9F -> Characters that are in the G2 group in 60-7F +/// (there are several blank characters here, that's OK) +/// A0-FF -> Group G1 as is - non-English characters and symbols + +// NOTE: Same as `lib_ccx/ccx_decoder_708_encoding.c` file + +#[no_mangle] +pub extern "C" fn dtvcc_get_internal_from_G0(g0_char: u8) -> u8 { + g0_char +} + +#[no_mangle] +pub extern "C" fn dtvcc_get_internal_from_G1(g1_char: u8) -> u8 { + g1_char +} + +/// G2: Extended Control Code Set 1 +#[no_mangle] +pub extern "C" fn dtvcc_get_internal_from_G2(g2_char: u8) -> u8 { + match g2_char { + 0x20..=0x3F => g2_char - 0x20, + 0x60..=0x7F => g2_char + 0x20, + _ => 0x20, + } +} + +/// G3: Future Characters and Icon Expansion +#[no_mangle] +pub extern "C" fn dtvcc_get_internal_from_G3(g3_char: u8) -> u8 { + if g3_char == 0xa0 { + // The "CC" (closed captions) sign + 0x06 + } else { + // Rest unmapped, so we return a blank space + 0x20 + } +} diff --git a/src/rust/src/decoder/mod.rs b/src/rust/src/decoder/mod.rs index e13c7ed2d..2ee5c26bc 100644 --- a/src/rust/src/decoder/mod.rs +++ b/src/rust/src/decoder/mod.rs @@ -3,6 +3,7 @@ //! Provides a CEA 708 decoder as defined by ANSI/CTA-708-E R-2018 mod commands; +mod encoding; mod output; mod service_decoder; mod timing; diff --git a/src/rust/src/decoder/output.rs b/src/rust/src/decoder/output.rs index fe13f6ff5..fd0c96dc3 100644 --- a/src/rust/src/decoder/output.rs +++ b/src/rust/src/decoder/output.rs @@ -20,6 +20,7 @@ pub struct Writer<'a> { pub no_font_color: bool, pub transcript_settings: &'a ccx_encoders_transcript_format, pub no_bom: i32, + pub old_cc_time_end: i32, } impl<'a> Writer<'a> { @@ -42,6 +43,7 @@ impl<'a> Writer<'a> { no_font_color: is_true(no_font_color), transcript_settings, no_bom, + old_cc_time_end: 0, } } /// Write subtitles to the file diff --git a/src/rust/src/decoder/timing.rs b/src/rust/src/decoder/timing.rs index e65aad9e2..5702359ef 100644 --- a/src/rust/src/decoder/timing.rs +++ b/src/rust/src/decoder/timing.rs @@ -47,3 +47,31 @@ pub fn get_time_str(time: LLONG) -> String { let ms = time - 1000 * (ss + 60 * (mm + 60 * hh)); format!("{:02}:{:02}:{:02},{:03}", hh, mm, ss, ms) } + +impl ccx_boundary_time { + /// Returns ccx_boundary_time from given time + pub fn get_time(time: LLONG) -> Self { + let hh = time / 1000 / 60 / 60; + let mm = time / 1000 / 60 - 60 * hh; + let ss = time / 1000 - 60 * (mm + 60 * hh); + + Self { + hh: hh as i32, + mm: mm as i32, + ss: ss as i32, + time_in_ms: time, + set: Default::default(), + } + } +} + +/// Returns a hh:mm:ss;frame string of time for SCC format +pub fn get_scc_time_str(time: ccx_boundary_time) -> String { + // Feel sorry for formatting:( + let frame: u8 = (((time.time_in_ms + - 1000 * ((time.ss as i64) + 60 * ((time.mm as i64) + 60 * (time.hh as i64)))) + as f64) + * 29.97 + / 1000.0) as u8; + format!("{:02}:{:02}:{:02};{:02}", time.hh, time.mm, time.ss, frame) +} diff --git a/src/rust/src/decoder/tv_screen.rs b/src/rust/src/decoder/tv_screen.rs index 71f3f2b2f..055f29739 100644 --- a/src/rust/src/decoder/tv_screen.rs +++ b/src/rust/src/decoder/tv_screen.rs @@ -3,6 +3,7 @@ //! TV screen contains the captions to be displayed. //! Captions are added to TV screen from a window when any of DSW, HDW, TGW, DLW or CR commands are received +use std::cmp::Ordering; #[cfg(unix)] use std::os::unix::prelude::IntoRawFd; #[cfg(windows)] @@ -10,7 +11,7 @@ use std::os::windows::io::IntoRawHandle; use std::{ffi::CStr, fs::File}; use super::output::{color_to_hex, write_char, Writer}; -use super::timing::get_time_str; +use super::timing::{get_scc_time_str, get_time_str}; use super::{CCX_DTVCC_SCREENGRID_COLUMNS, CCX_DTVCC_SCREENGRID_ROWS}; use crate::{ bindings::*, @@ -104,7 +105,8 @@ impl dtvcc_tv_screen { /// Returns the bounds in which captions are present pub fn get_write_interval(&self, row_index: usize) -> (usize, usize) { let mut first = 0; - let mut last = CCX_DTVCC_SCREENGRID_COLUMNS as usize - 1; + let mut last = 0; + for col in 0..CCX_DTVCC_SCREENGRID_COLUMNS as usize { if self.chars[row_index][col].is_set() { first = col; @@ -128,6 +130,7 @@ impl dtvcc_tv_screen { ccx_output_format::CCX_OF_SRT => self.write_srt(writer), ccx_output_format::CCX_OF_SAMI => self.write_sami(writer), ccx_output_format::CCX_OF_TRANSCRIPT => self.write_transcript(writer), + ccx_output_format::CCX_OF_SCC => self.write_scc(writer), _ => { self.write_debug(); Err("Unsupported write format".to_owned()) @@ -358,6 +361,149 @@ impl dtvcc_tv_screen { Ok(()) } + fn count_captions_lines_scc(&self) -> usize { + (0..CCX_DTVCC_SCREENGRID_ROWS) + .filter(|&row_index| !self.is_row_empty(row_index as usize)) + .count() + } + + /// Write captions in SCC format + pub fn write_scc(&self, writer: &mut Writer) -> Result<(), String> { + fn adjust_odd_parity(value: u8) -> u8 { + let mut ones = 0; + for i in 0..=7 { + if value & (1 << i) != 0 { + ones += 1; + } + } + if ones % 2 == 0 { + 0b10000000 | value + } else { + value + } + } + // This function is designed to assign appropriate SSC labels for positioning subtitles based on their length. + // In some scenarios where the video stream provides lengthy subtitles that cannot fit within a single line. + // Single-line subtitle can be placed in 15th row(most bottom row) + // 2 line length subtitles can be placed in 14th and 15th row + // 3 line length subtitles can be placed in 13th, 14th and 15th row + fn add_needed_scc_labels( + buf: &mut String, + total_subtitle_count: usize, + current_subtitle_count: usize, + ) { + match total_subtitle_count { + // row 15, column 00 + 1 => buf.push_str(" 94e0 94e0"), + 2 => { + if current_subtitle_count == 1 { + // row 14, column 00 + buf.push_str(" 9440 9440"); + } else { + // row 15, column 00 + buf.push_str(" 94e0 94e0") + } + } + _ => { + if current_subtitle_count == 1 { + // row 13, column 04 + buf.push_str(" 13e0 13e0"); + } else if current_subtitle_count == 2 { + // row 14, column 00 + buf.push_str(" 9440 9440"); + } else { + // row 15, column 00 + buf.push_str(" 94e0 94e0") + } + } + } + } + if self.is_screen_empty(writer) { + return Ok(()); + } + + if self.time_ms_show + writer.subs_delay < 0 { + return Ok(()); + } + + if self.cc_count == 2 { + writer.write_to_file(b"Scenarist_SCC V1.0\n\n")?; + } + + if writer.old_cc_time_end == 0 { + writer.old_cc_time_end = self.time_ms_show as i32; + } + + let mut buf = String::new(); + let mut time_show = ccx_boundary_time::get_time(self.time_ms_show); + let time_end = ccx_boundary_time::get_time(self.time_ms_hide); + + // Caption overlapping situation + match writer.old_cc_time_end.cmp(&(time_show.time_in_ms as i32)) { + Ordering::Greater => { + // Correct the frame delay + time_show.time_in_ms -= 1000 / 29.97 as i64; + buf.push_str(&(get_scc_time_str(time_show) + "\t942c 942c ").to_owned()); + time_show.time_in_ms += 1000 / 29.97 as i64; + // Clear the buffer and start pop on caption + buf.push_str("94ae 94ae 9420 9420"); + } + Ordering::Less => { + // Clear the screen for new caption + let time_to_display = ccx_boundary_time::get_time(writer.old_cc_time_end as i64); + buf.push_str(&(get_scc_time_str(time_to_display) + "\t942c 942c \n\n").to_owned()); + // Correct the frame delay + time_show.time_in_ms -= 1000 / 29.97 as i64; + // Clear the buffer and start pop on caption in new time + buf.push_str(&(get_scc_time_str(time_show) + "\t94ae 94ae 9420 9420").to_owned()); + time_show.time_in_ms += 1000 / 29.97 as i64; + } + Ordering::Equal => { + time_show.time_in_ms -= 1000 / 29.97 as i64; + buf.push_str( + &(get_scc_time_str(time_show) + "\t942c 942c 94ae 94ae 9420 9420").to_owned(), + ); + time_show.time_in_ms += 1000 / 29.97 as i64; + } + } + + let total_subtitle_count = self.count_captions_lines_scc(); + let mut current_subtitle_count = 0; + + for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize { + if !self.is_row_empty(row_index) { + current_subtitle_count += 1; + add_needed_scc_labels(&mut buf, total_subtitle_count, current_subtitle_count); + + let (first, last) = self.get_write_interval(row_index); + debug!("First: {}, Last: {}", first, last); + + let mut bytes_written = 0; + for i in 0..last + 1 { + if bytes_written % 2 == 0 { + buf.push(' '); + } + let adjusted_val = adjust_odd_parity(self.chars[row_index][i].sym as u8); + buf = format!("{}{:x}", buf, adjusted_val); + bytes_written += 1; + } + // add 0x80 padding and form byte pair if the last byte pair is not form + if bytes_written % 2 == 1 { + buf.push_str("80 "); + } else { + buf.push(' '); + } + } + } + + // Display caption (942f 942f) + buf.push_str("942f 942f \n\n"); + writer.write_to_file(buf.as_bytes())?; + + writer.old_cc_time_end = time_end.time_in_ms as i32; + Ok(()) + } + /// Write debug messages /// /// Write all characters,show and hide time as a debug log diff --git a/src/rust/src/decoder/window.rs b/src/rust/src/decoder/window.rs index fbd4d024c..868709193 100644 --- a/src/rust/src/decoder/window.rs +++ b/src/rust/src/decoder/window.rs @@ -441,13 +441,13 @@ impl PenStyle { }; let color = PenColor { - /// White(2,2,2) i.e 10,10,10 i.e 42 + // White(2,2,2) i.e 10,10,10 i.e 42 fg_color: 42, fg_opacity: Opacity::Solid, - /// Either N/A or black, still always 0 + // Either N/A or black, still always 0 bg_color: 0, bg_opacity, - /// Either N/A or black, still always 0 + // Either N/A or black, still always 0 edge_color: 0, }; diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index a76faa419..1fec8fa5e 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -15,6 +15,7 @@ pub mod bindings { pub mod decoder; #[cfg(feature = "hardsubx_ocr")] pub mod hardsubx; +pub mod libccxr_exports; pub mod utils; #[cfg(windows)] diff --git a/src/rust/src/libccxr_exports/mod.rs b/src/rust/src/libccxr_exports/mod.rs new file mode 100644 index 000000000..e365e0fb2 --- /dev/null +++ b/src/rust/src/libccxr_exports/mod.rs @@ -0,0 +1 @@ +//! Provides C-FFI functions that are direct equivalent of functions available in C. diff --git a/windows/.gitignore b/windows/.gitignore new file mode 100644 index 000000000..6b7a5ab6f --- /dev/null +++ b/windows/.gitignore @@ -0,0 +1,2 @@ +ccextractor +vcpkg_installed \ No newline at end of file diff --git a/windows/ccextractor.vcxproj b/windows/ccextractor.vcxproj index d2190f3c2..74f29588f 100644 --- a/windows/ccextractor.vcxproj +++ b/windows/ccextractor.vcxproj @@ -62,10 +62,8 @@ - - + + @@ -188,21 +186,26 @@ - + {0F0063C4-BCBC-4379-A6D5-84A5669C940A} ccextractor Win32Proj - 10.0.19041.0 + 10.0.22621.0 + + + true + x64-windows-static + true Application - v142 + v143 Application - v142 + v143 @@ -278,14 +281,14 @@ 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-56.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-8.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\swscale-6.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avdevice-59.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avcodec-59.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avformat-59.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avutil-57.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)" @@ -325,14 +328,14 @@ 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-56.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-8.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\swscale-6.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avdevice-59.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avcodec-59.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avformat-59.dll" "$(OutDir)" - xcopy /y "C:\Program Files\GPAC\avutil-57.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)" diff --git a/windows/ccextractor.vcxproj.filters b/windows/ccextractor.vcxproj.filters index 27083f551..8a7029ba0 100644 --- a/windows/ccextractor.vcxproj.filters +++ b/windows/ccextractor.vcxproj.filters @@ -183,108 +183,6 @@ 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 - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - - - Header Files - Header Files @@ -300,39 +198,6 @@ 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 @@ -381,603 +246,379 @@ Header Files + + Header Files + + + Header Files + + + Header Files + + + Header Files + - - Source Files - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\ccx_common - - - Source Files\ccx_common - - - Source Files\ccx_common - - - Source Files\ccx_common - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_common - Source Files\ccx_decoders - - Source Files - - - Source Files - - - Source Files\ccx_decoders - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\ccx_decoders - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files\lib_ccx - - - Source Files - - - Source Files\ccx_encoders - - - Source Files\ccx_encoders - 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\zlib - - - Source Files - - + Source Files - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - - Source Files - - + Source Files - - Header Files\lib_ccx - + \ No newline at end of file diff --git a/windows/vcpkg.json b/windows/vcpkg.json new file mode 100644 index 000000000..caef96365 --- /dev/null +++ b/windows/vcpkg.json @@ -0,0 +1,10 @@ +{ + "name": "ccextractor", + "version": "1.0.0", + "dependencies": [ + "leptonica", + "tesseract", + "ffmpeg" + ], + "builtin-baseline": "fba75d09065fcc76a25dcf386b1d00d33f5175af" +} \ No newline at end of file