diff --git a/.github/workflows/build_linux.yml b/.github/workflows/build_linux.yml index 94dbb5bb..4358b012 100644 --- a/.github/workflows/build_linux.yml +++ b/.github/workflows/build_linux.yml @@ -75,7 +75,7 @@ jobs: steps: - uses: actions/checkout@v3 - name: dependencies - run: sudo apt update && sudo apt install libtesseract-dev libavformat-dev libswscale-dev + run: sudo apt update && sudo apt install libtesseract-dev libavformat-dev libswscale-dev yasm - name: cmake run: mkdir build && cd build && cmake -DWITH_OCR=ON -DWITH_HARDSUBX=ON ../src - name: build diff --git a/.github/workflows/format.yml b/.github/workflows/format.yml index 213b3d68..36b34581 100644 --- a/.github/workflows/format.yml +++ b/.github/workflows/format.yml @@ -45,7 +45,9 @@ jobs: toolchain: 1.56.0 override: true components: rustfmt, clippy + - name: dependencies + run: sudo apt update && sudo apt install libtesseract-dev libavformat-dev libswscale-dev yasm - name: rustfmt run: cargo fmt --all -- --check - name: clippy - run: cargo clippy -- -D warnings + run: cargo clippy --all-features -- -D warnings diff --git a/linux/Makefile.am b/linux/Makefile.am index 555eba37..fddc26a5 100644 --- a/linux/Makefile.am +++ b/linux/Makefile.am @@ -331,22 +331,6 @@ ccextractor_CPPFLAGS =-I../src/lib_ccx/ -I../src/thirdparty/gpacmp4/ -I../src/th ccextractor_LDADD=-lm -lpthread -ldl -if WITH_RUST -ccextractor_LDADD += ./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a -else -ccextractor_CFLAGS += -DDISABLE_RUST -ccextractor_CPPFLAGS += -DDISABLE_RUST -endif - -if DEBUG_RELEASE -CARGO_RELEASE_ARGS= -else -CARGO_RELEASE_ARGS=--release -endif - -./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a: - cd ../src/rust && \ - CARGO_TARGET_DIR=../../linux/rust $(CARGO) build $(CARGO_RELEASE_ARGS); if SYS_IS_LINUX ccextractor_CFLAGS += -O3 -s -DGPAC_CONFIG_LINUX @@ -361,6 +345,8 @@ if SYS_IS_64_BIT ccextractor_CFLAGS += -DGPAC_64_BITS endif +HARDSUBX_FEATURE_RUST= + if HARDSUBX_IS_ENABLED ccextractor_CFLAGS += -DENABLE_HARDSUBX ccextractor_CPPFLAGS+= ${libavcodec_CFLAGS} @@ -372,6 +358,7 @@ AV_LIB += ${libavformat_LIBS} AV_LIB += ${libavutil_LIBS} AV_LIB += ${libswscale_LIBS} ccextractor_LDADD += $(AV_LIB) +HARDSUBX_FEATURE_RUST += --features "hardsubx_ocr" endif if OCR_IS_ENABLED @@ -398,5 +385,22 @@ ccextractor_LDADD += $(TESS_LIB) ccextractor_LDADD += $(LEPT_LIB) endif +if WITH_RUST +ccextractor_LDADD += ./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a +else +ccextractor_CFLAGS += -DDISABLE_RUST +ccextractor_CPPFLAGS += -DDISABLE_RUST +endif + +if DEBUG_RELEASE +CARGO_RELEASE_ARGS= +else +CARGO_RELEASE_ARGS=--release +endif + +./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a: + cd ../src/rust && \ + CARGO_TARGET_DIR=../../linux/rust $(CARGO) build $(HARDSUBX_FEATURE_RUST) $(CARGO_RELEASE_ARGS); + EXTRA_DIST = ../src/thirdparty/gpacmp4/gpac/sync_layer.h ../src/lib_ccx/ccfont2.xbm ../src/thirdparty/utf8proc/utf8proc_data.c fonts/ icon/ diff --git a/linux/build b/linux/build index 79946223..dca9a365 100755 --- a/linux/build +++ b/linux/build @@ -84,7 +84,13 @@ else fi echo "Building rust files..." - (cd ../src/rust && CARGO_TARGET_DIR=../../linux/rust cargo build) || { echo "Failed. " ; exit 1; } + echo "${BLD_FLAGS}" + tokens=( ${BLD_FLAGS} ) + if [ ${tokens[0]} = "-DENABLE_HARDSUBX" ]; then + (cd ../src/rust && CARGO_TARGET_DIR=../../linux/rust cargo build --features "hardsubx_ocr") || { echo "Failed. " ; exit 1; } + else + (cd ../src/rust && CARGO_TARGET_DIR=../../linux/rust cargo build) || { echo "Failed. " ; exit 1; } + fi cp rust/debug/libccx_rust.a ./libccx_rust.a fi diff --git a/mac/Makefile.am b/mac/Makefile.am index 27c9de45..fddc26a5 100644 --- a/mac/Makefile.am +++ b/mac/Makefile.am @@ -331,22 +331,6 @@ ccextractor_CPPFLAGS =-I../src/lib_ccx/ -I../src/thirdparty/gpacmp4/ -I../src/th ccextractor_LDADD=-lm -lpthread -ldl -if WITH_RUST -ccextractor_LDADD += ./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a -else -ccextractor_CFLAGS += -DDISABLE_RUST -ccextractor_CPPFLAGS += -DDISABLE_RUST -endif - -if DEBUG_RELEASE -CARGO_RELEASE_ARGS= -else -CARGO_RELEASE_ARGS=--release -endif - -./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a: - cd ../src/rust && \ - CARGO_TARGET_DIR=../../linux/rust $(CARGO) build $(CARGO_RELEASE_ARGS); if SYS_IS_LINUX ccextractor_CFLAGS += -O3 -s -DGPAC_CONFIG_LINUX @@ -361,6 +345,8 @@ if SYS_IS_64_BIT ccextractor_CFLAGS += -DGPAC_64_BITS endif +HARDSUBX_FEATURE_RUST= + if HARDSUBX_IS_ENABLED ccextractor_CFLAGS += -DENABLE_HARDSUBX ccextractor_CPPFLAGS+= ${libavcodec_CFLAGS} @@ -372,6 +358,7 @@ AV_LIB += ${libavformat_LIBS} AV_LIB += ${libavutil_LIBS} AV_LIB += ${libswscale_LIBS} ccextractor_LDADD += $(AV_LIB) +HARDSUBX_FEATURE_RUST += --features "hardsubx_ocr" endif if OCR_IS_ENABLED @@ -398,4 +385,22 @@ ccextractor_LDADD += $(TESS_LIB) ccextractor_LDADD += $(LEPT_LIB) endif +if WITH_RUST +ccextractor_LDADD += ./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a +else +ccextractor_CFLAGS += -DDISABLE_RUST +ccextractor_CPPFLAGS += -DDISABLE_RUST +endif + +if DEBUG_RELEASE +CARGO_RELEASE_ARGS= +else +CARGO_RELEASE_ARGS=--release +endif + +./rust/@RUST_TARGET_SUBDIR@/libccx_rust.a: + cd ../src/rust && \ + CARGO_TARGET_DIR=../../linux/rust $(CARGO) build $(HARDSUBX_FEATURE_RUST) $(CARGO_RELEASE_ARGS); + EXTRA_DIST = ../src/thirdparty/gpacmp4/gpac/sync_layer.h ../src/lib_ccx/ccfont2.xbm ../src/thirdparty/utf8proc/utf8proc_data.c fonts/ icon/ + diff --git a/src/lib_ccx/hardsubx_utility.c b/src/lib_ccx/hardsubx_utility.c index 7b5999fe..4e5ab423 100644 --- a/src/lib_ccx/hardsubx_utility.c +++ b/src/lib_ccx/hardsubx_utility.c @@ -1,7 +1,7 @@ #include "lib_ccx.h" #include "utility.h" -#ifdef ENABLE_HARDSUBX +#if defined(ENABLE_HARDSUBX) && defined(DISABLE_RUST) //TODO: Correct FFMpeg integration #include #include diff --git a/src/rust/CMakeLists.txt b/src/rust/CMakeLists.txt index 74e8fdcd..7f9c1137 100644 --- a/src/rust/CMakeLists.txt +++ b/src/rust/CMakeLists.txt @@ -1,8 +1,16 @@ +if (WITH_OCR AND WITH_HARDSUBX) + set(FEATURE_ARG "\"hardsubx_ocr\"") + set(FEATURE_FLAG "--features") +else () + set(FEATURE_ARG "") + set(FEATURE_FLAG "") +endif () + if (CMAKE_BUILD_TYPE STREQUAL "Debug") - set(CARGO_CMD cargo build) + set(CARGO_CMD cargo build ${FEATURE_FLAG} ${FEATURE_ARG}) set(TARGET_DIR "debug") else () - set(CARGO_CMD cargo build --release) + set(CARGO_CMD cargo build ${FEATURE_FLAG} ${FEATURE_ARG} --release) set(TARGET_DIR "release") endif () diff --git a/src/rust/Cargo.lock b/src/rust/Cargo.lock index 4e16e064..a07da06b 100644 --- a/src/rust/Cargo.lock +++ b/src/rust/Cargo.lock @@ -53,7 +53,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f" dependencies = [ "bitflags", - "cexpr", + "cexpr 0.4.0", "clang-sys", "clap", "env_logger", @@ -69,18 +69,44 @@ dependencies = [ "which", ] +[[package]] +name = "bindgen" +version = "0.59.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bd2a9a458e8f4304c52c43ebb0cfbd520289f8379a52e329a38afda99bf8eb8" +dependencies = [ + "bitflags", + "cexpr 0.6.0", + "clang-sys", + "lazy_static", + "lazycell", + "peeking_take_while", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", +] + [[package]] name = "bitflags" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693" +[[package]] +name = "cc" +version = "1.0.73" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fff2a6927b3bb87f9595d67196a70493f627687a71d87a0d692242c33f58c11" + [[package]] name = "ccx_rust" version = "0.1.0" dependencies = [ - "bindgen", + "bindgen 0.58.1", "env_logger", + "ffmpeg-sys-next", "iconv", "log", "palette", @@ -92,7 +118,16 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27" dependencies = [ - "nom", + "nom 5.1.2", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom 7.1.1", ] [[package]] @@ -146,6 +181,20 @@ dependencies = [ "termcolor", ] +[[package]] +name = "ffmpeg-sys-next" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba12dea33516e30c160ce557c7e43dd857276368eb1cd0eef4fce6529f2dee5" +dependencies = [ + "bindgen 0.59.2", + "cc", + "libc", + "num_cpus", + "pkg-config", + "vcpkg", +] + [[package]] name = "find-crate" version = "0.6.3" @@ -240,6 +289,12 @@ version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc" +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + [[package]] name = "nom" version = "5.1.2" @@ -250,6 +305,16 @@ dependencies = [ "version_check", ] +[[package]] +name = "nom" +version = "7.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8903e5a29a317527874d0402f867152a3d21c908bb0b933e416c65e301d4c36" +dependencies = [ + "memchr", + "minimal-lexical", +] + [[package]] name = "num-traits" version = "0.2.15" @@ -259,6 +324,16 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num_cpus" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" +dependencies = [ + "hermit-abi", + "libc", +] + [[package]] name = "palette" version = "0.6.0" @@ -333,6 +408,12 @@ dependencies = [ "siphasher", ] +[[package]] +name = "pkg-config" +version = "0.3.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae" + [[package]] name = "ppv-lite86" version = "0.2.16" @@ -490,6 +571,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3" +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + [[package]] name = "vec_map" version = "0.8.2" diff --git a/src/rust/Cargo.toml b/src/rust/Cargo.toml index 7455b906..d0f6f8d9 100644 --- a/src/rust/Cargo.toml +++ b/src/rust/Cargo.toml @@ -15,6 +15,10 @@ log = "0.4.0" env_logger = "0.8.4" iconv = "0.1.1" palette = "0.6.0" +ffmpeg-sys-next = { version = "5.0.1", optional = true, default-features = false, features = ["avcodec", "build"]} [build-dependencies] -bindgen = "0.58.1" \ No newline at end of file +bindgen = "0.58.1" + +[features] +hardsubx_ocr = ["ffmpeg-sys-next"] diff --git a/src/rust/build.rs b/src/rust/build.rs index d0ce16ce..9f69a257 100644 --- a/src/rust/build.rs +++ b/src/rust/build.rs @@ -2,19 +2,42 @@ use std::env; use std::path::PathBuf; fn main() { + let mut allowlist_functions = vec![ + ".*(?i)_?dtvcc_.*", + "get_visible_.*", + "get_fts", + "printdata", + "writercwtdata", + ]; + + #[cfg(feature = "hardsubx_ocr")] + allowlist_functions.extend_from_slice(&["edit_distance", "convert_pts_to_.*", "av_rescale_q"]); + + let mut allowlist_types = vec![".*(?i)_?dtvcc_.*", "encoder_ctx", "lib_cc_decode"]; + + #[cfg(feature = "hardsubx_ocr")] + allowlist_types.extend_from_slice(&["AVRational"]); + let mut builder = bindgen::Builder::default() // The input header we would like to generate // bindings for. - .header("wrapper.h") - // Tell cargo to invalidate the built crate whenever any of the - // included header files changed. - .parse_callbacks(Box::new(bindgen::CargoCallbacks)); + .header("wrapper.h"); - for type_name in ALLOWLIST_TYPES { + // enable hardsubx if and only if the feature is on + #[cfg(feature = "hardsubx_ocr")] + { + builder = builder.clang_arg("-DENABLE_HARDSUBX"); + } + + // Tell cargo to invalidate the built crate whenever any of the + // included header files changed. + builder = builder.parse_callbacks(Box::new(bindgen::CargoCallbacks)); + + for type_name in allowlist_types { builder = builder.allowlist_type(type_name); } - for fn_name in ALLOWLIST_FUNCTIONS { + for fn_name in allowlist_functions { builder = builder.allowlist_function(fn_name); } @@ -35,12 +58,4 @@ fn main() { .expect("Couldn't write bindings!"); } -const ALLOWLIST_FUNCTIONS: &[&str] = &[ - ".*(?i)_?dtvcc_.*", - "get_visible_.*", - "get_fts", - "printdata", - "writercwtdata", -]; -const ALLOWLIST_TYPES: &[&str] = &[".*(?i)_?dtvcc_.*", "encoder_ctx", "lib_cc_decode"]; const RUSTIFIED_ENUMS: &[&str] = &["dtvcc_(window|pen)_.*", "ccx_output_format"]; diff --git a/src/rust/src/hardsubx/mod.rs b/src/rust/src/hardsubx/mod.rs index dd0f059b..20b808d5 100644 --- a/src/rust/src/hardsubx/mod.rs +++ b/src/rust/src/hardsubx/mod.rs @@ -1 +1,2 @@ pub mod imgops; +pub mod utility; diff --git a/src/rust/src/hardsubx/utility.rs b/src/rust/src/hardsubx/utility.rs new file mode 100644 index 00000000..d2b8e150 --- /dev/null +++ b/src/rust/src/hardsubx/utility.rs @@ -0,0 +1,88 @@ +#[cfg(feature = "hardsubx_ocr")] +use ffmpeg_sys_next::*; +use std::os::raw::{c_char, c_int}; +use std::{cmp, ffi}; + +const AV_TIME_BASE: i32 = 1000000; +const AV_TIME_BASE_Q: AVRational = AVRational { + num: 1, + den: AV_TIME_BASE, +}; + +#[no_mangle] +pub extern "C" fn convert_pts_to_ns(pts: i64, time_base: AVRational) -> i64 { + unsafe { av_rescale_q(pts, time_base, AV_TIME_BASE_Q) } +} + +#[no_mangle] +pub extern "C" fn convert_pts_to_ms(pts: i64, time_base: AVRational) -> i64 { + unsafe { av_rescale_q(pts, time_base, AV_TIME_BASE_Q) / 1000 } +} + +#[no_mangle] +pub extern "C" fn convert_pts_to_s(pts: i64, time_base: AVRational) -> i64 { + unsafe { av_rescale_q(pts, time_base, AV_TIME_BASE_Q) / 1000000 } +} + +fn _edit_distance_rec( + word1: &[u8], + word2: &[u8], + i: usize, + j: usize, + dp_array: &mut Vec>, +) -> i32 { + // Recursive implementation with DP of Levenshtein distance + + if dp_array.is_empty() || dp_array[0].is_empty() { + // in case word1 or word2 has length 0 + + cmp::max(i as i32, j as i32) + } else if dp_array[i][j] != -1 { + dp_array[i][j] + } else if cmp::min(i, j) == 0 { + dp_array[i][j] = cmp::max(i as i32, j as i32); + cmp::max(i as i32, j as i32) + } else { + let length_branch_one = _edit_distance_rec(word1, word2, i - 1, j, dp_array) + 1; + let length_branch_two = _edit_distance_rec(word1, word2, i, j - 1, dp_array) + 1; + + let length_branch_three = _edit_distance_rec(word1, word2, i - 1, j - 1, dp_array) + + if word1[i - 1] == word2[j - 1] { 0 } else { 1 }; + dp_array[i][j] = cmp::min( + length_branch_one, + cmp::min(length_branch_two, length_branch_three), + ); + dp_array[i][j] + } +} + +/// # Safety +/// +/// Function deals with C string pointers +/// which might be null + +#[no_mangle] +pub unsafe extern "C" fn edit_distance( + word1: *mut c_char, + word2: *mut c_char, + len1: c_int, + len2: c_int, +) -> c_int { + // The actual edit_distance function + + let word1_string: &ffi::CStr = ffi::CStr::from_ptr(word1); + let word2_string: &ffi::CStr = ffi::CStr::from_ptr(word2); + + let len1 = len1 as usize; + let len2 = len2 as usize; + + let mut dp_array = vec![vec![-1; len2 + 1]; len1 + 1]; + + _edit_distance_rec( + word1_string.to_bytes(), + word2_string.to_bytes(), + len1, + len2, + &mut dp_array, + ) as c_int +} diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 2fcd3944..a76faa41 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -13,6 +13,7 @@ pub mod bindings { include!(concat!(env!("OUT_DIR"), "/bindings.rs")); } pub mod decoder; +#[cfg(feature = "hardsubx_ocr")] pub mod hardsubx; pub mod utils; diff --git a/src/rust/wrapper.h b/src/rust/wrapper.h index 3ecb54ed..f14d23a0 100644 --- a/src/rust/wrapper.h +++ b/src/rust/wrapper.h @@ -4,4 +4,5 @@ #include "../lib_ccx/ccx_decoders_708_output.h" #include "../lib_ccx/ccx_decoders_708_encoding.h" #include "../lib_ccx/ccx_common_timing.h" -#include "../lib_ccx/lib_ccx.h" \ No newline at end of file +#include "../lib_ccx/lib_ccx.h" +#include "../lib_ccx/hardsubx.h" diff --git a/windows/rustx86.bat b/windows/rustx86.bat index f9cbf122..1a7d6b35 100644 --- a/windows/rustx86.bat +++ b/windows/rustx86.bat @@ -1,6 +1,6 @@ for /f "delims=" %%i in ('cd') do set output=%%i set CARGO_TARGET_DIR=%output% cd ..\src\rust -cargo build --target=i686-pc-windows-msvc +cargo build --target=i686-pc-windows-msvc --features "hardsubx_ocr" cd ..\..\windows copy i686-pc-windows-msvc\debug\ccx_rust.lib .\ccx_rust.lib \ No newline at end of file