Port hardsubx utility (#1443)

* set up bindings conversion of hardsubx utility functions (and structs) and set up the module

* add low level ffmpeg rust binding

* Methods ported:

- convert_pts_to_ns
- convert_pts_to_ms
- convert_pts_to_s

A pure rust method was added called _edit_distance_rec that implements levenstein distance calculation using recursion and dynamic programming

The port of edit_distance_rec is simply a wrapper that calls above function.

This redundancy won't be nevessary as more downstream modules are ported to Rust

* put C code of hardsubx_utility under define rust flag

* run formatter

* make compilation of hardsubx rust modules conditional on the HARDSUBX and the OCR flags. Make ffmpeg a conditional dependency based on those flags

* remove namespaced dependency in cargo because that is a nightly feature

* add conditioal compilatio of ffmpeg related bindigs in build.rs

* make clang argument of -DENABLE_HARDSUBX conditional on cargo feature of hardsubx_ocr

* enable specific relevant features for ffmpeg-sys-next

* enable hardsubx_ocr feature in windows build

* add build feature in ffmpeg-sys-next

* ffmpeg build feature is conditional on platform

* Revert "ffmpeg build feature is conditional on platform"

This reverts commit e456fee942.

This is because conditional features do not work in cargo toml

* install yasm in the linux build github action for ocr and hardsubx enabled cmake

* turn globals to locals to reduce code

* remove redundant attributes

* style changes

* make import of ffmpeg-sys-next conditional on hardsubx_ocr flag

* add --all-features flag in clippy for github workflow

* run formatter

* fix clippy command

* install yasm as part of rust format build check

* install libtesseract-dev etc. for clippy build test

* readability change

* declare the function edit_distance as unsafe

* remove commented code

* formatting changes

* combine declaration and assignment

* add build command for building hardsubx rust

context to issue: #1445

* make hardsubx rust work with autoconf build. For issue: #1445

* update autoconf for mac for issue #1445
This commit is contained in:
Shashwat Singh
2022-07-13 14:36:30 +05:30
committed by GitHub
parent 0bd213e789
commit 170066f046
15 changed files with 280 additions and 58 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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/

View File

@@ -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

View File

@@ -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/

View File

@@ -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 <libavcodec/avcodec.h>
#include <libavformat/avformat.h>

View File

@@ -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 ()

93
src/rust/Cargo.lock generated
View File

@@ -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"

View File

@@ -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"
bindgen = "0.58.1"
[features]
hardsubx_ocr = ["ffmpeg-sys-next"]

View File

@@ -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"];

View File

@@ -1 +1,2 @@
pub mod imgops;
pub mod utility;

View File

@@ -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<Vec<i32>>,
) -> 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
}

View File

@@ -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;

View File

@@ -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"
#include "../lib_ccx/lib_ccx.h"
#include "../lib_ccx/hardsubx.h"

View File

@@ -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