mirror of
https://github.com/CCExtractor/ccextractor.git
synced 2026-07-08 18:06:30 +00:00
[FEAT] Removed C code already ported to Rust (#1738)
* Removal: Removed redundant C code already ported to Rust * Removal: C formatting * Removal: More Removal and CI issues in Mac * Removal: CI issues in Mac * Removal: Changes due to Rebase * Removal: Failing CI on mac * Removal: Failing regression test on dvdraw
This commit is contained in:
@@ -113,7 +113,6 @@ sudo make install
|
||||
|
||||
`cmake` also accepts the options:
|
||||
`-DWITH_OCR=ON` to enable OCR
|
||||
`-DWITHOUT_RUST=ON` to disable rust.
|
||||
`-DWITH_HARDSUBX=ON` to enable burned-in subtitles
|
||||
|
||||
([OPTIONAL] For hardsubx, you also need to set these environment variables correctly according to your machine)
|
||||
@@ -176,7 +175,6 @@ make
|
||||
|
||||
`cmake` also accepts the options:
|
||||
`-DWITH_OCR=ON` to enable OCR
|
||||
`-DWITHOUT_RUST=ON` to disable rust.
|
||||
`-DWITH_HARDSUBX=ON` to enable burned-in subtitles
|
||||
|
||||
#### Standard compilation through Autoconf scripts:
|
||||
|
||||
@@ -32,8 +32,5 @@ cmake -DWITH_OCR=ON ../src/
|
||||
If you want to build CCExtractor with HARDSUBX support
|
||||
cmake -DWITH_HARDSUBX=ON ../src/
|
||||
|
||||
If you want to build CCExtractor with rust disabled you need to pass
|
||||
cmake -DWITHOUT_RUST=ON ../src/
|
||||
|
||||
Hint for looking all the things you want to set from outside
|
||||
cmake -LAH ../src/
|
||||
|
||||
@@ -327,12 +327,7 @@ 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=
|
||||
|
||||
51
linux/build
51
linux/build
@@ -11,10 +11,6 @@ while [[ $# -gt 0 ]]; do
|
||||
RUST_LIB="rust/debug/libccx_rust.a"
|
||||
shift
|
||||
;;
|
||||
-without-rust)
|
||||
WITHOUT_RUST=true
|
||||
shift
|
||||
;;
|
||||
-hardsubx)
|
||||
HARDSUBX=true
|
||||
RUST_FEATURES="--features hardsubx_ocr"
|
||||
@@ -90,34 +86,29 @@ echo "Running pre-build script..."
|
||||
./pre-build.sh
|
||||
echo "Trying to compile..."
|
||||
|
||||
if [ "$WITHOUT_RUST" = true ]; then
|
||||
echo "Building without rust files..."
|
||||
BLD_FLAGS="$BLD_FLAGS -DDISABLE_RUST"
|
||||
else
|
||||
BLD_LINKER="$BLD_LINKER ./libccx_rust.a"
|
||||
echo "Checking for cargo..."
|
||||
if ! [ -x "$(command -v cargo)" ]; then
|
||||
echo 'Error: cargo is not installed.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rustc_version="$(rustc --version)"
|
||||
semver=( ${rustc_version//./ } )
|
||||
version="${semver[1]}.${semver[2]}.${semver[3]}"
|
||||
MSRV="1.54.0"
|
||||
if [ "$(printf '%s\n' "$MSRV" "$version" | sort -V | head -n1)" = "$MSRV" ]; then
|
||||
echo "rustc >= MSRV(${MSRV})"
|
||||
else
|
||||
echo "Minimum supported rust version(MSRV) is ${MSRV}, please upgrade rust"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building rust files..."
|
||||
(cd ../src/rust && CARGO_TARGET_DIR=../../linux/rust cargo build $RUST_PROFILE $RUST_FEATURES) || { echo "Failed. " ; exit 1; }
|
||||
|
||||
cp $RUST_LIB ./libccx_rust.a
|
||||
BLD_LINKER="$BLD_LINKER ./libccx_rust.a"
|
||||
echo "Checking for cargo..."
|
||||
if ! [ -x "$(command -v cargo)" ]; then
|
||||
echo 'Error: cargo is not installed.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
rustc_version="$(rustc --version)"
|
||||
semver=( ${rustc_version//./ } )
|
||||
version="${semver[1]}.${semver[2]}.${semver[3]}"
|
||||
MSRV="1.54.0"
|
||||
if [ "$(printf '%s\n' "$MSRV" "$version" | sort -V | head -n1)" = "$MSRV" ]; then
|
||||
echo "rustc >= MSRV(${MSRV})"
|
||||
else
|
||||
echo "Minimum supported rust version(MSRV) is ${MSRV}, please upgrade rust"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building rust files..."
|
||||
(cd ../src/rust && CARGO_TARGET_DIR=../../linux/rust cargo build $RUST_PROFILE $RUST_FEATURES) || { echo "Failed. " ; exit 1; }
|
||||
|
||||
cp $RUST_LIB ./libccx_rust.a
|
||||
|
||||
echo "Building ccextractor"
|
||||
out=$((LC_ALL=C gcc $BLD_FLAGS $BLD_INCLUDE -o ccextractor $BLD_SOURCES $BLD_LINKER)2>&1)
|
||||
res=$?
|
||||
|
||||
@@ -300,12 +300,8 @@ 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=
|
||||
|
||||
@@ -1,58 +1,183 @@
|
||||
#!/bin/bash
|
||||
cd `dirname $0`
|
||||
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DDISABLE_RUST"
|
||||
[[ $1 = "OCR" ]] && BLD_FLAGS="$BLD_FLAGS -DENABLE_OCR"
|
||||
|
||||
RUST_LIB="rust/release/libccx_rust.a"
|
||||
RUST_PROFILE="--release"
|
||||
RUST_FEATURES=""
|
||||
|
||||
# Parse command line arguments
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case $1 in
|
||||
OCR)
|
||||
ENABLE_OCR=true
|
||||
shift
|
||||
;;
|
||||
-debug)
|
||||
DEBUG=true
|
||||
RUST_PROFILE=""
|
||||
RUST_LIB="rust/debug/libccx_rust.a"
|
||||
shift
|
||||
;;
|
||||
-hardsubx)
|
||||
HARDSUBX=true
|
||||
RUST_FEATURES="--features hardsubx_ocr"
|
||||
shift
|
||||
;;
|
||||
-*)
|
||||
echo "Unknown option $1"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
|
||||
|
||||
# Add debug flags if needed
|
||||
if [[ "$DEBUG" == "true" ]]; then
|
||||
BLD_FLAGS="$BLD_FLAGS -g -fsanitize=address"
|
||||
fi
|
||||
|
||||
# Add OCR support if requested
|
||||
if [[ "$ENABLE_OCR" == "true" ]]; then
|
||||
BLD_FLAGS="$BLD_FLAGS -DENABLE_OCR"
|
||||
fi
|
||||
|
||||
# Add hardsubx support if requested
|
||||
if [[ "$HARDSUBX" == "true" ]]; then
|
||||
BLD_FLAGS="$BLD_FLAGS -DENABLE_HARDSUBX"
|
||||
fi
|
||||
|
||||
BLD_INCLUDE="-I../src/ -I../src/lib_ccx -I../src/lib_hash -I../src/thirdparty/libpng -I../src/thirdparty -I../src/thirdparty/zlib -I../src/thirdparty/freetype/include `pkg-config --cflags --silence-errors gpac`"
|
||||
[[ $1 = "OCR" ]] && BLD_INCLUDE="$BLD_INCLUDE `pkg-config --cflags --silence-errors tesseract`"
|
||||
|
||||
if [[ "$ENABLE_OCR" == "true" ]]; then
|
||||
BLD_INCLUDE="$BLD_INCLUDE `pkg-config --cflags --silence-errors tesseract`"
|
||||
fi
|
||||
|
||||
SRC_CCX="$(find ../src/lib_ccx -name '*.c')"
|
||||
SRC_LIB_HASH="$(find ../src/thirdparty/lib_hash -name '*.c')"
|
||||
SRC_LIBPNG="$(find ../src/thirdparty/libpng -name '*.c')"
|
||||
SRC_UTF8="../src/thirdparty/utf8proc/utf8proc.c"
|
||||
SRC_ZLIB="$(find ../src/thirdparty/zlib -name '*.c')"
|
||||
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \
|
||||
../src/thirdparty/freetype/base/ftbase.c \
|
||||
../src/thirdparty/freetype/base/ftbbox.c \
|
||||
../src/thirdparty/freetype/base/ftbdf.c \
|
||||
../src/thirdparty/freetype/base/ftbitmap.c \
|
||||
../src/thirdparty/freetype/base/ftcid.c \
|
||||
../src/thirdparty/freetype/base/ftfntfmt.c \
|
||||
../src/thirdparty/freetype/base/ftfstype.c \
|
||||
../src/thirdparty/freetype/base/ftgasp.c \
|
||||
../src/thirdparty/freetype/base/ftglyph.c \
|
||||
../src/thirdparty/freetype/base/ftgxval.c \
|
||||
../src/thirdparty/freetype/base/ftinit.c \
|
||||
../src/thirdparty/freetype/base/ftlcdfil.c \
|
||||
../src/thirdparty/freetype/base/ftmm.c \
|
||||
../src/thirdparty/freetype/base/ftotval.c \
|
||||
../src/thirdparty/freetype/base/ftpatent.c \
|
||||
../src/thirdparty/freetype/base/ftpfr.c \
|
||||
../src/thirdparty/freetype/base/ftstroke.c \
|
||||
../src/thirdparty/freetype/base/ftsynth.c \
|
||||
../src/thirdparty/freetype/base/ftsystem.c \
|
||||
../src/thirdparty/freetype/base/fttype1.c \
|
||||
../src/thirdparty/freetype/base/ftwinfnt.c \
|
||||
../src/thirdparty/freetype/bdf/bdf.c \
|
||||
../src/thirdparty/freetype/bzip2/ftbzip2.c \
|
||||
../src/thirdparty/freetype/cache/ftcache.c \
|
||||
../src/thirdparty/freetype/cff/cff.c \
|
||||
../src/thirdparty/freetype/cid/type1cid.c \
|
||||
../src/thirdparty/freetype/gzip/ftgzip.c \
|
||||
../src/thirdparty/freetype/lzw/ftlzw.c \
|
||||
../src/thirdparty/freetype/pcf/pcf.c \
|
||||
../src/thirdparty/freetype/pfr/pfr.c \
|
||||
../src/thirdparty/freetype/psaux/psaux.c \
|
||||
../src/thirdparty/freetype/pshinter/pshinter.c \
|
||||
../src/thirdparty/freetype/psnames/psnames.c \
|
||||
../src/thirdparty/freetype/raster/raster.c \
|
||||
../src/thirdparty/freetype/sfnt/sfnt.c \
|
||||
../src/thirdparty/freetype/smooth/smooth.c \
|
||||
../src/thirdparty/freetype/truetype/truetype.c \
|
||||
../src/thirdparty/freetype/type1/type1.c \
|
||||
../src/thirdparty/freetype/type42/type42.c \
|
||||
../src/thirdparty/freetype/winfonts/winfnt.c"
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_API $SRC_CCX $SRC_LIB_HASH $SRC_LIBPNG $SRC_UTF8 $SRC_ZLIB $SRC_ZVBI $SRC_FREETYPE"
|
||||
BLD_LINKER="-lm -liconv -lpthread -ldl `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors gpac`"
|
||||
[[ $1 = "OCR" ]] && BLD_LINKER="$BLD_LINKER `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors lept`"
|
||||
../src/thirdparty/freetype/base/ftbase.c \
|
||||
../src/thirdparty/freetype/base/ftbbox.c \
|
||||
../src/thirdparty/freetype/base/ftbdf.c \
|
||||
../src/thirdparty/freetype/base/ftbitmap.c \
|
||||
../src/thirdparty/freetype/base/ftcid.c \
|
||||
../src/thirdparty/freetype/base/ftfntfmt.c \
|
||||
../src/thirdparty/freetype/base/ftfstype.c \
|
||||
../src/thirdparty/freetype/base/ftgasp.c \
|
||||
../src/thirdparty/freetype/base/ftglyph.c \
|
||||
../src/thirdparty/freetype/base/ftgxval.c \
|
||||
../src/thirdparty/freetype/base/ftinit.c \
|
||||
../src/thirdparty/freetype/base/ftlcdfil.c \
|
||||
../src/thirdparty/freetype/base/ftmm.c \
|
||||
../src/thirdparty/freetype/base/ftotval.c \
|
||||
../src/thirdparty/freetype/base/ftpatent.c \
|
||||
../src/thirdparty/freetype/base/ftpfr.c \
|
||||
../src/thirdparty/freetype/base/ftstroke.c \
|
||||
../src/thirdparty/freetype/base/ftsynth.c \
|
||||
../src/thirdparty/freetype/base/ftsystem.c \
|
||||
../src/thirdparty/freetype/base/fttype1.c \
|
||||
../src/thirdparty/freetype/base/ftwinfnt.c \
|
||||
../src/thirdparty/freetype/bdf/bdf.c \
|
||||
../src/thirdparty/freetype/bzip2/ftbzip2.c \
|
||||
../src/thirdparty/freetype/cache/ftcache.c \
|
||||
../src/thirdparty/freetype/cff/cff.c \
|
||||
../src/thirdparty/freetype/cid/type1cid.c \
|
||||
../src/thirdparty/freetype/gzip/ftgzip.c \
|
||||
../src/thirdparty/freetype/lzw/ftlzw.c \
|
||||
../src/thirdparty/freetype/pcf/pcf.c \
|
||||
../src/thirdparty/freetype/pfr/pfr.c \
|
||||
../src/thirdparty/freetype/psaux/psaux.c \
|
||||
../src/thirdparty/freetype/pshinter/pshinter.c \
|
||||
../src/thirdparty/freetype/psnames/psnames.c \
|
||||
../src/thirdparty/freetype/raster/raster.c \
|
||||
../src/thirdparty/freetype/sfnt/sfnt.c \
|
||||
../src/thirdparty/freetype/smooth/smooth.c \
|
||||
../src/thirdparty/freetype/truetype/truetype.c \
|
||||
../src/thirdparty/freetype/type1/type1.c \
|
||||
../src/thirdparty/freetype/type42/type42.c \
|
||||
../src/thirdparty/freetype/winfonts/winfnt.c"
|
||||
|
||||
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_LIB_HASH $SRC_LIBPNG $SRC_UTF8 $SRC_ZLIB $SRC_FREETYPE"
|
||||
|
||||
BLD_LINKER="-lm -liconv -lpthread -ldl `pkg-config --libs --silence-errors gpac`"
|
||||
|
||||
if [[ "$ENABLE_OCR" == "true" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors lept`"
|
||||
fi
|
||||
|
||||
if [[ "$HARDSUBX" == "true" ]]; then
|
||||
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter"
|
||||
fi
|
||||
|
||||
echo "Running pre-build script..."
|
||||
./pre-build.sh
|
||||
gcc $BLD_FLAGS $BLD_INCLUDE -o ccextractor $BLD_SOURCES $BLD_LINKER
|
||||
echo "Trying to compile..."
|
||||
|
||||
# Check for cargo
|
||||
echo "Checking for cargo..."
|
||||
if ! [ -x "$(command -v cargo)" ]; then
|
||||
echo 'Error: cargo is not installed.' >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check rust version
|
||||
rustc_version="$(rustc --version)"
|
||||
semver=( ${rustc_version//./ } )
|
||||
version="${semver[1]}.${semver[2]}.${semver[3]}"
|
||||
MSRV="1.54.0"
|
||||
if [ "$(printf '%s\n' "$MSRV" "$version" | sort -V | head -n1)" = "$MSRV" ]; then
|
||||
echo "rustc >= MSRV(${MSRV})"
|
||||
else
|
||||
echo "Minimum supported rust version(MSRV) is ${MSRV}, please upgrade rust"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Building rust files..."
|
||||
(cd ../src/rust && CARGO_TARGET_DIR=../../mac/rust cargo build $RUST_PROFILE $RUST_FEATURES) || { echo "Failed building Rust components." ; exit 1; }
|
||||
|
||||
# Copy the Rust library
|
||||
cp $RUST_LIB ./libccx_rust.a
|
||||
|
||||
# Add Rust library to linker flags
|
||||
BLD_LINKER="$BLD_LINKER ./libccx_rust.a"
|
||||
|
||||
echo "Building ccextractor"
|
||||
out=$((LC_ALL=C gcc $BLD_FLAGS $BLD_INCLUDE -o ccextractor $BLD_SOURCES $BLD_LINKER) 2>&1)
|
||||
res=$?
|
||||
|
||||
# Handle common error cases
|
||||
if [[ $out == *"gcc: command not found"* ]]; then
|
||||
echo "Error: please install gcc or Xcode command line tools"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [[ $out == *"curl.h: No such file or directory"* ]]; then
|
||||
echo "Error: please install curl development library"
|
||||
exit 2
|
||||
fi
|
||||
|
||||
if [[ $out == *"capi.h: No such file or directory"* ]]; then
|
||||
echo "Error: please install tesseract development library"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
if [[ $out == *"allheaders.h: No such file or directory"* ]]; then
|
||||
echo "Error: please install leptonica development library"
|
||||
exit 4
|
||||
fi
|
||||
|
||||
if [[ $res -ne 0 ]]; then # Unknown error
|
||||
echo "Compiled with errors"
|
||||
>&2 echo "$out"
|
||||
exit 5
|
||||
fi
|
||||
|
||||
if [[ "$out" != "" ]]; then
|
||||
echo "$out"
|
||||
echo "Compilation successful, compiler message shown in previous lines"
|
||||
else
|
||||
echo "Compilation successful, no compiler messages."
|
||||
fi
|
||||
@@ -6,7 +6,6 @@ include (CTest)
|
||||
option (WITH_FFMPEG "Build using FFmpeg demuxer and decoder" OFF)
|
||||
option (WITH_OCR "Build with OCR (Optical Character Recognition) feature" OFF)
|
||||
option (WITH_HARDSUBX "Build with support for burned-in subtitles" OFF)
|
||||
option (WITHOUT_RUST "Build without Rust library" OFF)
|
||||
|
||||
# Version number
|
||||
set (CCEXTRACTOR_VERSION_MAJOR 0)
|
||||
@@ -237,12 +236,10 @@ add_executable (ccextractor ${SOURCEFILE} ${FREETYPE_SOURCE} ${UTF8PROC_SOURCE})
|
||||
# Build with Rust library
|
||||
########################################################
|
||||
|
||||
if (PKG_CONFIG_FOUND AND NOT WITHOUT_RUST)
|
||||
if (PKG_CONFIG_FOUND)
|
||||
add_subdirectory (rust)
|
||||
set (EXTRA_LIBS ${EXTRA_LIBS} ccx_rust)
|
||||
else ()
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDISABLE_RUST")
|
||||
endif (PKG_CONFIG_FOUND AND NOT WITHOUT_RUST)
|
||||
endif (PKG_CONFIG_FOUND)
|
||||
|
||||
|
||||
target_link_libraries (ccextractor ${EXTRA_LIBS})
|
||||
|
||||
@@ -418,15 +418,9 @@ int main(int argc, char *argv[])
|
||||
// See docs/ccextractor.cnf.sample for more info.
|
||||
parse_configuration(&ccx_options);
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
ccxr_init_basic_logger();
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
int compile_ret = ccxr_parse_parameters(argc, argv);
|
||||
#else
|
||||
int compile_ret = parse_parameters(&ccx_options, argc, argv);
|
||||
#endif
|
||||
|
||||
if (compile_ret == EXIT_NO_INPUT_FILES)
|
||||
{
|
||||
|
||||
@@ -87,10 +87,6 @@ if (WITH_HARDSUBX)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_HARDSUBX")
|
||||
endif (WITH_HARDSUBX)
|
||||
|
||||
if (WITHOUT_RUST)
|
||||
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DDISABLE_RUST")
|
||||
endif (WITHOUT_RUST)
|
||||
|
||||
file (GLOB HeaderFiles *.h)
|
||||
file (WRITE ccx.pc "prefix=${CMAKE_INSTALL_PREFIX}\n"
|
||||
"includedir=\${prefix}/include\n"
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
// Hold functions to read streams on a bit or byte oriented basis
|
||||
// plus some data related helper functions.
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
extern uint64_t ccxr_next_bits(struct bitstream *bs, uint32_t bnum);
|
||||
extern uint64_t ccxr_read_bits(struct bitstream *bs, uint32_t bnum);
|
||||
extern int ccxr_skip_bits(struct bitstream *bs, uint32_t bnum);
|
||||
@@ -16,7 +15,6 @@ extern int64_t ccxr_read_exp_golomb(struct bitstream *bs);
|
||||
extern uint8_t ccxr_reverse8(uint8_t data);
|
||||
extern uint64_t ccxr_bitstream_get_num(struct bitstream *bs, unsigned bytes, int advance);
|
||||
extern int64_t ccxr_read_int(struct bitstream *bs, unsigned bnum);
|
||||
#endif
|
||||
|
||||
// Guidelines for all bitsream functions:
|
||||
// * No function shall advance the pointer past the end marker
|
||||
@@ -50,96 +48,14 @@ int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *
|
||||
// there are not enough bits left in the bitstream.
|
||||
uint64_t next_bits(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_next_bits(bstr, bnum);
|
||||
#else
|
||||
uint64_t res = 0;
|
||||
|
||||
if (bnum > 64)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In next_bits: Argument is greater than the maximum bit number i.e. 64: %u!", bnum);
|
||||
|
||||
// Sanity check
|
||||
if (bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In next_bits: Bitstream can not have negative length!");
|
||||
|
||||
// Keep a negative bitstream.bitsleft, but correct it.
|
||||
if (bstr->bitsleft <= 0)
|
||||
{
|
||||
bstr->bitsleft -= bnum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate the remaining number of bits in bitstream after reading.
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1) * 8 + bstr->bpos - bnum;
|
||||
if (bstr->bitsleft < 0)
|
||||
return 0;
|
||||
|
||||
// Special case for reading zero bits. Return zero
|
||||
if (bnum == 0)
|
||||
return 0;
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
unsigned char *vpos = bstr->pos;
|
||||
|
||||
if (vbit < 1 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In next_bits: Illegal bit position value %d!", vbit);
|
||||
}
|
||||
|
||||
while (1)
|
||||
{
|
||||
if (vpos >= bstr->end)
|
||||
{
|
||||
// We should not get here ...
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In next_bits: Trying to read after end of data ...");
|
||||
}
|
||||
|
||||
res |= (*vpos & (0x01 << (vbit - 1)) ? 1 : 0);
|
||||
vbit--;
|
||||
bnum--;
|
||||
|
||||
if (vbit == 0)
|
||||
{
|
||||
vpos++;
|
||||
vbit = 8;
|
||||
}
|
||||
|
||||
if (bnum)
|
||||
{
|
||||
res <<= 1;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
// Remember the bitstream position
|
||||
bstr->_i_bpos = vbit;
|
||||
bstr->_i_pos = vpos;
|
||||
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read bnum bits from bitstream bstr with the most significant
|
||||
// bit read first. A 64 bit unsigned integer is returned.
|
||||
uint64_t read_bits(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_read_bits(bstr, bnum);
|
||||
#else
|
||||
uint64_t res = next_bits(bstr, bnum);
|
||||
|
||||
// Special case for reading zero bits. Also abort when not enough
|
||||
// bits are left. Return zero
|
||||
if (bnum == 0 || bstr->bitsleft < 0)
|
||||
return 0;
|
||||
|
||||
// Advance the bitstream
|
||||
bstr->bpos = bstr->_i_bpos;
|
||||
bstr->pos = bstr->_i_pos;
|
||||
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
// This function will advance the bitstream by bnum bits, if possible.
|
||||
@@ -147,39 +63,7 @@ uint64_t read_bits(struct bitstream *bstr, unsigned bnum)
|
||||
// Return TRUE when successful, otherwise FALSE
|
||||
int skip_bits(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_skip_bits(bstr, bnum);
|
||||
#else
|
||||
// Sanity check
|
||||
if (bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In skip_bits: bitstream length cannot be negative!");
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
bstr->bitsleft -= bnum;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Calculate the remaining number of bits in bitstream after reading.
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1) * 8 + bstr->bpos - bnum;
|
||||
if (bstr->bitsleft < 0)
|
||||
return 0;
|
||||
|
||||
// Special case for reading zero bits. Return one == success
|
||||
if (bnum == 0)
|
||||
return 1;
|
||||
|
||||
bstr->bpos -= bnum % 8;
|
||||
bstr->pos += bnum / 8;
|
||||
|
||||
if (bstr->bpos < 1)
|
||||
{
|
||||
bstr->bpos += 8;
|
||||
bstr->pos += 1;
|
||||
}
|
||||
return 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return TRUE if the current position in the bitstream is on a byte
|
||||
@@ -187,62 +71,13 @@ int skip_bits(struct bitstream *bstr, unsigned bnum)
|
||||
// a byte, otherwise return FALSE
|
||||
int is_byte_aligned(struct bitstream *bstr)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_is_byte_aligned(bstr);
|
||||
#else
|
||||
// Sanity check
|
||||
if (bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In is_byte_aligned: bitstream length can not be negative!");
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
|
||||
if (vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In is_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
|
||||
if (vbit == 8)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Move bitstream to next byte border. Adjust bitsleft.
|
||||
void make_byte_aligned(struct bitstream *bstr)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
ccxr_make_byte_aligned(bstr);
|
||||
#else
|
||||
// Sanity check
|
||||
if (bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In make_byte_aligned: bitstream length can not be negative!");
|
||||
|
||||
int vbit = bstr->bpos;
|
||||
|
||||
if (vbit == 0 || vbit > 8)
|
||||
{
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In make_byte_aligned: Illegal bit position value %d!\n", vbit);
|
||||
}
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
// Pay attention to the bit alignment
|
||||
bstr->bitsleft = (bstr->bitsleft - 7) / 8 * 8;
|
||||
return;
|
||||
}
|
||||
|
||||
if (bstr->bpos != 8)
|
||||
{
|
||||
bstr->bpos = 8;
|
||||
bstr->pos += 1;
|
||||
}
|
||||
// Reset, in case a next_???() function was used before
|
||||
bstr->bitsleft = 0LL + 8 * (bstr->end - bstr->pos - 1) + bstr->bpos;
|
||||
|
||||
return;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return pointer to first of bynum bytes from the bitstream if the
|
||||
@@ -252,31 +87,7 @@ void make_byte_aligned(struct bitstream *bstr)
|
||||
// This function does not advance the bitstream pointer.
|
||||
unsigned char *next_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return (unsigned char *)ccxr_next_bytes(bstr, bynum);
|
||||
#else
|
||||
// Sanity check
|
||||
if (bstr->end - bstr->pos < 0)
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In next_bytes: bitstream length can not be negative!");
|
||||
|
||||
// Keep a negative bstr->bitsleft, but correct it.
|
||||
if (bstr->bitsleft < 0)
|
||||
{
|
||||
bstr->bitsleft -= bynum * 8;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bstr->bitsleft = 0LL + (bstr->end - bstr->pos - 1) * 8 + bstr->bpos - bynum * 8;
|
||||
|
||||
if (!is_byte_aligned(bstr) || bstr->bitsleft < 0 || bynum < 1)
|
||||
return NULL;
|
||||
|
||||
// Remember the bitstream position
|
||||
bstr->_i_bpos = 8;
|
||||
bstr->_i_pos = bstr->pos + bynum;
|
||||
|
||||
return bstr->pos;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return pointer to first of bynum bytes from the bitstream if the
|
||||
@@ -286,19 +97,7 @@ unsigned char *next_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
// This function does advance the bitstream pointer.
|
||||
unsigned char *read_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return (unsigned char *)ccxr_read_bytes(bstr, bynum);
|
||||
#else
|
||||
unsigned char *res = next_bytes(bstr, bynum);
|
||||
|
||||
// Advance the bitstream when a read was possible
|
||||
if (res)
|
||||
{
|
||||
bstr->bpos = bstr->_i_bpos;
|
||||
bstr->pos = bstr->_i_pos;
|
||||
}
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return an integer number with "bytes" precision from the current
|
||||
@@ -309,77 +108,19 @@ unsigned char *read_bytes(struct bitstream *bstr, unsigned bynum)
|
||||
// little-endian and big-endian CPUs.
|
||||
uint64_t bitstream_get_num(struct bitstream *bstr, unsigned bytes, int advance)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_bitstream_get_num(bstr, bytes, advance);
|
||||
#else
|
||||
void *bpos;
|
||||
uint64_t rval = 0;
|
||||
|
||||
if (advance)
|
||||
bpos = read_bytes(bstr, bytes);
|
||||
else
|
||||
bpos = next_bytes(bstr, bytes);
|
||||
|
||||
if (!bpos)
|
||||
return 0;
|
||||
|
||||
switch (bytes)
|
||||
{
|
||||
case 1:
|
||||
case 2:
|
||||
case 4:
|
||||
case 8:
|
||||
break;
|
||||
default:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "In bitstream_get_num: Illegal precision value [%u]!",
|
||||
bytes);
|
||||
break;
|
||||
}
|
||||
for (unsigned i = 0; i < bytes; i++)
|
||||
{
|
||||
unsigned char *ucpos = ((unsigned char *)bpos) + bytes - i - 1; // Read backwards
|
||||
unsigned char uc = *ucpos;
|
||||
rval = (rval << 8) + uc;
|
||||
}
|
||||
return rval;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read unsigned Exp-Golomb code from bitstream
|
||||
uint64_t read_exp_golomb_unsigned(struct bitstream *bstr)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_read_exp_golomb_unsigned(bstr);
|
||||
#else
|
||||
uint64_t res = 0;
|
||||
int zeros = 0;
|
||||
|
||||
while (!read_bits(bstr, 1) && bstr->bitsleft >= 0)
|
||||
zeros++;
|
||||
|
||||
res = (0x01 << zeros) - 1 + read_bits(bstr, zeros);
|
||||
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read signed Exp-Golomb code from bitstream
|
||||
int64_t read_exp_golomb(struct bitstream *bstr)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_read_exp_golomb(bstr);
|
||||
#else
|
||||
int64_t res = 0;
|
||||
|
||||
res = read_exp_golomb_unsigned(bstr);
|
||||
|
||||
// The following function might truncate when res+1 overflows
|
||||
// res = (res+1)/2 * (res % 2 ? 1 : -1);
|
||||
// Use this:
|
||||
res = (res / 2 + (res % 2 ? 1 : 0)) * (res % 2 ? 1 : -1);
|
||||
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Read unsigned integer with bnum bits length. Basically an
|
||||
@@ -392,33 +133,11 @@ uint64_t read_int_unsigned(struct bitstream *bstr, unsigned bnum)
|
||||
// Read signed integer with bnum bits length.
|
||||
int64_t read_int(struct bitstream *bstr, unsigned bnum)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_read_int(bstr, bnum);
|
||||
#else
|
||||
uint64_t res = read_bits(bstr, bnum);
|
||||
|
||||
// Special case for reading zero bits. Return zero
|
||||
if (bnum == 0)
|
||||
return 0;
|
||||
|
||||
return (0xFFFFFFFFFFFFFFFFULL << bnum) | res;
|
||||
#endif
|
||||
}
|
||||
|
||||
// Return the value with the bit order reversed.
|
||||
uint8_t reverse8(uint8_t data)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_reverse8(data);
|
||||
#else
|
||||
uint8_t res = 0;
|
||||
|
||||
for (int k = 0; k < 8; k++)
|
||||
{
|
||||
res <<= 1;
|
||||
res |= (data & (0x01 << k) ? 1 : 0);
|
||||
}
|
||||
|
||||
return res;
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -26,17 +26,7 @@ int fdprintf(int fd, const char *fmt, ...)
|
||||
void millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes,
|
||||
unsigned *seconds, unsigned *ms)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_millis_to_time(milli, hours, minutes, seconds, ms);
|
||||
#endif /* ifndef DISABLE_RUST */
|
||||
// LLONG milli = (LLONG) ((ccblock*1000)/29.97);
|
||||
*ms = (unsigned)(milli % 1000); // milliseconds
|
||||
milli = (milli - *ms) / 1000; // Remainder, in seconds
|
||||
*seconds = (int)milli % 60;
|
||||
milli = (milli - *seconds) / 60; // Remainder, in minutes
|
||||
*minutes = (int)(milli % 60);
|
||||
milli = (milli - *minutes) / 60; // Remainder, in hours
|
||||
*hours = (int)milli;
|
||||
}
|
||||
|
||||
/* Frees the given pointer */
|
||||
|
||||
@@ -44,9 +44,7 @@ int cc608_parity(unsigned int byte);
|
||||
int fdprintf(int fd, const char *fmt, ...);
|
||||
void millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds, unsigned *ms);
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
extern void ccxr_millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds, unsigned *ms);
|
||||
#endif // !DISABLE_RUST
|
||||
|
||||
void freep(void *arg);
|
||||
void dbg_print(LLONG mask, const char *fmt, ...);
|
||||
|
||||
@@ -30,7 +30,6 @@ int gop_rollover = 0;
|
||||
|
||||
struct ccx_common_timing_settings_t ccx_common_timing_settings;
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
void ccxr_add_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts);
|
||||
void ccxr_set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts);
|
||||
int ccxr_set_fts(struct ccx_common_timing_ctx *ctx);
|
||||
@@ -40,7 +39,6 @@ char *ccxr_print_mstime_static(LLONG mstime, char *buf);
|
||||
void ccxr_print_debug_timing(struct ccx_common_timing_ctx *ctx);
|
||||
void ccxr_calculate_ms_gop_time(struct gop_time_code *g);
|
||||
int ccxr_gop_accepted(struct gop_time_code *g);
|
||||
#endif
|
||||
|
||||
void ccx_common_timing_init(LLONG *file_position, int no_sync)
|
||||
{
|
||||
@@ -85,244 +83,27 @@ struct ccx_common_timing_ctx *init_timing_ctx(struct ccx_common_timing_settings_
|
||||
|
||||
void add_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_add_current_pts(ctx, pts);
|
||||
#endif
|
||||
|
||||
set_current_pts(ctx, ctx->current_pts + pts);
|
||||
}
|
||||
|
||||
void set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_set_current_pts(ctx, pts);
|
||||
#endif
|
||||
LLONG prev_pts = ctx->current_pts;
|
||||
ctx->current_pts = pts;
|
||||
if (ctx->pts_set == 0)
|
||||
ctx->pts_set = 1;
|
||||
dbg_print(CCX_DMT_VIDES, "PTS: %s (%8u)", print_mstime_static(ctx->current_pts / (MPEG_CLOCK_FREQ / 1000)),
|
||||
(unsigned)(ctx->current_pts));
|
||||
dbg_print(CCX_DMT_VIDES, " FTS: %s \n", print_mstime_static(ctx->fts_now));
|
||||
|
||||
// Check if PTS reset
|
||||
if (ctx->current_pts < prev_pts)
|
||||
{
|
||||
ctx->pts_reset = 1;
|
||||
}
|
||||
}
|
||||
|
||||
int set_fts(struct ccx_common_timing_ctx *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_set_fts(ctx);
|
||||
#endif
|
||||
int pts_jump = 0;
|
||||
|
||||
// ES don't have PTS unless GOP timing is used
|
||||
if (!ctx->pts_set && ccx_common_timing_settings.is_elementary_stream)
|
||||
return CCX_OK;
|
||||
|
||||
// First check for timeline jump (only when min_pts was set (implies sync_pts)).
|
||||
int dif = 0;
|
||||
if (ctx->pts_set == 2)
|
||||
{
|
||||
dif = (int)(ctx->current_pts - ctx->sync_pts) / MPEG_CLOCK_FREQ;
|
||||
|
||||
if (ccx_common_timing_settings.disable_sync_check)
|
||||
{
|
||||
// Disables sync check. Used for several input formats.
|
||||
dif = 0;
|
||||
}
|
||||
|
||||
if (dif < -0.2 || dif >= max_dif)
|
||||
{
|
||||
// ATSC specs: More than 3501 ms means missing component
|
||||
ccx_common_logging.log_ftn("\nWarning: Reference clock has changed abruptly (%d seconds filepos=%lld), attempting to synchronize\n", (int)dif, *ccx_common_timing_settings.file_position);
|
||||
ccx_common_logging.log_ftn("Last sync PTS value: %lld\n", ctx->sync_pts);
|
||||
ccx_common_logging.log_ftn("Current PTS value: %lld\n", ctx->current_pts);
|
||||
ccx_common_logging.log_ftn("Note: You can disable this behavior by adding -ignoreptsjumps to the parameters.\n");
|
||||
pts_jump = 1;
|
||||
pts_big_change = 1;
|
||||
|
||||
// Discard the gap if it is not on an I-frame or temporal reference zero.
|
||||
if (ctx->current_tref != 0 && ctx->current_picture_coding_type != CCX_FRAME_TYPE_I_FRAME)
|
||||
{
|
||||
ctx->fts_now = ctx->fts_max;
|
||||
ccx_common_logging.log_ftn("Change did not occur on first frame - probably a broken GOP\n");
|
||||
return CCX_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If min_pts was set just before a rollover we compensate by "roll-oving" it too
|
||||
if (ctx->pts_set == 2 && !ctx->min_pts_adjusted) // min_pts set
|
||||
{
|
||||
// We want to be aware of the upcoming rollover, not after it happened, so we don't take
|
||||
// the 3 most significant bits but the 3 next ones
|
||||
uint64_t min_pts_big_bits = (ctx->min_pts >> 30) & 7;
|
||||
uint64_t cur_pts_big_bits = (ctx->current_pts >> 30) & 7;
|
||||
if (cur_pts_big_bits == 7 && !min_pts_big_bits)
|
||||
{
|
||||
// Huge difference possibly means the first min_pts was actually just over the boundary
|
||||
// Take the current_pts (smaller, accounting for the rollover) instead
|
||||
ctx->min_pts = ctx->current_pts;
|
||||
ctx->min_pts_adjusted = 1;
|
||||
}
|
||||
else if (cur_pts_big_bits >= 1 && cur_pts_big_bits <= 6) // Far enough from the boundary
|
||||
{
|
||||
// Prevent the eventual difference with min_pts to make a bad adjustment
|
||||
ctx->min_pts_adjusted = 1;
|
||||
}
|
||||
}
|
||||
// Set min_pts, fts_offset
|
||||
if (ctx->pts_set != 0)
|
||||
{
|
||||
ctx->pts_set = 2;
|
||||
|
||||
// Use this part only the first time min_pts is set. Later treat
|
||||
// it as a reference clock change
|
||||
if (ctx->current_pts < ctx->min_pts && !pts_jump)
|
||||
{
|
||||
// If this is the first GOP, and seq 0 was not encountered yet
|
||||
// we might reset min_pts/fts_offset again
|
||||
|
||||
ctx->min_pts = ctx->current_pts;
|
||||
|
||||
// Avoid next async test
|
||||
ctx->sync_pts = (LLONG)(ctx->current_pts - ctx->current_tref * 1000.0 / current_fps * (MPEG_CLOCK_FREQ / 1000));
|
||||
|
||||
if (ctx->current_tref == 0)
|
||||
{ // Earliest time in GOP.
|
||||
ctx->fts_offset = 0;
|
||||
}
|
||||
else if (total_frames_count - frames_since_ref_time == 0)
|
||||
{ // If this is the first frame (PES) there cannot be an offset.
|
||||
// This part is also reached for dvr-ms/NTSC (RAW) as
|
||||
// total_frames_count = frames_since_ref_time = 0 when
|
||||
// this is called for the first time.
|
||||
ctx->fts_offset = 0;
|
||||
}
|
||||
else
|
||||
{ // It needs to be "+1" because the current frame is
|
||||
// not yet counted.
|
||||
ctx->fts_offset = (LLONG)((total_frames_count - frames_since_ref_time + 1) * 1000.0 / current_fps);
|
||||
}
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "\nFirst sync time PTS: %s %+lldms (time before this PTS)\n",
|
||||
print_mstime_static(ctx->min_pts / (MPEG_CLOCK_FREQ / 1000)),
|
||||
ctx->fts_offset);
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "Total_frames_count %u frames_since_ref_time %u\n",
|
||||
total_frames_count, frames_since_ref_time);
|
||||
}
|
||||
|
||||
// -nosync disables syncing
|
||||
if (pts_jump && !ccx_common_timing_settings.no_sync)
|
||||
{
|
||||
// The current time in the old time base is calculated using
|
||||
// sync_pts (set at the beginning of the last GOP) plus the
|
||||
// time of the frames since then.
|
||||
ctx->fts_offset = ctx->fts_offset + (ctx->sync_pts - ctx->min_pts) / (MPEG_CLOCK_FREQ / 1000) + (LLONG)(frames_since_ref_time * 1000 / current_fps);
|
||||
ctx->fts_max = ctx->fts_offset;
|
||||
|
||||
// Start counting again from here
|
||||
ctx->pts_set = 1; // Force min to be set again
|
||||
ctx->sync_pts2fts_set = 0; // Make note of the new conversion values
|
||||
|
||||
// Avoid next async test - the gap might have occured on
|
||||
// current_tref != 0.
|
||||
ctx->sync_pts = (LLONG)(ctx->current_pts - ctx->current_tref * 1000.0 / current_fps * (MPEG_CLOCK_FREQ / 1000));
|
||||
// Set min_pts = sync_pts as this is used for fts_now
|
||||
ctx->min_pts = ctx->sync_pts;
|
||||
|
||||
ccx_common_logging.debug_ftn(CCX_DMT_TIME, "\nNew min PTS time: %s %+lldms (time before this PTS)\n",
|
||||
print_mstime_static(ctx->min_pts / (MPEG_CLOCK_FREQ / 1000)),
|
||||
ctx->fts_offset);
|
||||
}
|
||||
}
|
||||
|
||||
// Set sync_pts, fts_offset
|
||||
if (ctx->current_tref == 0)
|
||||
ctx->sync_pts = ctx->current_pts;
|
||||
|
||||
// Reset counters
|
||||
cb_field1 = 0;
|
||||
cb_field2 = 0;
|
||||
cb_708 = 0;
|
||||
|
||||
// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
|
||||
// for uninitialized min_pts
|
||||
if (1) // CFS: Remove or think decent condition
|
||||
{
|
||||
if (ctx->pts_set)
|
||||
{
|
||||
// If pts_set is TRUE we have min_pts
|
||||
ctx->fts_now = (LLONG)((ctx->current_pts - ctx->min_pts) / (MPEG_CLOCK_FREQ / 1000) + ctx->fts_offset);
|
||||
if (!ctx->sync_pts2fts_set)
|
||||
{
|
||||
ctx->sync_pts2fts_pts = ctx->current_pts;
|
||||
ctx->sync_pts2fts_fts = ctx->fts_now;
|
||||
ctx->sync_pts2fts_set = 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No PTS info at all!!
|
||||
ccx_common_logging.log_ftn("Set PTS called without any global timestamp set\n");
|
||||
return CCX_EINVAL;
|
||||
}
|
||||
}
|
||||
if (ctx->fts_now > ctx->fts_max)
|
||||
{
|
||||
ctx->fts_max = ctx->fts_now;
|
||||
}
|
||||
|
||||
// If PTS resets, then fix minimum_fts and fts_max
|
||||
if (ctx->pts_reset)
|
||||
{
|
||||
ctx->minimum_fts = 0;
|
||||
ctx->fts_max = ctx->fts_now;
|
||||
ctx->pts_reset = 0;
|
||||
}
|
||||
return CCX_OK;
|
||||
}
|
||||
|
||||
LLONG get_fts(struct ccx_common_timing_ctx *ctx, int current_field)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_get_fts(ctx, current_field);
|
||||
#endif
|
||||
|
||||
LLONG fts;
|
||||
|
||||
switch (current_field)
|
||||
{
|
||||
case 1:
|
||||
fts = ctx->fts_now + ctx->fts_global + cb_field1 * 1001 / 30;
|
||||
break;
|
||||
case 2:
|
||||
fts = ctx->fts_now + ctx->fts_global + cb_field2 * 1001 / 30;
|
||||
break;
|
||||
case 3:
|
||||
fts = ctx->fts_now + ctx->fts_global + cb_708 * 1001 / 30;
|
||||
break;
|
||||
default:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "get_fts: unhandled branch");
|
||||
}
|
||||
// ccx_common_logging.debug_ftn(CCX_DMT_TIME, "[FTS] "
|
||||
// "fts: %llu, fts_now: %llu, fts_global: %llu, current_field: %llu, cb_708: %llu\n",
|
||||
// fts, fts_now, fts_global, current_field, cb_708);
|
||||
return fts;
|
||||
}
|
||||
|
||||
LLONG get_fts_max(struct ccx_common_timing_ctx *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_get_fts_max(ctx);
|
||||
#endif
|
||||
|
||||
// This returns the maximum FTS that belonged to a frame. Caption block
|
||||
// counters are not applicable.
|
||||
return ctx->fts_max + ctx->fts_global;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -380,80 +161,21 @@ size_t print_mstime_buff(LLONG mstime, char *fmt, char *buf)
|
||||
char *print_mstime_static(LLONG mstime)
|
||||
{
|
||||
static char buf[15]; // 14 should be long enough
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_print_mstime_static(mstime, buf);
|
||||
#endif
|
||||
|
||||
print_mstime_buff(mstime, "%02u:%02u:%02u:%03u", buf);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* Helper function for to display debug timing info. */
|
||||
void print_debug_timing(struct ccx_common_timing_ctx *ctx)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_print_debug_timing(ctx);
|
||||
#endif
|
||||
|
||||
// Avoid wrong "Calc. difference" and "Asynchronous by" numbers
|
||||
// for uninitialized min_pts
|
||||
LLONG tempmin_pts = (ctx->min_pts == 0x01FFFFFFFFLL ? ctx->sync_pts : ctx->min_pts);
|
||||
|
||||
ccx_common_logging.log_ftn("Sync time stamps: PTS: %s ",
|
||||
print_mstime_static((ctx->sync_pts) / (MPEG_CLOCK_FREQ / 1000)));
|
||||
ccx_common_logging.log_ftn("GOP: %s \n", print_mstime_static(gop_time.ms));
|
||||
|
||||
// Length first GOP to last GOP
|
||||
LLONG goplenms = (LLONG)(gop_time.ms - first_gop_time.ms);
|
||||
// Length at last sync point
|
||||
LLONG ptslenms = (unsigned)((ctx->sync_pts - tempmin_pts) / (MPEG_CLOCK_FREQ / 1000) + ctx->fts_offset);
|
||||
|
||||
ccx_common_logging.log_ftn("Last FTS: %s",
|
||||
print_mstime_static(get_fts_max(ctx)));
|
||||
ccx_common_logging.log_ftn(" GOP start FTS: %s\n",
|
||||
print_mstime_static(fts_at_gop_start));
|
||||
|
||||
// Times are based on last GOP and/or sync time
|
||||
ccx_common_logging.log_ftn("Max FTS diff. to PTS: %6lldms GOP: %6lldms\n\n",
|
||||
get_fts_max(ctx) + (LLONG)(1000.0 / current_fps) - ptslenms,
|
||||
get_fts_max(ctx) + (LLONG)(1000.0 / current_fps) - goplenms);
|
||||
}
|
||||
|
||||
void calculate_ms_gop_time(struct gop_time_code *g)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_calculate_ms_gop_time(g);
|
||||
#endif
|
||||
|
||||
int seconds = (g->time_code_hours * 3600) + (g->time_code_minutes * 60) + g->time_code_seconds;
|
||||
g->ms = (LLONG)(1000 * (seconds + g->time_code_pictures / current_fps));
|
||||
if (gop_rollover)
|
||||
g->ms += 24 * 60 * 60 * 1000;
|
||||
}
|
||||
|
||||
int gop_accepted(struct gop_time_code *g)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_gop_accepted(g);
|
||||
#endif
|
||||
|
||||
if (!((g->time_code_hours <= 23) && (g->time_code_minutes <= 59) && (g->time_code_seconds <= 59) && (g->time_code_pictures <= 59)))
|
||||
return 0;
|
||||
|
||||
if (gop_time.time_code_hours == 23 && gop_time.time_code_minutes == 59 &&
|
||||
g->time_code_hours == 0 && g->time_code_minutes == 0)
|
||||
{
|
||||
gop_rollover = 1;
|
||||
return 1;
|
||||
}
|
||||
if (gop_time.inited)
|
||||
{
|
||||
if (gop_time.ms > g->ms)
|
||||
{
|
||||
// We are going back in time but it's not a complete day rollover
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -824,27 +824,6 @@ void dtvcc_process_character(dtvcc_service_decoder *decoder, dtvcc_symbol symbol
|
||||
}
|
||||
}
|
||||
|
||||
void dtvcc_decoder_flush(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder)
|
||||
{
|
||||
ccx_common_logging.debug_ftn(
|
||||
CCX_DMT_708, "[CEA-708] dtvcc_decoder_flush: Flushing decoder\n");
|
||||
int screen_content_changed = 0;
|
||||
for (int i = 0; i < CCX_DTVCC_MAX_WINDOWS; i++)
|
||||
{
|
||||
dtvcc_window *window = &decoder->windows[i];
|
||||
if (window->visible)
|
||||
{
|
||||
screen_content_changed = 1;
|
||||
dtvcc_window_update_time_hide(window, dtvcc->timing);
|
||||
dtvcc_window_copy_to_screen(decoder, window);
|
||||
window->visible = 0;
|
||||
}
|
||||
}
|
||||
if (screen_content_changed)
|
||||
dtvcc_screen_print(dtvcc, decoder);
|
||||
dtvcc_write_done(decoder->tv, dtvcc->encoder);
|
||||
}
|
||||
|
||||
//---------------------------------- COMMANDS ------------------------------------
|
||||
|
||||
void dtvcc_handle_CWx_SetCurrentWindow(dtvcc_service_decoder *decoder, int window_id)
|
||||
|
||||
@@ -11,39 +11,3 @@ EIA-708, SO INTERNALLY WE USE THIS TABLE (FOR CONVENIENCE)
|
||||
(there are several blank characters here, that's OK)
|
||||
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;
|
||||
}
|
||||
|
||||
unsigned char dtvcc_get_internal_from_G1(unsigned char g1_char)
|
||||
{
|
||||
return g1_char;
|
||||
}
|
||||
|
||||
// TODO: Probably not right
|
||||
// G2: Extended Control Code Set 1
|
||||
unsigned char dtvcc_get_internal_from_G2(unsigned char g2_char)
|
||||
{
|
||||
if (g2_char >= 0x20 && g2_char <= 0x3F)
|
||||
return g2_char - (unsigned char)0x20;
|
||||
if (g2_char >= 0x60 && g2_char <= 0x7F)
|
||||
return g2_char + (unsigned char)0x20;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
// TODO: Probably not right
|
||||
// G3: Future Characters and Icon Expansion
|
||||
unsigned char dtvcc_get_internal_from_G3(unsigned char g3_char)
|
||||
{
|
||||
if (g3_char == 0xa0) // The "CC" (closed captions) sign
|
||||
return 0x06;
|
||||
// Rest unmapped, so we return a blank space
|
||||
return 0x20;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,16 +3,9 @@
|
||||
|
||||
#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_*/
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
#include "utility.h"
|
||||
#include "ccx_common_common.h"
|
||||
|
||||
#if !defined(DISABLE_RUST) && defined(WIN32)
|
||||
#if defined(WIN32)
|
||||
extern void ccxr_close_handle(void *handle);
|
||||
#endif
|
||||
|
||||
@@ -590,10 +590,8 @@ void dtvcc_writer_init(dtvcc_writer_ctx *writer,
|
||||
|
||||
char *charset = cfg->all_services_charset ? cfg->all_services_charset : cfg->services_charsets[service_number - 1];
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
writer->fhandle = NULL;
|
||||
writer->charset = charset;
|
||||
#endif
|
||||
|
||||
if (charset)
|
||||
{
|
||||
@@ -611,7 +609,7 @@ void dtvcc_writer_cleanup(dtvcc_writer_ctx *writer)
|
||||
{
|
||||
if (writer->fd >= 0 && writer->fd != STDOUT_FILENO)
|
||||
close(writer->fd);
|
||||
#if !defined(DISABLE_RUST) && defined(WIN32)
|
||||
#if defined(WIN32)
|
||||
ccxr_close_handle(writer->fhandle);
|
||||
writer->charset = NULL;
|
||||
#endif
|
||||
|
||||
@@ -15,10 +15,8 @@ made to reuse, not duplicate, as many functions as possible */
|
||||
#include "ccx_encoders_mcc.h"
|
||||
#include "ccx_dtvcc.h"
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
extern int ccxr_process_cc_data(struct lib_cc_decode *dec_ctx, unsigned char *cc_data, int cc_count);
|
||||
extern void ccxr_flush_decoder(struct dtvcc_ctx *dtvcc, struct dtvcc_service_decoder *decoder);
|
||||
#endif
|
||||
|
||||
uint64_t utc_refvalue = UINT64_MAX; /* _UI64_MAX/UINT64_MAX means don't use UNIX, 0 = use current system time as reference, +1 use a specific reference */
|
||||
extern int in_xds_mode;
|
||||
@@ -54,9 +52,7 @@ int process_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx,
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
ret = ccxr_process_cc_data(dec_ctx, cc_data, cc_count);
|
||||
#endif
|
||||
|
||||
for (int j = 0; j < cc_count * 3; j = j + 3)
|
||||
{
|
||||
@@ -198,18 +194,7 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
|
||||
}
|
||||
if (timeok)
|
||||
{
|
||||
if (ctx->write_format != CCX_OF_RCWT)
|
||||
{
|
||||
#ifdef DISABLE_RUST
|
||||
char temp[4];
|
||||
temp[0] = cc_valid;
|
||||
temp[1] = cc_type;
|
||||
temp[2] = cc_block[1];
|
||||
temp[3] = cc_block[2];
|
||||
dtvcc_process_data(ctx->dtvcc, (const unsigned char *)temp);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
if (ctx->write_format == CCX_OF_RCWT)
|
||||
writercwtdata(ctx, cc_block, sub);
|
||||
}
|
||||
cb_708++;
|
||||
@@ -451,11 +436,7 @@ void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub)
|
||||
if (decoder->cc_count > 0)
|
||||
{
|
||||
ctx->current_field = 3;
|
||||
#ifndef DISABLE_RUST
|
||||
ccxr_flush_decoder(ctx->dtvcc, decoder);
|
||||
#else
|
||||
dtvcc_decoder_flush(ctx->dtvcc, decoder);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,10 +16,8 @@ int fsync(int fd)
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
int ccxr_get_str_basic(unsigned char *out_buffer, unsigned char *in_buffer, int trim_subs,
|
||||
enum ccx_encoding_type in_enc, enum ccx_encoding_type out_enc, int max_len);
|
||||
#endif
|
||||
// These are the default settings for plain transcripts. No times, no CC or caption mode, and no XDS.
|
||||
ccx_encoders_transcript_format ccx_encoders_default_transcript_settings =
|
||||
{
|
||||
@@ -118,223 +116,10 @@ void find_limit_characters(const unsigned char *line, int *first_non_blank, int
|
||||
}
|
||||
}
|
||||
|
||||
unsigned int utf8_to_latin1_map(const unsigned int code)
|
||||
{
|
||||
/* Code points 0 to U+00FF are the same in both. */
|
||||
if (code < 256U)
|
||||
return code;
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 0x0152U:
|
||||
return 188U; /* U+0152 = 0xBC: OE ligature */
|
||||
case 0x0153U:
|
||||
return 189U; /* U+0153 = 0xBD: oe ligature */
|
||||
case 0x0160U:
|
||||
return 166U; /* U+0160 = 0xA6: S with caron */
|
||||
case 0x0161U:
|
||||
return 168U; /* U+0161 = 0xA8: s with caron */
|
||||
case 0x0178U:
|
||||
return 190U; /* U+0178 = 0xBE: Y with diaresis */
|
||||
case 0x017DU:
|
||||
return 180U; /* U+017D = 0xB4: Z with caron */
|
||||
case 0x017EU:
|
||||
return 184U; /* U+017E = 0xB8: z with caron */
|
||||
case 0x20ACU:
|
||||
return 164U; /* U+20AC = 0xA4: Euro */
|
||||
default:
|
||||
return 256U;
|
||||
}
|
||||
}
|
||||
|
||||
int change_utf8_encoding(unsigned char *dest, unsigned char *src, int len, enum ccx_encoding_type out_enc)
|
||||
{
|
||||
unsigned char *orig = dest; // Keep for calculating length
|
||||
unsigned char *orig_src = src; // Keep for calculating length
|
||||
for (int i = 0; src < orig_src + len;)
|
||||
{
|
||||
unsigned char c = src[i];
|
||||
int c_len = 0;
|
||||
|
||||
if (c < 0x80)
|
||||
c_len = 1;
|
||||
else if ((c & 0x20) == 0)
|
||||
c_len = 2;
|
||||
else if ((c & 0x10) == 0)
|
||||
c_len = 3;
|
||||
else if ((c & 0x08) == 0)
|
||||
c_len = 4;
|
||||
else if ((c & 0x04) == 0)
|
||||
c_len = 5;
|
||||
|
||||
switch (out_enc)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
memcpy(dest, src, len);
|
||||
return len;
|
||||
case CCX_ENC_LATIN_1:
|
||||
if (c_len == 1)
|
||||
*dest++ = *src;
|
||||
else if (c_len == 2)
|
||||
{
|
||||
if ((src[1] & 0x40) == 0)
|
||||
{
|
||||
int cp = utf8_to_latin1_map((((unsigned int)(src[0] & 0x1F)) << 6) | ((unsigned int)(src[1] & 0x3F)));
|
||||
if (cp < 256)
|
||||
*dest++ = cp;
|
||||
else
|
||||
*dest++ = '?';
|
||||
}
|
||||
else
|
||||
*dest++ = '?';
|
||||
}
|
||||
else if (c_len == 3)
|
||||
{
|
||||
if ((src[1] & 0x40) == 0 && (src[2] & 0x40) == 0)
|
||||
{
|
||||
int cp = utf8_to_latin1_map((((unsigned int)(src[0] & 0x0F)) << 12) | (((unsigned int)(src[1] & 0x3F)) << 6) | ((unsigned int)(src[2] & 0x3F)));
|
||||
if (cp < 256)
|
||||
*dest++ = cp;
|
||||
else
|
||||
*dest++ = '?';
|
||||
}
|
||||
}
|
||||
else if (c_len == 4)
|
||||
{
|
||||
if ((src[1] & 0x40) == 0 &&
|
||||
(src[2] & 0x40) == 0 &&
|
||||
(src[3] & 0x40) == 0)
|
||||
{
|
||||
int cp = utf8_to_latin1_map((((unsigned int)(src[0] & 0x07)) << 18) | (((unsigned int)(src[1] & 0x3F)) << 12) | (((unsigned int)(src[2] & 0x3F)) << 6) | ((unsigned int)(src[3] & 0x3F)));
|
||||
if (cp < 256)
|
||||
*(dest++) = cp;
|
||||
else
|
||||
*dest++ = '?';
|
||||
}
|
||||
else
|
||||
*dest++ = '?';
|
||||
}
|
||||
else if (c_len == 5)
|
||||
{
|
||||
if ((src[1] & 0x40) == 0 &&
|
||||
(src[2] & 0x40) == 0 &&
|
||||
(src[3] & 0x40) == 0 &&
|
||||
(src[4] & 0x40) == 0)
|
||||
{
|
||||
int cp = utf8_to_latin1_map((((unsigned int)(src[0] & 0x03)) << 24U) | (((unsigned int)(src[1] & 0x3F)) << 18U) | (((unsigned int)(src[2] & 0x3F)) << 12U) | (((unsigned int)(src[3] & 0x3F)) << 6U) | ((unsigned int)(src[4] & 0x3FU)));
|
||||
if (cp < 256)
|
||||
*(dest++) = cp;
|
||||
else
|
||||
*dest++ = '?';
|
||||
}
|
||||
else
|
||||
*dest++ = '?';
|
||||
}
|
||||
else
|
||||
*dest++ = '?';
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
return CCX_ENOSUPP;
|
||||
case CCX_ENC_ASCII:
|
||||
if (c_len == 1)
|
||||
*dest++ = *src;
|
||||
else
|
||||
*dest++ = '?';
|
||||
default:
|
||||
return CCX_ENOSUPP;
|
||||
}
|
||||
src += c_len;
|
||||
}
|
||||
*dest = 0;
|
||||
return (dest - orig); // Return length
|
||||
}
|
||||
|
||||
int change_latin1_encoding(unsigned char *dest, unsigned char *src, int len, enum ccx_encoding_type out_enc)
|
||||
{
|
||||
return CCX_ENOSUPP;
|
||||
}
|
||||
|
||||
int change_unicode_encoding(unsigned char *dest, unsigned char *src, int len, enum ccx_encoding_type out_enc)
|
||||
{
|
||||
return CCX_ENOSUPP;
|
||||
}
|
||||
|
||||
int change_ascii_encoding(unsigned char *dest, unsigned char *src, int len, enum ccx_encoding_type out_enc)
|
||||
{
|
||||
unsigned char *orig = dest; // Keep for calculating length
|
||||
int bytes = 0;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
char c = src[i];
|
||||
switch (out_enc)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
bytes = get_char_in_utf_8(dest, c);
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
get_char_in_latin_1(dest, c);
|
||||
bytes = 1;
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
get_char_in_unicode(dest, c);
|
||||
bytes = 2;
|
||||
break;
|
||||
case CCX_ENC_ASCII:
|
||||
memcpy(dest, src, len);
|
||||
return len;
|
||||
default:
|
||||
return CCX_ENOSUPP;
|
||||
}
|
||||
dest += bytes;
|
||||
}
|
||||
*dest = 0;
|
||||
return (dest - orig); // Return length
|
||||
}
|
||||
|
||||
int get_str_basic(unsigned char *out_buffer, unsigned char *in_buffer, int trim_subs,
|
||||
enum ccx_encoding_type in_enc, enum ccx_encoding_type out_enc, int max_len)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_get_str_basic(out_buffer, in_buffer, trim_subs, in_enc, out_enc, max_len);
|
||||
#else
|
||||
int last_non_blank = -1;
|
||||
int first_non_blank = -1;
|
||||
int len = 0;
|
||||
find_limit_characters(in_buffer, &first_non_blank, &last_non_blank, max_len);
|
||||
if (!trim_subs)
|
||||
first_non_blank = 0;
|
||||
|
||||
if (first_non_blank == -1)
|
||||
{
|
||||
*out_buffer = 0;
|
||||
return 0;
|
||||
}
|
||||
// change encoding only when required
|
||||
switch (in_enc)
|
||||
{
|
||||
case CCX_ENC_UTF_8:
|
||||
len = change_utf8_encoding(out_buffer, in_buffer + first_non_blank, last_non_blank - first_non_blank + 1, out_enc);
|
||||
break;
|
||||
case CCX_ENC_LATIN_1:
|
||||
len = change_latin1_encoding(out_buffer, in_buffer + first_non_blank, last_non_blank - first_non_blank + 1, out_enc);
|
||||
break;
|
||||
case CCX_ENC_UNICODE:
|
||||
len = change_unicode_encoding(out_buffer, in_buffer + first_non_blank, last_non_blank - first_non_blank + 1, out_enc);
|
||||
break;
|
||||
case CCX_ENC_ASCII:
|
||||
len = change_ascii_encoding(out_buffer, in_buffer + first_non_blank, last_non_blank - first_non_blank + 1, out_enc);
|
||||
break;
|
||||
}
|
||||
if (len < 0)
|
||||
mprint("WARNING: Could not encode in specified format\n");
|
||||
else if (len == CCX_ENOSUPP)
|
||||
// we only support ASCII to other encoding std
|
||||
mprint("WARNING: Encoding is not yet supported\n");
|
||||
else
|
||||
return (unsigned)len; // Return length
|
||||
|
||||
return 0; // Return length
|
||||
#endif
|
||||
}
|
||||
|
||||
int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
|
||||
@@ -873,10 +658,8 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
|
||||
if (!cfg->services_enabled[i])
|
||||
{
|
||||
ctx->dtvcc_writers[i].fd = -1;
|
||||
#ifndef DISABLE_RUST
|
||||
ctx->dtvcc_writers[i].fhandle = NULL;
|
||||
ctx->dtvcc_writers[i].charset = NULL;
|
||||
#endif
|
||||
ctx->dtvcc_writers[i].filename = NULL;
|
||||
ctx->dtvcc_writers[i].cd = (iconv_t)-1;
|
||||
continue;
|
||||
@@ -885,10 +668,8 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
|
||||
if (cfg->cc_to_stdout)
|
||||
{
|
||||
ctx->dtvcc_writers[i].fd = STDOUT_FILENO;
|
||||
#ifndef DISABLE_RUST
|
||||
ctx->dtvcc_writers[i].fhandle = NULL;
|
||||
ctx->dtvcc_writers[i].charset = NULL;
|
||||
#endif
|
||||
ctx->dtvcc_writers[i].filename = NULL;
|
||||
ctx->dtvcc_writers[i].cd = (iconv_t)-1;
|
||||
}
|
||||
|
||||
@@ -24,12 +24,10 @@ if (ctx->buffer == NULL) { fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for
|
||||
typedef struct dtvcc_writer_ctx
|
||||
{
|
||||
int fd;
|
||||
#ifndef DISABLE_RUST
|
||||
// File handle used to work with files on windows
|
||||
void *fhandle;
|
||||
// Charset of the subtitle
|
||||
char *charset;
|
||||
#endif
|
||||
char *filename;
|
||||
iconv_t cd;
|
||||
} dtvcc_writer_ctx;
|
||||
|
||||
@@ -1,346 +1,2 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "utility.h"
|
||||
|
||||
#if defined(ENABLE_HARDSUBX) && defined(DISABLE_RUST)
|
||||
// TODO: Correct FFMpeg integration
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <leptonica/allheaders.h>
|
||||
#include "hardsubx.h"
|
||||
|
||||
char *get_ocr_text_simple(struct lib_hardsubx_ctx *ctx, PIX *image)
|
||||
{
|
||||
char *text_out = NULL;
|
||||
|
||||
TessBaseAPISetImage2(ctx->tess_handle, image);
|
||||
if (TessBaseAPIRecognize(ctx->tess_handle, NULL) != 0)
|
||||
{
|
||||
mprint("Error in Tesseract recognition, skipping frame\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((text_out = TessBaseAPIGetUTF8Text(ctx->tess_handle)) == NULL)
|
||||
{
|
||||
mprint("Error getting text, skipping frame\n");
|
||||
}
|
||||
return text_out;
|
||||
}
|
||||
|
||||
char *get_ocr_text_wordwise(struct lib_hardsubx_ctx *ctx, PIX *image)
|
||||
{
|
||||
char *text_out = NULL;
|
||||
|
||||
TessBaseAPISetImage2(ctx->tess_handle, image);
|
||||
if (TessBaseAPIRecognize(ctx->tess_handle, NULL) != 0)
|
||||
{
|
||||
mprint("Error in Tesseract recognition, skipping word\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TessResultIterator *it = TessBaseAPIGetIterator(ctx->tess_handle);
|
||||
TessPageIteratorLevel level = RIL_WORD;
|
||||
|
||||
int prev_ital = 0;
|
||||
|
||||
if (it != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
char *word;
|
||||
char *ts_word = TessResultIteratorGetUTF8Text(it, level);
|
||||
if (ts_word == NULL || strlen(ts_word) == 0)
|
||||
continue;
|
||||
word = strdup(ts_word);
|
||||
TessDeleteText(ts_word);
|
||||
if (text_out == NULL)
|
||||
{
|
||||
if (ctx->detect_italics)
|
||||
{
|
||||
int italic = 0;
|
||||
int dummy = 0;
|
||||
TessResultIteratorWordFontAttributes(it, &dummy, &italic, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy);
|
||||
if (italic == 1 && prev_ital == 0)
|
||||
{
|
||||
char *word_copy = strdup(word);
|
||||
word = realloc(word, strlen(word) + strlen("<i>") + 2);
|
||||
strcpy(word, "<i>");
|
||||
strcat(word, word_copy);
|
||||
free(word_copy);
|
||||
prev_ital = 1;
|
||||
}
|
||||
else if (italic == 0 && prev_ital == 1)
|
||||
{
|
||||
word = realloc(word, strlen(word) + strlen("</i>") + 2);
|
||||
strcat(word, "</i>");
|
||||
prev_ital = 0;
|
||||
}
|
||||
}
|
||||
text_out = strdup(word);
|
||||
text_out = realloc(text_out, strlen(text_out) + 2);
|
||||
strcat(text_out, " ");
|
||||
continue;
|
||||
}
|
||||
if (ctx->detect_italics)
|
||||
{
|
||||
int italic = 0;
|
||||
int dummy = 0;
|
||||
TessResultIteratorWordFontAttributes(it, &dummy, &italic, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy);
|
||||
if (italic == 1 && prev_ital == 0)
|
||||
{
|
||||
char *word_copy = strdup(word);
|
||||
word = realloc(word, strlen(word) + strlen("<i>") + 2);
|
||||
strcpy(word, "<i>");
|
||||
strcat(word, word_copy);
|
||||
free(word_copy);
|
||||
prev_ital = 1;
|
||||
}
|
||||
else if (italic == 0 && prev_ital == 1)
|
||||
{
|
||||
word = realloc(word, strlen(word) + strlen("</i>") + 2);
|
||||
strcat(word, "</i>");
|
||||
prev_ital = 0;
|
||||
}
|
||||
}
|
||||
text_out = realloc(text_out, strlen(text_out) + strlen(word) + 2);
|
||||
strcat(text_out, word);
|
||||
strcat(text_out, " ");
|
||||
free(word);
|
||||
} while (TessPageIteratorNext((TessPageIterator *)it, level));
|
||||
}
|
||||
|
||||
if (ctx->detect_italics && prev_ital == 1)
|
||||
{
|
||||
text_out = realloc(text_out, strlen(text_out) + strlen("</i>") + 2);
|
||||
strcat(text_out, "</i>");
|
||||
}
|
||||
|
||||
TessResultIteratorDelete(it);
|
||||
|
||||
return text_out;
|
||||
}
|
||||
|
||||
char *get_ocr_text_letterwise(struct lib_hardsubx_ctx *ctx, PIX *image)
|
||||
{
|
||||
char *text_out = NULL;
|
||||
|
||||
TessBaseAPISetImage2(ctx->tess_handle, image);
|
||||
if (TessBaseAPIRecognize(ctx->tess_handle, NULL) != 0)
|
||||
{
|
||||
mprint("Error in Tesseract recognition, skipping symbol\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TessResultIterator *it = TessBaseAPIGetIterator(ctx->tess_handle);
|
||||
TessPageIteratorLevel level = RIL_SYMBOL;
|
||||
|
||||
if (it != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
char *letter;
|
||||
char *ts_letter = TessResultIteratorGetUTF8Text(it, level);
|
||||
if (ts_letter == NULL || strlen(ts_letter) == 0)
|
||||
continue;
|
||||
letter = strdup(ts_letter);
|
||||
TessDeleteText(ts_letter);
|
||||
if (text_out == NULL)
|
||||
{
|
||||
text_out = strdup(letter);
|
||||
continue;
|
||||
}
|
||||
text_out = realloc(text_out, strlen(text_out) + strlen(letter) + 1);
|
||||
strcat(text_out, letter);
|
||||
free(letter);
|
||||
} while (TessPageIteratorNext((TessPageIterator *)it, level));
|
||||
}
|
||||
|
||||
TessResultIteratorDelete(it);
|
||||
|
||||
return text_out;
|
||||
}
|
||||
|
||||
char *get_ocr_text_simple_threshold(struct lib_hardsubx_ctx *ctx, PIX *image, float threshold)
|
||||
{
|
||||
char *text_out = NULL;
|
||||
|
||||
TessBaseAPISetImage2(ctx->tess_handle, image);
|
||||
if (TessBaseAPIRecognize(ctx->tess_handle, NULL) != 0)
|
||||
{
|
||||
mprint("Error in Tesseract recognition, skipping frame\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if ((text_out = TessBaseAPIGetUTF8Text(ctx->tess_handle)) == NULL)
|
||||
{
|
||||
mprint("Error getting text, skipping frame\n");
|
||||
}
|
||||
|
||||
int conf = TessBaseAPIMeanTextConf(ctx->tess_handle);
|
||||
|
||||
if (conf < threshold)
|
||||
return NULL;
|
||||
|
||||
ctx->cur_conf = (float)conf;
|
||||
|
||||
return text_out;
|
||||
}
|
||||
|
||||
char *get_ocr_text_wordwise_threshold(struct lib_hardsubx_ctx *ctx, PIX *image, float threshold)
|
||||
{
|
||||
char *text_out = NULL;
|
||||
|
||||
TessBaseAPISetImage2(ctx->tess_handle, image);
|
||||
if (TessBaseAPIRecognize(ctx->tess_handle, NULL) != 0)
|
||||
{
|
||||
mprint("Error in Tesseract recognition, skipping word\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TessResultIterator *it = TessBaseAPIGetIterator(ctx->tess_handle);
|
||||
TessPageIteratorLevel level = RIL_WORD;
|
||||
|
||||
int prev_ital = 0;
|
||||
float total_conf = 0.0;
|
||||
int num_words = 0;
|
||||
|
||||
if (it != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
char *word;
|
||||
char *ts_word = TessResultIteratorGetUTF8Text(it, level);
|
||||
if (ts_word == NULL || strlen(ts_word) == 0)
|
||||
continue;
|
||||
word = strdup(ts_word);
|
||||
TessDeleteText(ts_word);
|
||||
float conf = TessResultIteratorConfidence(it, level);
|
||||
if (conf < threshold)
|
||||
continue;
|
||||
total_conf += conf;
|
||||
num_words++;
|
||||
if (text_out == NULL)
|
||||
{
|
||||
if (ctx->detect_italics)
|
||||
{
|
||||
int italic = 0;
|
||||
int dummy = 0;
|
||||
TessResultIteratorWordFontAttributes(it, &dummy, &italic, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy);
|
||||
if (italic == 1 && prev_ital == 0)
|
||||
{
|
||||
char *word_copy = strdup(word);
|
||||
word = realloc(word, strlen(word) + strlen("<i>") + 2);
|
||||
strcpy(word, "<i>");
|
||||
strcat(word, word_copy);
|
||||
free(word_copy);
|
||||
prev_ital = 1;
|
||||
}
|
||||
else if (italic == 0 && prev_ital == 1)
|
||||
{
|
||||
word = realloc(word, strlen(word) + strlen("</i>") + 2);
|
||||
strcat(word, "</i>");
|
||||
prev_ital = 0;
|
||||
}
|
||||
}
|
||||
text_out = strdup(word);
|
||||
text_out = realloc(text_out, strlen(text_out) + 2);
|
||||
strcat(text_out, " ");
|
||||
continue;
|
||||
}
|
||||
if (ctx->detect_italics)
|
||||
{
|
||||
int italic = 0;
|
||||
int dummy = 0;
|
||||
TessResultIteratorWordFontAttributes(it, &dummy, &italic, &dummy, &dummy, &dummy, &dummy, &dummy, &dummy);
|
||||
if (italic == 1 && prev_ital == 0)
|
||||
{
|
||||
char *word_copy = strdup(word);
|
||||
word = realloc(word, strlen(word) + strlen("<i>") + 2);
|
||||
strcpy(word, "<i>");
|
||||
strcat(word, word_copy);
|
||||
free(word_copy);
|
||||
prev_ital = 1;
|
||||
}
|
||||
else if (italic == 0 && prev_ital == 1)
|
||||
{
|
||||
word = realloc(word, strlen(word) + strlen("</i>") + 2);
|
||||
strcat(word, "</i>");
|
||||
prev_ital = 0;
|
||||
}
|
||||
}
|
||||
text_out = realloc(text_out, strlen(text_out) + strlen(word) + 2);
|
||||
strcat(text_out, word);
|
||||
strcat(text_out, " ");
|
||||
free(word);
|
||||
} while (TessPageIteratorNext((TessPageIterator *)it, level));
|
||||
}
|
||||
|
||||
if (ctx->detect_italics && prev_ital == 1)
|
||||
{
|
||||
text_out = realloc(text_out, strlen(text_out) + strlen("</i>") + 2);
|
||||
strcat(text_out, "</i>");
|
||||
}
|
||||
|
||||
if (num_words > 0)
|
||||
total_conf = total_conf / num_words;
|
||||
ctx->cur_conf = total_conf;
|
||||
|
||||
TessResultIteratorDelete(it);
|
||||
|
||||
return text_out;
|
||||
}
|
||||
|
||||
char *get_ocr_text_letterwise_threshold(struct lib_hardsubx_ctx *ctx, PIX *image, float threshold)
|
||||
{
|
||||
char *text_out = NULL;
|
||||
|
||||
TessBaseAPISetImage2(ctx->tess_handle, image);
|
||||
if (TessBaseAPIRecognize(ctx->tess_handle, NULL) != 0)
|
||||
{
|
||||
mprint("Error in Tesseract recognition, skipping symbol\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
TessResultIterator *it = TessBaseAPIGetIterator(ctx->tess_handle);
|
||||
TessPageIteratorLevel level = RIL_SYMBOL;
|
||||
|
||||
float total_conf = 0.0;
|
||||
int num_words = 0;
|
||||
|
||||
if (it != 0)
|
||||
{
|
||||
do
|
||||
{
|
||||
char *letter;
|
||||
char *ts_letter = TessResultIteratorGetUTF8Text(it, level);
|
||||
if (ts_letter == NULL || strlen(ts_letter) == 0)
|
||||
continue;
|
||||
letter = strdup(ts_letter);
|
||||
TessDeleteText(ts_letter);
|
||||
float conf = TessResultIteratorConfidence(it, level);
|
||||
if (conf < threshold)
|
||||
continue;
|
||||
total_conf += conf;
|
||||
num_words++;
|
||||
if (text_out == NULL)
|
||||
{
|
||||
text_out = strdup(letter);
|
||||
continue;
|
||||
}
|
||||
text_out = realloc(text_out, strlen(text_out) + strlen(letter) + 1);
|
||||
strcat(text_out, letter);
|
||||
free(letter);
|
||||
} while (TessPageIteratorNext((TessPageIterator *)it, level));
|
||||
}
|
||||
|
||||
if (num_words > 0)
|
||||
total_conf = total_conf / num_words;
|
||||
ctx->cur_conf = total_conf;
|
||||
|
||||
TessResultIteratorDelete(it);
|
||||
|
||||
return text_out;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -14,212 +14,6 @@
|
||||
#include "ffmpeg_intgr.h"
|
||||
#endif
|
||||
|
||||
#ifdef DISABLE_RUST
|
||||
char *_process_frame_white_basic(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int index)
|
||||
{
|
||||
// printf("frame : %04d\n", index);
|
||||
PIX *im;
|
||||
PIX *gray_im;
|
||||
PIX *sobel_edge_im;
|
||||
PIX *dilate_gray_im;
|
||||
PIX *edge_im;
|
||||
PIX *lum_im;
|
||||
PIX *feat_im;
|
||||
char *subtitle_text;
|
||||
|
||||
subtitle_text = NULL;
|
||||
im = pixCreate(width, height, 32);
|
||||
lum_im = pixCreate(width, height, 32);
|
||||
feat_im = pixCreate(width, height, 32);
|
||||
|
||||
for (int i = (3 * height) / 4; i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
int p = j * 3 + i * frame->linesize[0];
|
||||
int r = frame->data[0][p];
|
||||
int g = frame->data[0][p + 1];
|
||||
int b = frame->data[0][p + 2];
|
||||
pixSetRGBPixel(im, j, i, r, g, b);
|
||||
float L, A, B;
|
||||
rgb_to_lab((float)r, (float)g, (float)b, &L, &A, &B);
|
||||
if (L > ctx->lum_thresh)
|
||||
pixSetRGBPixel(lum_im, j, i, 255, 255, 255);
|
||||
else
|
||||
pixSetRGBPixel(lum_im, j, i, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the edge image
|
||||
gray_im = pixConvertRGBToGray(im, 0.0, 0.0, 0.0);
|
||||
sobel_edge_im = pixSobelEdgeFilter(gray_im, L_VERTICAL_EDGES);
|
||||
dilate_gray_im = pixDilateGray(sobel_edge_im, 21, 11);
|
||||
edge_im = pixThresholdToBinary(dilate_gray_im, 50);
|
||||
|
||||
for (int i = 3 * (height / 4); i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
unsigned int p1, p2, p3;
|
||||
pixGetPixel(edge_im, j, i, &p1);
|
||||
// pixGetPixel(pixd,j,i,&p2);
|
||||
pixGetPixel(lum_im, j, i, &p3);
|
||||
if (p1 == 0 && p3 > 0)
|
||||
pixSetRGBPixel(feat_im, j, i, 255, 255, 255);
|
||||
else
|
||||
pixSetRGBPixel(feat_im, j, i, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->detect_italics)
|
||||
{
|
||||
ctx->ocr_mode = HARDSUBX_OCRMODE_WORD;
|
||||
}
|
||||
|
||||
// TESSERACT OCR FOR THE FRAME HERE
|
||||
switch (ctx->ocr_mode)
|
||||
{
|
||||
case HARDSUBX_OCRMODE_WORD:
|
||||
if (ctx->conf_thresh > 0)
|
||||
subtitle_text = get_ocr_text_wordwise_threshold(ctx, feat_im, ctx->conf_thresh);
|
||||
else
|
||||
subtitle_text = get_ocr_text_wordwise(ctx, feat_im);
|
||||
break;
|
||||
case HARDSUBX_OCRMODE_LETTER:
|
||||
if (ctx->conf_thresh > 0)
|
||||
subtitle_text = get_ocr_text_letterwise_threshold(ctx, feat_im, ctx->conf_thresh);
|
||||
else
|
||||
subtitle_text = get_ocr_text_letterwise(ctx, feat_im);
|
||||
break;
|
||||
case HARDSUBX_OCRMODE_FRAME:
|
||||
if (ctx->conf_thresh > 0)
|
||||
subtitle_text = get_ocr_text_simple_threshold(ctx, feat_im, ctx->conf_thresh);
|
||||
else
|
||||
subtitle_text = get_ocr_text_simple(ctx, feat_im);
|
||||
break;
|
||||
default:
|
||||
fatal(EXIT_MALFORMED_PARAMETER, "Invalid OCR Mode");
|
||||
}
|
||||
|
||||
pixDestroy(&im);
|
||||
pixDestroy(&gray_im);
|
||||
pixDestroy(&sobel_edge_im);
|
||||
pixDestroy(&dilate_gray_im);
|
||||
pixDestroy(&edge_im);
|
||||
pixDestroy(&lum_im);
|
||||
pixDestroy(&feat_im);
|
||||
|
||||
return subtitle_text;
|
||||
}
|
||||
|
||||
char *_process_frame_color_basic(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int index)
|
||||
{
|
||||
PIX *im;
|
||||
PIX *hue_im;
|
||||
PIX *gray_im;
|
||||
PIX *sobel_edge_im;
|
||||
PIX *dilate_gray_im;
|
||||
PIX *edge_im;
|
||||
PIX *gray_im_2;
|
||||
PIX *edge_im_2;
|
||||
PIX *pixd;
|
||||
PIX *feat_im;
|
||||
char *subtitle_text = NULL;
|
||||
|
||||
im = pixCreate(width, height, 32);
|
||||
hue_im = pixCreate(width, height, 32);
|
||||
subtitle_text = NULL;
|
||||
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
int p = j * 3 + i * frame->linesize[0];
|
||||
int r = frame->data[0][p];
|
||||
int g = frame->data[0][p + 1];
|
||||
int b = frame->data[0][p + 2];
|
||||
pixSetRGBPixel(im, j, i, r, g, b);
|
||||
float H, S, V;
|
||||
rgb_to_hsv((float)r, (float)g, (float)b, &H, &S, &V);
|
||||
if (fabsf(H - ctx->hue) < 20)
|
||||
{
|
||||
pixSetRGBPixel(hue_im, j, i, r, g, b);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gray_im = pixConvertRGBToGray(im, 0.0, 0.0, 0.0);
|
||||
sobel_edge_im = pixSobelEdgeFilter(gray_im, L_VERTICAL_EDGES);
|
||||
dilate_gray_im = pixDilateGray(sobel_edge_im, 21, 1);
|
||||
edge_im = pixThresholdToBinary(dilate_gray_im, 50);
|
||||
|
||||
gray_im_2 = pixConvertRGBToGray(hue_im, 0.0, 0.0, 0.0);
|
||||
edge_im_2 = pixDilateGray(gray_im_2, 5, 5);
|
||||
|
||||
pixSauvolaBinarize(gray_im_2, 15, 0.3, 1, NULL, NULL, NULL, &pixd);
|
||||
|
||||
feat_im = pixCreate(width, height, 32);
|
||||
for (int i = 3 * (height / 4); i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
unsigned int p1, p2, p3, p4;
|
||||
pixGetPixel(edge_im, j, i, &p1);
|
||||
pixGetPixel(pixd, j, i, &p2);
|
||||
// pixGetPixel(hue_im,j,i,&p3);
|
||||
pixGetPixel(edge_im_2, j, i, &p4);
|
||||
if (p1 == 0 && p2 == 0 && p4 > 0) // if(p4>0&&p1==0)//if(p2==0&&p1==0&&p3>0)
|
||||
{
|
||||
pixSetRGBPixel(feat_im, j, i, 255, 255, 255);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (ctx->detect_italics)
|
||||
{
|
||||
ctx->ocr_mode = HARDSUBX_OCRMODE_WORD;
|
||||
}
|
||||
|
||||
// TESSERACT OCR FOR THE FRAME HERE
|
||||
switch (ctx->ocr_mode)
|
||||
{
|
||||
case HARDSUBX_OCRMODE_WORD:
|
||||
if (ctx->conf_thresh > 0)
|
||||
subtitle_text = get_ocr_text_wordwise_threshold(ctx, feat_im, ctx->conf_thresh);
|
||||
else
|
||||
subtitle_text = get_ocr_text_wordwise(ctx, feat_im);
|
||||
break;
|
||||
case HARDSUBX_OCRMODE_LETTER:
|
||||
if (ctx->conf_thresh > 0)
|
||||
subtitle_text = get_ocr_text_letterwise_threshold(ctx, feat_im, ctx->conf_thresh);
|
||||
else
|
||||
subtitle_text = get_ocr_text_letterwise(ctx, feat_im);
|
||||
break;
|
||||
case HARDSUBX_OCRMODE_FRAME:
|
||||
if (ctx->conf_thresh > 0)
|
||||
subtitle_text = get_ocr_text_simple_threshold(ctx, feat_im, ctx->conf_thresh);
|
||||
else
|
||||
subtitle_text = get_ocr_text_simple(ctx, feat_im);
|
||||
break;
|
||||
default:
|
||||
fatal(EXIT_MALFORMED_PARAMETER, "Invalid OCR Mode");
|
||||
}
|
||||
|
||||
pixDestroy(&im);
|
||||
pixDestroy(&hue_im);
|
||||
pixDestroy(&gray_im);
|
||||
pixDestroy(&sobel_edge_im);
|
||||
pixDestroy(&dilate_gray_im);
|
||||
pixDestroy(&edge_im);
|
||||
pixDestroy(&gray_im_2);
|
||||
pixDestroy(&edge_im_2);
|
||||
pixDestroy(&pixd);
|
||||
pixDestroy(&feat_im);
|
||||
|
||||
return subtitle_text;
|
||||
}
|
||||
#endif
|
||||
|
||||
void _display_frame(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int timestamp)
|
||||
{
|
||||
// Debug: Display the frame after processing
|
||||
@@ -299,82 +93,6 @@ void _display_frame(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int
|
||||
pixDestroy(&feat_im);
|
||||
}
|
||||
|
||||
#ifdef DISABLE_RUST
|
||||
char *_process_frame_tickertext(struct lib_hardsubx_ctx *ctx, AVFrame *frame, int width, int height, int index)
|
||||
{
|
||||
PIX *im;
|
||||
PIX *gray_im;
|
||||
PIX *sobel_edge_im;
|
||||
PIX *dilate_gray_im;
|
||||
PIX *edge_im;
|
||||
PIX *lum_im;
|
||||
PIX *feat_im;
|
||||
char *subtitle_text;
|
||||
|
||||
subtitle_text = NULL;
|
||||
im = pixCreate(width, height, 32);
|
||||
lum_im = pixCreate(width, height, 32);
|
||||
feat_im = pixCreate(width, height, 32);
|
||||
|
||||
for (int i = (92 * height) / 100; i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
int p = j * 3 + i * frame->linesize[0];
|
||||
int r = frame->data[0][p];
|
||||
int g = frame->data[0][p + 1];
|
||||
int b = frame->data[0][p + 2];
|
||||
pixSetRGBPixel(im, j, i, r, g, b);
|
||||
float L, A, B;
|
||||
rgb_to_lab((float)r, (float)g, (float)b, &L, &A, &B);
|
||||
if (L > ctx->lum_thresh)
|
||||
pixSetRGBPixel(lum_im, j, i, 255, 255, 255);
|
||||
else
|
||||
pixSetRGBPixel(lum_im, j, i, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Handle the edge image
|
||||
gray_im = pixConvertRGBToGray(im, 0.0, 0.0, 0.0);
|
||||
sobel_edge_im = pixSobelEdgeFilter(gray_im, L_VERTICAL_EDGES);
|
||||
dilate_gray_im = pixDilateGray(sobel_edge_im, 21, 11);
|
||||
edge_im = pixThresholdToBinary(dilate_gray_im, 50);
|
||||
|
||||
for (int i = 92 * (height / 100); i < height; i++)
|
||||
{
|
||||
for (int j = 0; j < width; j++)
|
||||
{
|
||||
unsigned int p1, p2, p3;
|
||||
pixGetPixel(edge_im, j, i, &p1);
|
||||
// pixGetPixel(pixd,j,i,&p2);
|
||||
pixGetPixel(lum_im, j, i, &p3);
|
||||
if (p1 == 0 && p3 > 0)
|
||||
pixSetRGBPixel(feat_im, j, i, 255, 255, 255);
|
||||
else
|
||||
pixSetRGBPixel(feat_im, j, i, 0, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Tesseract OCR for the ticker text here
|
||||
subtitle_text = get_ocr_text_simple(ctx, lum_im);
|
||||
char write_path[100];
|
||||
sprintf(write_path, "./lum_im%04d.jpg", index);
|
||||
pixWrite(write_path, lum_im, IFF_JFIF_JPEG);
|
||||
sprintf(write_path, "./im%04d.jpg", index);
|
||||
pixWrite(write_path, im, IFF_JFIF_JPEG);
|
||||
|
||||
pixDestroy(&im);
|
||||
pixDestroy(&gray_im);
|
||||
pixDestroy(&sobel_edge_im);
|
||||
pixDestroy(&dilate_gray_im);
|
||||
pixDestroy(&edge_im);
|
||||
pixDestroy(&lum_im);
|
||||
pixDestroy(&feat_im);
|
||||
|
||||
return subtitle_text;
|
||||
}
|
||||
#endif
|
||||
|
||||
int hardsubx_process_frames_tickertext(struct lib_hardsubx_ctx *ctx, struct encoder_ctx *enc_ctx)
|
||||
{
|
||||
// Search for ticker text at the bottom of the screen, such as in Russia TV1 or stock prices
|
||||
|
||||
@@ -1,116 +1,2 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "utility.h"
|
||||
|
||||
#if defined(ENABLE_HARDSUBX) && defined(DISABLE_RUST)
|
||||
// TODO: Correct FFMpeg integration
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <leptonica/allheaders.h>
|
||||
#include "hardsubx.h"
|
||||
|
||||
#define BLACK 20.0
|
||||
#define YELLOW 70.0
|
||||
|
||||
#define min_f(a, b, c) (fminf(a, fminf(b, c)))
|
||||
#define max_f(a, b, c) (fmaxf(a, fmaxf(b, c)))
|
||||
|
||||
void rgb_to_hsv(float R, float G, float B, float *H, float *S, float *V)
|
||||
{
|
||||
// Conversion into HSV color space to get Hue
|
||||
float r = R / 255.0f;
|
||||
float g = G / 255.0f;
|
||||
float b = B / 255.0f;
|
||||
|
||||
float h, s, v; // h:0-360.0, s:0.0-1.0, v:0.0-1.0
|
||||
|
||||
float max = max_f(r, g, b);
|
||||
float min = min_f(r, g, b);
|
||||
|
||||
v = max;
|
||||
|
||||
if (max == 0.0f)
|
||||
{
|
||||
s = 0;
|
||||
h = 0;
|
||||
}
|
||||
else if (max - min == 0.0f)
|
||||
{
|
||||
s = 0;
|
||||
h = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = (max - min) / max;
|
||||
|
||||
if (max == r)
|
||||
{
|
||||
h = 60 * ((g - b) / (max - min)) + 0;
|
||||
}
|
||||
else if (max == g)
|
||||
{
|
||||
h = 60 * ((b - r) / (max - min)) + 120;
|
||||
}
|
||||
else
|
||||
{
|
||||
h = 60 * ((r - g) / (max - min)) + 240;
|
||||
}
|
||||
}
|
||||
|
||||
if (h < 0)
|
||||
h += 360.0f;
|
||||
|
||||
*H = (unsigned char)(h); // dst_h : 0-360
|
||||
*S = (unsigned char)(s * 255); // dst_s : 0-255
|
||||
*V = (unsigned char)(v * 255); // dst_v : 0-255
|
||||
}
|
||||
|
||||
void rgb_to_lab(float R, float G, float B, float *L, float *a, float *b)
|
||||
{
|
||||
// Conversion to the CIE-LAB color space to get the Luminance
|
||||
float X, Y, Z, fX, fY, fZ;
|
||||
|
||||
X = 0.412453 * R + 0.357580 * G + 0.180423 * B;
|
||||
Y = 0.212671 * R + 0.715160 * G + 0.072169 * B;
|
||||
Z = 0.019334 * R + 0.119193 * G + 0.950227 * B;
|
||||
|
||||
X /= (255 * 0.950456);
|
||||
Y /= 255;
|
||||
Z /= (255 * 1.088754);
|
||||
|
||||
if (Y > 0.008856)
|
||||
{
|
||||
fY = pow(Y, 1.0 / 3.0);
|
||||
*L = 116.0 * fY - 16.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
fY = 7.787 * Y + 16.0 / 116.0;
|
||||
*L = 903.3 * Y;
|
||||
}
|
||||
|
||||
if (X > 0.008856)
|
||||
fX = pow(X, 1.0 / 3.0);
|
||||
else
|
||||
fX = 7.787 * X + 16.0 / 116.0;
|
||||
|
||||
if (Z > 0.008856)
|
||||
fZ = pow(Z, 1.0 / 3.0);
|
||||
else
|
||||
fZ = 7.787 * Z + 16.0 / 116.0;
|
||||
|
||||
*a = 500.0 * (fX - fY);
|
||||
*b = 200.0 * (fY - fZ);
|
||||
|
||||
if (*L < BLACK)
|
||||
{
|
||||
*a *= exp((*L - BLACK) / (BLACK / 4));
|
||||
*b *= exp((*L - BLACK) / (BLACK / 4));
|
||||
*L = BLACK;
|
||||
}
|
||||
if (*b > YELLOW)
|
||||
*b = YELLOW;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1,132 +1,2 @@
|
||||
#include "lib_ccx.h"
|
||||
#include "utility.h"
|
||||
|
||||
#if defined(ENABLE_HARDSUBX) && defined(DISABLE_RUST)
|
||||
// TODO: Correct FFMpeg integration
|
||||
#include <libavcodec/avcodec.h>
|
||||
#include <libavformat/avformat.h>
|
||||
#include <libavutil/imgutils.h>
|
||||
#include <libswscale/swscale.h>
|
||||
#include <leptonica/allheaders.h>
|
||||
#include "hardsubx.h"
|
||||
|
||||
int64_t convert_pts_to_ms(int64_t pts, AVRational time_base)
|
||||
{
|
||||
return av_rescale_q(pts, time_base, AV_TIME_BASE_Q) / 1000;
|
||||
}
|
||||
|
||||
int64_t convert_pts_to_ns(int64_t pts, AVRational time_base)
|
||||
{
|
||||
return av_rescale_q(pts, time_base, AV_TIME_BASE_Q);
|
||||
}
|
||||
|
||||
int64_t convert_pts_to_s(int64_t pts, AVRational time_base)
|
||||
{
|
||||
return av_rescale_q(pts, time_base, AV_TIME_BASE_Q) / 1000000;
|
||||
}
|
||||
|
||||
int edit_distance(char *word1, char *word2, int len1, int len2)
|
||||
{
|
||||
// len2 <= len1
|
||||
if (len2 > len1)
|
||||
{
|
||||
int tmp = len1;
|
||||
len1 = len2;
|
||||
len2 = tmp;
|
||||
|
||||
char *tmp_ptr = word1;
|
||||
word1 = word2;
|
||||
word2 = tmp_ptr;
|
||||
}
|
||||
|
||||
int **matrix = (int **)malloc(2 * sizeof(int *));
|
||||
for (int i = 0; i < 2; i++)
|
||||
matrix[i] = (int *)malloc((len2 + 1) * sizeof(int));
|
||||
|
||||
int i, delete, insert, substitute, minimum;
|
||||
for (i = 0; i <= len2; i++)
|
||||
matrix[0][i] = i;
|
||||
for (i = 1; i <= len1; i++)
|
||||
{
|
||||
int j;
|
||||
for (j = 0; j <= len2; j++)
|
||||
matrix[i % 2][j] = 0;
|
||||
matrix[i % 2][0] = i;
|
||||
|
||||
char c1;
|
||||
c1 = word1[i - 1];
|
||||
for (j = 1; j <= len2; j++)
|
||||
{
|
||||
char c2;
|
||||
c2 = word2[j - 1];
|
||||
if (c1 == c2)
|
||||
{
|
||||
matrix[i % 2][j] = matrix[(i - 1) % 2][j - 1];
|
||||
}
|
||||
else
|
||||
{
|
||||
delete = matrix[(i - 1) % 2][j] + 1;
|
||||
insert = matrix[i % 2][j - 1] + 1;
|
||||
substitute = matrix[(i - 1) % 2][j - 1] + 1;
|
||||
minimum = delete;
|
||||
if (insert < minimum)
|
||||
{
|
||||
minimum = insert;
|
||||
}
|
||||
if (substitute < minimum)
|
||||
{
|
||||
minimum = substitute;
|
||||
}
|
||||
matrix[i % 2][j] = minimum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int ret = matrix[len1 % 2][len2];
|
||||
for (int i = 0; i < 2; i++)
|
||||
free(matrix[i]);
|
||||
free(matrix);
|
||||
return ret;
|
||||
}
|
||||
|
||||
int is_valid_trailing_char(char c)
|
||||
{
|
||||
char *prune_text = ",~`'-_+=‘:;” \n";
|
||||
printf("%c ", c);
|
||||
int ret = 1;
|
||||
size_t len = strlen(prune_text);
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
if (prune_text[i] == c)
|
||||
{
|
||||
ret = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
printf("%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
char *prune_string(char *s)
|
||||
{
|
||||
size_t size;
|
||||
char *end;
|
||||
|
||||
size = strlen(s);
|
||||
|
||||
if (!size)
|
||||
return s;
|
||||
|
||||
end = s + size - 1;
|
||||
while (end >= s && is_valid_trailing_char(*end) == -1)
|
||||
end--;
|
||||
*(end + 1) = '\0';
|
||||
|
||||
while (*s && is_valid_trailing_char(*end) == -1)
|
||||
s++;
|
||||
printf("%s\n", s);
|
||||
return s;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -153,17 +153,13 @@ struct lib_ccx_ctx
|
||||
struct lib_ccx_ctx *init_libraries(struct ccx_s_options *opt);
|
||||
void dinit_libraries( struct lib_ccx_ctx **ctx);
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
extern void ccxr_init_basic_logger();
|
||||
#endif
|
||||
|
||||
//ccextractor.c
|
||||
void print_end_msg(void);
|
||||
|
||||
//params.c
|
||||
#ifndef DISABLE_RUST
|
||||
extern int ccxr_parse_parameters(int argc, char *argv[]);
|
||||
#endif
|
||||
int parse_parameters (struct ccx_s_options *opt, int argc, char *argv[]);
|
||||
void print_usage (void);
|
||||
int atoi_hex (char *s);
|
||||
|
||||
2209
src/lib_ccx/params.c
2209
src/lib_ccx/params.c
File diff suppressed because it is too large
Load Diff
@@ -9,7 +9,6 @@
|
||||
int temp_debug = 0; // This is a convenience variable used to enable/disable debug on variable conditions. Find references to understand.
|
||||
volatile sig_atomic_t change_filename_requested = 0;
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
extern int ccxr_verify_crc32(uint8_t *buf, int len);
|
||||
extern int ccxr_levenshtein_dist(const uint64_t *s1, const uint64_t *s2, unsigned s1len, unsigned s2len);
|
||||
extern int ccxr_levenshtein_dist_char(const char *s1, const char *s2, unsigned s1len, unsigned s2len);
|
||||
@@ -17,7 +16,6 @@ extern void ccxr_timestamp_to_srttime(uint64_t timestamp, char *buffer);
|
||||
extern void ccxr_timestamp_to_vtttime(uint64_t timestamp, char *buffer);
|
||||
extern void ccxr_millis_to_date(uint64_t timestamp, char *buffer, enum ccx_output_date_format date_format, char millis_separator);
|
||||
extern int ccxr_stringztoms(const char *s, struct ccx_boundary_time *bt);
|
||||
#endif
|
||||
|
||||
static uint32_t crc32_table[] = {
|
||||
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
|
||||
@@ -87,182 +85,37 @@ static uint32_t crc32_table[] = {
|
||||
|
||||
int verify_crc32(uint8_t *buf, int len)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_verify_crc32(buf, len);
|
||||
#endif
|
||||
|
||||
int i = 0;
|
||||
int32_t crc = -1;
|
||||
for (i = 0; i < len; i++)
|
||||
crc = (crc << 8) ^ crc32_table[((crc >> 24) ^ (buf[i] & 0xff)) & 0xff];
|
||||
return crc ? CCX_FALSE : CCX_TRUE;
|
||||
}
|
||||
|
||||
int stringztoms(const char *s, struct ccx_boundary_time *bt)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_stringztoms(s, bt);
|
||||
#endif
|
||||
|
||||
unsigned ss = 0, mm = 0, hh = 0;
|
||||
int value = -1;
|
||||
int colons = 0;
|
||||
const char *c = s;
|
||||
while (*c)
|
||||
{
|
||||
if (*c == ':')
|
||||
{
|
||||
if (value == -1) // : at the start, or ::, etc
|
||||
return -1;
|
||||
colons++;
|
||||
if (colons > 2) // Max 2, for HH:MM:SS
|
||||
return -1;
|
||||
hh = mm;
|
||||
mm = ss;
|
||||
ss = value;
|
||||
value = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!isdigit(*c)) // Only : or digits, so error
|
||||
return -1;
|
||||
if (value == -1)
|
||||
value = *c - '0';
|
||||
else
|
||||
value = value * 10 + *c - '0';
|
||||
}
|
||||
c++;
|
||||
}
|
||||
hh = mm;
|
||||
mm = ss;
|
||||
ss = value;
|
||||
if (mm > 59 || ss > 59)
|
||||
return -1;
|
||||
bt->set = 1;
|
||||
bt->hh = hh;
|
||||
bt->mm = mm;
|
||||
bt->ss = ss;
|
||||
LLONG secs = (hh * 3600 + mm * 60 + ss);
|
||||
bt->time_in_ms = secs * 1000;
|
||||
return 0;
|
||||
}
|
||||
void timestamp_to_srttime(uint64_t timestamp, char *buffer)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_timestamp_to_srttime(timestamp, buffer);
|
||||
#endif
|
||||
|
||||
uint64_t p = timestamp;
|
||||
uint8_t h = (uint8_t)(p / 3600000);
|
||||
uint8_t m = (uint8_t)(p / 60000 - 60 * h);
|
||||
uint8_t s = (uint8_t)(p / 1000 - 3600 * h - 60 * m);
|
||||
uint16_t u = (uint16_t)(p - 3600000 * h - 60000 * m - 1000 * s);
|
||||
sprintf(buffer, "%02" PRIu8 ":%02" PRIu8 ":%02" PRIu8 ",%03" PRIu16, h, m, s, u);
|
||||
}
|
||||
void timestamp_to_vtttime(uint64_t timestamp, char *buffer)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_timestamp_to_vtttime(timestamp, buffer);
|
||||
#endif
|
||||
|
||||
uint64_t p = timestamp;
|
||||
uint8_t h = (uint8_t)(p / 3600000);
|
||||
uint8_t m = (uint8_t)(p / 60000 - 60 * h);
|
||||
uint8_t s = (uint8_t)(p / 1000 - 3600 * h - 60 * m);
|
||||
uint16_t u = (uint16_t)(p - 3600000 * h - 60000 * m - 1000 * s);
|
||||
sprintf(buffer, "%02" PRIu8 ":%02" PRIu8 ":%02" PRIu8 ".%03" PRIu16, h, m, s, u);
|
||||
}
|
||||
|
||||
#define MIN3(a, b, c) ((a) < (b) ? ((a) < (c) ? (a) : (c)) : ((b) < (c) ? (b) : (c)))
|
||||
|
||||
int levenshtein_dist(const uint64_t *s1, const uint64_t *s2, unsigned s1len, unsigned s2len)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_levenshtein_dist(s1, s2, s1len, s2len);
|
||||
#endif
|
||||
|
||||
unsigned int x, y, v, lastdiag, olddiag;
|
||||
unsigned int *column = (unsigned *)malloc((s1len + 1) * sizeof(unsigned int));
|
||||
for (y = 1; y <= s1len; y++)
|
||||
column[y] = y;
|
||||
for (x = 1; x <= s2len; x++)
|
||||
{
|
||||
column[0] = x;
|
||||
for (y = 1, lastdiag = x - 1; y <= s1len; y++)
|
||||
{
|
||||
olddiag = column[y];
|
||||
column[y] = MIN3(column[y] + 1, column[y - 1] + 1, lastdiag + (s1[y - 1] == s2[x - 1] ? 0 : 1));
|
||||
lastdiag = olddiag;
|
||||
}
|
||||
}
|
||||
v = column[s1len];
|
||||
free(column);
|
||||
return v;
|
||||
}
|
||||
|
||||
int levenshtein_dist_char(const char *s1, const char *s2, unsigned s1len, unsigned s2len)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_levenshtein_dist_char(s1, s2, s1len, s2len);
|
||||
#endif
|
||||
unsigned int x, y, v, lastdiag, olddiag;
|
||||
unsigned int *column = (unsigned *)malloc((s1len + 1) * sizeof(unsigned int));
|
||||
for (y = 1; y <= s1len; y++)
|
||||
column[y] = y;
|
||||
for (x = 1; x <= s2len; x++)
|
||||
{
|
||||
column[0] = x;
|
||||
for (y = 1, lastdiag = x - 1; y <= s1len; y++)
|
||||
{
|
||||
olddiag = column[y];
|
||||
column[y] = MIN3(column[y] + 1, column[y - 1] + 1, lastdiag + (s1[y - 1] == s2[x - 1] ? 0 : 1));
|
||||
lastdiag = olddiag;
|
||||
}
|
||||
}
|
||||
v = column[s1len];
|
||||
free(column);
|
||||
return v;
|
||||
}
|
||||
|
||||
void millis_to_date(uint64_t timestamp, char *buffer, enum ccx_output_date_format date_format, char millis_separator)
|
||||
{
|
||||
#ifndef DISABLE_RUST
|
||||
return ccxr_millis_to_date(timestamp, buffer, date_format, millis_separator);
|
||||
#endif
|
||||
|
||||
time_t secs;
|
||||
unsigned int millis;
|
||||
char c_temp[80];
|
||||
struct tm *time_struct = NULL;
|
||||
switch (date_format)
|
||||
{
|
||||
case ODF_NONE:
|
||||
buffer[0] = 0;
|
||||
break;
|
||||
case ODF_HHMMSS:
|
||||
timestamp_to_srttime(timestamp, buffer);
|
||||
buffer[8] = 0;
|
||||
break;
|
||||
case ODF_HHMMSSMS:
|
||||
timestamp_to_srttime(timestamp, buffer);
|
||||
break;
|
||||
case ODF_SECONDS:
|
||||
secs = (time_t)(timestamp / 1000);
|
||||
millis = (time_t)(timestamp % 1000);
|
||||
sprintf(buffer, "%lu%c%03u", (unsigned long)secs,
|
||||
millis_separator, (unsigned)millis);
|
||||
break;
|
||||
case ODF_DATE:
|
||||
secs = (time_t)(timestamp / 1000);
|
||||
millis = (unsigned int)(timestamp % 1000);
|
||||
time_struct = gmtime(&secs);
|
||||
strftime(c_temp, sizeof(c_temp), "%Y%m%d%H%M%S", time_struct);
|
||||
sprintf(buffer, "%s%c%03u", c_temp, millis_separator, millis);
|
||||
break;
|
||||
|
||||
default:
|
||||
fatal(CCX_COMMON_EXIT_BUG_BUG, "Invalid value for date_format in millis_to_date()\n");
|
||||
}
|
||||
}
|
||||
|
||||
bool_t in_array(uint16_t *array, uint16_t length, uint16_t element)
|
||||
|
||||
@@ -26,7 +26,6 @@ struct ccx_rational
|
||||
extern int temp_debug;
|
||||
volatile extern sig_atomic_t change_filename_requested;
|
||||
|
||||
#ifndef DISABLE_RUST
|
||||
extern int ccxr_verify_crc32(uint8_t *buf, int len);
|
||||
extern int ccxr_levenshtein_dist(const uint64_t *s1, const uint64_t *s2, unsigned s1len, unsigned s2len);
|
||||
extern int ccxr_levenshtein_dist_char(const char *s1, const char *s2, unsigned s1len, unsigned s2len);
|
||||
@@ -34,7 +33,6 @@ extern void ccxr_timestamp_to_srttime(uint64_t timestamp, char *buffer);
|
||||
extern void ccxr_timestamp_to_vtttime(uint64_t timestamp, char *buffer);
|
||||
extern void ccxr_millis_to_date(uint64_t timestamp, char *buffer, enum ccx_output_date_format date_format, char millis_separator);
|
||||
extern int ccxr_stringztoms(const char *s, struct ccx_boundary_time *bt);
|
||||
#endif
|
||||
|
||||
int levenshtein_dist_char (const char *s1, const char *s2, unsigned s1len, unsigned s2len);
|
||||
void init_boundary_time (struct ccx_boundary_time *bt);
|
||||
|
||||
Reference in New Issue
Block a user