Compare commits

...

23 Commits

Author SHA1 Message Date
Carlos Fernandez Sanz
ccf2a031e9 Bump version on Mac 0.91 -> 0.92 2021-08-10 04:24:39 -07:00
Carlos Fernandez Sanz
9784cd5bd1 Update CHANGES.TXT with last-minute Rust updates 2021-08-10 04:22:11 -07:00
Punit Lodha
5d8dc3b9eb Rust updates:- Add writers for transcripts and SAMI (#1372)
* add rust-iconv

* Add writer for transcipts and SAMI

* consistent import ordering
2021-08-10 04:21:22 -07:00
Carlos Fernandez Sanz
a42e847bcb Bump version 0.91 -> 0.92 2021-08-10 04:10:43 -07:00
Jayesh Nirve
b7a1dd1030 Update ISSUE_TEMPLATE.md (#1370) 2021-08-05 20:43:32 -07:00
Punit Lodha
b18e696c85 Rust updates: Added srt writer (#1368)
* pass -DENABLE_RUST to clang

* impl Default

* Update time str format

* Add SRT writer

* fmt
2021-08-03 13:59:31 -07:00
Willem
d58f078c38 Add missing DLL to the installer
Fixes #1367
2021-07-29 14:02:52 +02:00
PunitLodha
0bbdfc13ee Rust updates (#1364)
* add copy to screen

* Add tv_screen and more functions
2021-07-27 23:55:24 -07:00
Carlos Fernandez Sanz
5127da50d1 Push version 0.90 -> 0.91 2021-07-26 09:53:45 -07:00
PunitLodha
352f035214 [rust] Add Pen Presets and timing functions (#1363)
* add pen presets and timing functions

* fix typo

* fix formatting
2021-07-22 19:26:06 -07:00
PunitLodha
f04ba8d0c4 Rust updates (#1361)
* add handlers for CLW, HDW, TGW, DLW, and CR

* refactor rust code

* fix clippy warnings

* add ccxr_  prefix to rust functions

* Add SPA, SPC, SPL and CWx commands

* Add DSW and DFx commands

* Add more C0 and extended commands

* Use slice instead of sending whole packet and pos
2021-07-18 10:48:14 -07:00
Willem
1ea94d0b14 Update installer.wxs
Make ID's unique
2021-07-14 09:53:52 +02:00
Willem
7f99603859 Update installer.wxs
Add missing DLL's to the installation folder
2021-07-14 09:41:36 +02:00
Carlos Fernandez Sanz
3713283dfc Bump version 0.89 -> 0.90 2021-07-14 00:16:09 -07:00
PunitLodha
09129f1e63 [Rust] Add few commands and refactor the code (#1360)
* add handlers for CLW, HDW, TGW, DLW, and CR

* refactor rust code

* fix clippy warnings

* add ccxr_  prefix to rust functions

* Add SPA, SPC, SPL and CWx commands
2021-07-10 09:32:53 -07:00
PunitLodha
c56840ff2c Add functions to rust (#1358)
* add process_current_packet

* add process_service_block

* Add handle_G0 and G1 code sets

* remove unnecessary return

* Add C0 and C1 commands and their handlers
2021-07-04 11:11:03 -07:00
Willem
2a34bd99e6 [IMPROVEMENT] Automate release process for installer (#1357)
* Do not run push/pull request workflows for tags

* Stop including the old UI into artifacts for Widnows

* Introduce WiX installer and release flow
2021-06-28 14:18:33 -07:00
PunitLodha
c7886ed615 Add CI and docs for rust lib (#1355) 2021-06-27 17:58:51 +00:00
PunitLodha
948531a4be Update win_iconv path (#1356) 2021-06-26 11:43:32 -07:00
PunitLodha
022987c804 Add rust library (#1351)
* Add rust lib

* add steps for building rust lib

* use rust lib

* add conditional flag for rust

* use cargo config.toml

* add decoder module and update bindings

* use match instead of if else

* add target directory flag

* add env_logger

* use env_logger

* Process data first and then pass to safe function
2021-06-25 18:03:00 -07:00
PunitLodha
db6c852fae Add -DGPAC_CONFIG_LINUX for UNIX platforms (#1353) 2021-06-23 06:22:30 +00:00
PunitLodha
b793f16343 Update function declarations and naming style (#1350)
* Add declarations of functions and update names

* fix formating

* update function signature for dtvcc_process_data
2021-06-19 08:32:34 -07:00
carlos@ccextractor.org
ceaaa65a26 Remove confusing commits from build-static 2021-06-13 19:28:19 +00:00
44 changed files with 4344 additions and 724 deletions

View File

@@ -2,6 +2,8 @@ Please prefix your issue with one of the following: [BUG], [PROPOSAL], [QUESTION
To get the version of CCExtractor, you can use `--version`.
If this issue is related to the flutter GUI, please make the issue on the GUI repo [here](https://github.com/CCExtractor/ccextractorfluttergui/issues/new)
Please check all that apply and **remove the ones that do not**.
In the necessary information section, if this is a regression (something that used to work does not work anymore), make sure to specify the last known working version.

View File

@@ -9,6 +9,9 @@ on:
- '**Makefile**'
- 'linux/**'
- 'package_creators/**'
- 'src/rust/**'
tags-ignore: # ignore push via new tag
- '*.*'
pull_request:
types: [opened, synchronize, reopened]
paths:
@@ -18,6 +21,7 @@ on:
- '**Makefile**'
- 'linux/**'
- 'package_creators/**'
- 'src/rust/**'
jobs:
build_shell:
runs-on: ubuntu-latest
@@ -87,3 +91,23 @@ jobs:
- name: Display version information
working-directory: ./bazel-bin
run: ./src/ccextractor --version
build_rust:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2.3.4
- name: cache
uses: actions/cache@v2
with:
path: |
src/rust/.cargo/registry
src/rust/.cargo/git
src/rust/target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: build
run: cargo build
working-directory: ./src/rust

View File

@@ -7,6 +7,8 @@ on:
- '**.c'
- '**.h'
- 'windows/**'
tags-ignore: # ignore push via new tag
- '*.*'
pull_request:
types: [opened, synchronize, reopened]
paths:
@@ -34,7 +36,6 @@ jobs:
name: CCExtractor Windows Non-OCR Release build
path: |
./windows/Release/ccextractorwin.exe
./windows/Release/ccextractorgui.exe
./windows/Release/*.dll
build_non_ocr_debug:
runs-on: windows-latest
@@ -55,7 +56,6 @@ jobs:
path: |
./windows/Debug/ccextractorwin.exe
./windows/Debug/ccextractorwin.pdb
./windows/Debug/ccextractorgui.exe
./windows/Debug/*.dll
build_ocr_hardsubx_release:
runs-on: windows-latest
@@ -76,11 +76,6 @@ jobs:
path: |
./windows/Release-Full/ccextractorwinfull.exe
./windows/Release-Full/*.dll
- uses: actions/upload-artifact@v2
with:
name: CCExtractor Windows OCR and HardSubX Release build
path: |
./windows/Release/ccextractorgui.exe
build_ocr_hardsubx_debug:
runs-on: windows-latest
steps:
@@ -101,8 +96,3 @@ jobs:
./windows/Debug-Full/ccextractorwinfull.exe
./windows/Debug-Full/ccextractorwinfull.pdb
./windows/Debug-Full/*.dll
- uses: actions/upload-artifact@v2
with:
name: CCExtractor Windows OCR and HardSubX Debug build
path: |
./windows/Debug/ccextractorgui.exe

View File

@@ -5,12 +5,16 @@ on:
- '.github/workflows/format.yml'
- 'src/**.c'
- 'src/**.h'
- 'src/rust/**'
tags-ignore: # ignore push via new tag
- '*.*'
pull_request:
types: [opened, synchronize, reopened]
paths:
- '.github/workflows/format.yml'
- 'src/**.c'
- 'src/**.h'
- 'src/rust/**'
jobs:
format:
runs-on: ubuntu-latest
@@ -20,3 +24,28 @@ jobs:
run: |
find src/ -type f -not -path "src/thirdparty/*" -not -path "src/lib_ccx/zvbi/*" -name '*.c' -not -path "src/GUI/icon_data.c" | xargs clang-format -i
git diff-index --quiet HEAD -- || (git diff && exit 1)
format_rust:
runs-on: ubuntu-latest
defaults:
run:
working-directory: ./src/rust
steps:
- uses: actions/checkout@v2.3.4
- name: cache
uses: actions/cache@v2
with:
path: |
src/rust/.cargo/registry
src/rust/.cargo/git
src/rust/target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
components: rustfmt, clippy
- name: rustfmt
run: cargo fmt --all -- --check
- name: clippy
run: cargo clippy -- -D warnings

49
.github/workflows/release.yml vendored Normal file
View File

@@ -0,0 +1,49 @@
name: Upload releases
on:
release:
types:
- created
jobs:
build_windows:
runs-on: windows-latest
steps:
- name: Check out repository
uses: actions/checkout@v2.3.4
- name: Get the version
id: get_version
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
shell: bash
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.0.2
- name: build Release
run: msbuild ccextractor.sln /p:Configuration=Release-Full
working-directory: ./windows
- name: Copy files to directory for installer
run: mkdir installer; cp ./Release-Full/ccextractorwinfull.exe ./installer; cp ./Release-Full/*.dll ./installer
working-directory: ./windows
- name: install WiX
run: dotnet tool install --global wix --version 4.0.0-preview.0 && wix extension -g add WixToolset.UI.wixext
- name: Make sure WiX works
run: wix --version && wix extension -g list
- name: Download Flutter GUI
run: ((Invoke-WebRequest -UseBasicParsing https://api.github.com/repos/CCExtractor/ccextractorfluttergui/releases/latest).Content | ConvertFrom-Json).assets | ForEach-Object {if ($_.name -eq "windows.zip") { Invoke-WebRequest -UseBasicParsing -Uri $_.browser_download_url -OutFile windows.zip}}
working-directory: ./windows
- name: Display contents of dir
run: ls
working-directory: ./windows
- name: Unzip Flutter GUI
run: Expand-Archive -Path ./windows.zip -DestinationPath ./installer
working-directory: ./windows
- name: Display installer folder contents
run: Get-ChildItem -Recurse ./installer
working-directory: ./windows
- name: build installer
run: wix build -ext "$HOME\.wix\extensions\WixToolset.UI.wixext\4.0.0-preview.0\tools\WixToolset.UI.wixext.dll" -d "AppVersion=${{ steps.get_version.outputs.VERSION }}.0.0" -o CCExtractor.msi installer.wxs
working-directory: ./windows
- name: Upload as asset
uses: AButler/upload-release-assets@v2.0
with:
files: './windows/CCExtractor.msi'
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -4,7 +4,7 @@ MAINTAINER = Marc Espie <espie@openbsd.org>
CATEGORIES = multimedia
COMMENT = closed caption subtitles extractor
HOMEPAGE = https://ccextractor.org
V = 0.89
V = 0.92
DISTFILES = ccextractor.${V:S/.//}-src.zip
MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/}
DISTNAME = ccextractor-$V

View File

@@ -1,3 +1,21 @@
0.92 (2021-08-10)
-----------------
- Rust updates: Added srt writer
- Rust updates:-Added writers for transcripts and SAMI
- Added missing DLL to Windows installer
- Updated Windows GUI
0.91 (2021-07-26)
-----------------
- More Rust in the 708 decoder (Add Pen Presets and timing functions)
- Updated GUI
0.90 (2021-07-14)
-----------------
- New installer (WiX based)
- New GUI (flutter based)
- More Rust (the 708 decoder is being rewritten)
0.89 (2021-06-13)
-----------------
- Fix: Fix broken links in README

View File

@@ -87,7 +87,7 @@ make
sudo make install
```
`cmake` also accepts the argument `-DWITH_OCR=ON` to enable OCR.
`cmake` also accepts the argument `-DWITH_OCR=ON` to enable OCR and `-DWITH_RUST=ON` to enable rust.
### Compiling with GUI:

View File

@@ -35,5 +35,8 @@ cmake -DWITH_SHARING=ON ../src/
If you want to build CCExtractor with HARDSUBX support
cmake -DWITH_HARDSUBX=ON ../src/
If you want to build CCExtractor with rust enabled you need to pass
cmake -DWITH_RUST=ON ../src/
Hint for looking all the things you want to set from outside
cmake -LAH ../src/

View File

@@ -83,9 +83,6 @@ cd -;
# ccextractor -- build static
git clone https://github.com/CCExtractor/ccextractor;
cd ccextractor/linux/;
# wget https://sourceforge.net/projects/ccextractor/files/ccextractor/0.82/ccextractor.src.0.82.zip;
# unzip ccextractor*.zip;
# cd ccextractor.*/linux/;
perl -i -pe 's/O3 /O3 -static /' Makefile;
# quick patch:
perl -i -pe 's/(strchr|strstr)\(/$1((char *)/' ../src/thirdparty/gpacmp4/url.c ../src/thirdparty/gpacmp4/error.c;

View File

@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([CCExtractor], [0.89], [carlos@ccextractor.org])
AC_INIT([CCExtractor], [0.92], [carlos@ccextractor.org])
AC_CONFIG_AUX_DIR([build-conf])
AC_CONFIG_SRCDIR([../src/ccextractor.c])
AM_INIT_AUTOMAKE([foreign subdir-objects])

View File

@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.69])
AC_INIT([CCExtractor], [0.89], [carlos@ccextractor.org])
AC_INIT([CCExtractor], [0.92], [carlos@ccextractor.org])
AC_CONFIG_AUX_DIR([build-conf])
AC_CONFIG_SRCDIR([../src/ccextractor.c])
AM_INIT_AUTOMAKE([foreign subdir-objects])

View File

@@ -1,5 +1,5 @@
pkgname=ccextractor
pkgver=0.89
pkgver=0.92
pkgrel=1
pkgdesc="A closed captions and teletext subtitles extractor for video streams."
arch=('i686' 'x86_64')

View File

@@ -6,6 +6,7 @@ option (WITH_FFMPEG "Build using FFmpeg demuxer and decoder" OFF)
option (WITH_OCR "Build with OCR (Optical Character Recognition) feature" OFF)
option (WITH_SHARING "Build with sharing and translation support" OFF)
option (WITH_HARDSUBX "Build with support for burned-in subtitles" OFF)
option (WITH_RUST "Build with Rust library" OFF)
# Version number
set (CCEXTRACTOR_VERSION_MAJOR 0)
@@ -42,6 +43,10 @@ configure_file (
add_definitions(-DVERSION_FILE_PRESENT -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DGPAC_HAVE_CONFIG_H)
if(UNIX)
add_definitions(-DGPAC_CONFIG_LINUX)
endif(UNIX)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
add_definitions(-DGPAC_64_BITS)
endif()
@@ -226,6 +231,19 @@ if (PKG_CONFIG_FOUND AND WITH_HARDSUBX)
endif (PKG_CONFIG_FOUND AND WITH_HARDSUBX)
add_executable (ccextractor ${SOURCEFILE} ${FREETYPE_SOURCE} ${UTF8PROC_SOURCE})
########################################################
# Build with Rust library
########################################################
if (PKG_CONFIG_FOUND AND WITH_RUST)
add_subdirectory (rust)
get_target_property(RUST_LIB ccx_rust LOCATION)
set (EXTRA_LIBS ${EXTRA_LIBS} ${RUST_LIB})
add_dependencies(ccextractor ccx_rust)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_RUST")
endif (PKG_CONFIG_FOUND AND WITH_RUST)
target_link_libraries (ccextractor ${EXTRA_LIBS})
target_include_directories (ccextractor PUBLIC ${EXTRA_INCLUDES})

View File

@@ -86,6 +86,10 @@ if (MINGW OR CYGWIN)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGPAC_CONFIG_WIN32")
endif (MINGW OR CYGWIN)
if (WITH_RUST)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_RUST")
endif (WITH_RUST)
if (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGPAC_CONFIG_LINUX")
endif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")

File diff suppressed because it is too large Load Diff

View File

@@ -28,55 +28,55 @@
#define CCX_DTVCC_NO_LAST_SEQUENCE -1
enum CCX_DTVCC_COMMANDS_C0_CODES
enum DTVCC_COMMANDS_C0_CODES
{
CCX_DTVCC_C0_NUL = 0x00,
CCX_DTVCC_C0_ETX = 0x03,
CCX_DTVCC_C0_BS = 0x08,
CCX_DTVCC_C0_FF = 0x0c,
CCX_DTVCC_C0_CR = 0x0d,
CCX_DTVCC_C0_HCR = 0x0e,
CCX_DTVCC_C0_EXT1 = 0x10,
CCX_DTVCC_C0_P16 = 0x18
DTVCC_C0_NUL = 0x00,
DTVCC_C0_ETX = 0x03,
DTVCC_C0_BS = 0x08,
DTVCC_C0_FF = 0x0c,
DTVCC_C0_CR = 0x0d,
DTVCC_C0_HCR = 0x0e,
DTVCC_C0_EXT1 = 0x10,
DTVCC_C0_P16 = 0x18
};
enum CCX_DTVCC_COMMANDS_C1_CODES
enum DTVCC_COMMANDS_C1_CODES
{
CCX_DTVCC_C1_CW0 = 0x80,
CCX_DTVCC_C1_CW1 = 0x81,
CCX_DTVCC_C1_CW2 = 0x82,
CCX_DTVCC_C1_CW3 = 0x83,
CCX_DTVCC_C1_CW4 = 0x84,
CCX_DTVCC_C1_CW5 = 0x85,
CCX_DTVCC_C1_CW6 = 0x86,
CCX_DTVCC_C1_CW7 = 0x87,
CCX_DTVCC_C1_CLW = 0x88,
CCX_DTVCC_C1_DSW = 0x89,
CCX_DTVCC_C1_HDW = 0x8A,
CCX_DTVCC_C1_TGW = 0x8B,
CCX_DTVCC_C1_DLW = 0x8C,
CCX_DTVCC_C1_DLY = 0x8D,
CCX_DTVCC_C1_DLC = 0x8E,
CCX_DTVCC_C1_RST = 0x8F,
CCX_DTVCC_C1_SPA = 0x90,
CCX_DTVCC_C1_SPC = 0x91,
CCX_DTVCC_C1_SPL = 0x92,
CCX_DTVCC_C1_RSV93 = 0x93,
CCX_DTVCC_C1_RSV94 = 0x94,
CCX_DTVCC_C1_RSV95 = 0x95,
CCX_DTVCC_C1_RSV96 = 0x96,
CCX_DTVCC_C1_SWA = 0x97,
CCX_DTVCC_C1_DF0 = 0x98,
CCX_DTVCC_C1_DF1 = 0x99,
CCX_DTVCC_C1_DF2 = 0x9A,
CCX_DTVCC_C1_DF3 = 0x9B,
CCX_DTVCC_C1_DF4 = 0x9C,
CCX_DTVCC_C1_DF5 = 0x9D,
CCX_DTVCC_C1_DF6 = 0x9E,
CCX_DTVCC_C1_DF7 = 0x9F
DTVCC_C1_CW0 = 0x80,
DTVCC_C1_CW1 = 0x81,
DTVCC_C1_CW2 = 0x82,
DTVCC_C1_CW3 = 0x83,
DTVCC_C1_CW4 = 0x84,
DTVCC_C1_CW5 = 0x85,
DTVCC_C1_CW6 = 0x86,
DTVCC_C1_CW7 = 0x87,
DTVCC_C1_CLW = 0x88,
DTVCC_C1_DSW = 0x89,
DTVCC_C1_HDW = 0x8A,
DTVCC_C1_TGW = 0x8B,
DTVCC_C1_DLW = 0x8C,
DTVCC_C1_DLY = 0x8D,
DTVCC_C1_DLC = 0x8E,
DTVCC_C1_RST = 0x8F,
DTVCC_C1_SPA = 0x90,
DTVCC_C1_SPC = 0x91,
DTVCC_C1_SPL = 0x92,
DTVCC_C1_RSV93 = 0x93,
DTVCC_C1_RSV94 = 0x94,
DTVCC_C1_RSV95 = 0x95,
DTVCC_C1_RSV96 = 0x96,
DTVCC_C1_SWA = 0x97,
DTVCC_C1_DF0 = 0x98,
DTVCC_C1_DF1 = 0x99,
DTVCC_C1_DF2 = 0x9A,
DTVCC_C1_DF3 = 0x9B,
DTVCC_C1_DF4 = 0x9C,
DTVCC_C1_DF5 = 0x9D,
DTVCC_C1_DF6 = 0x9E,
DTVCC_C1_DF7 = 0x9F
};
struct CCX_DTVCC_S_COMMANDS_C1
struct DTVCC_S_COMMANDS_C1
{
int code;
const char *name;
@@ -84,142 +84,142 @@ struct CCX_DTVCC_S_COMMANDS_C1
int length;
};
enum ccx_dtvcc_window_justify
enum dtvcc_window_justify
{
CCX_DTVCC_WINDOW_JUSTIFY_LEFT = 0,
CCX_DTVCC_WINDOW_JUSTIFY_RIGHT = 1,
CCX_DTVCC_WINDOW_JUSTIFY_CENTER = 2,
CCX_DTVCC_WINDOW_JUSTIFY_FULL = 3
DTVCC_WINDOW_JUSTIFY_LEFT = 0,
DTVCC_WINDOW_JUSTIFY_RIGHT = 1,
DTVCC_WINDOW_JUSTIFY_CENTER = 2,
DTVCC_WINDOW_JUSTIFY_FULL = 3
};
enum ccx_dtvcc_window_pd //Print Direction
enum dtvcc_window_pd //Print Direction
{
CCX_DTVCC_WINDOW_PD_LEFT_RIGHT = 0, //left -> right
CCX_DTVCC_WINDOW_PD_RIGHT_LEFT = 1,
CCX_DTVCC_WINDOW_PD_TOP_BOTTOM = 2,
CCX_DTVCC_WINDOW_PD_BOTTOM_TOP = 3
DTVCC_WINDOW_PD_LEFT_RIGHT = 0, //left -> right
DTVCC_WINDOW_PD_RIGHT_LEFT = 1,
DTVCC_WINDOW_PD_TOP_BOTTOM = 2,
DTVCC_WINDOW_PD_BOTTOM_TOP = 3
};
enum ccx_dtvcc_window_sd //Scroll Direction
enum dtvcc_window_sd //Scroll Direction
{
CCX_DTVCC_WINDOW_SD_LEFT_RIGHT = 0,
CCX_DTVCC_WINDOW_SD_RIGHT_LEFT = 1,
CCX_DTVCC_WINDOW_SD_TOP_BOTTOM = 2,
CCX_DTVCC_WINDOW_SD_BOTTOM_TOP = 3
DTVCC_WINDOW_SD_LEFT_RIGHT = 0,
DTVCC_WINDOW_SD_RIGHT_LEFT = 1,
DTVCC_WINDOW_SD_TOP_BOTTOM = 2,
DTVCC_WINDOW_SD_BOTTOM_TOP = 3
};
enum ccx_dtvcc_window_sde //Scroll Display Effect
enum dtvcc_window_sde //Scroll Display Effect
{
CCX_DTVCC_WINDOW_SDE_SNAP = 0,
CCX_DTVCC_WINDOW_SDE_FADE = 1,
CCX_DTVCC_WINDOW_SDE_WIPE = 2
DTVCC_WINDOW_SDE_SNAP = 0,
DTVCC_WINDOW_SDE_FADE = 1,
DTVCC_WINDOW_SDE_WIPE = 2
};
enum ccx_dtvcc_window_ed //Effect Direction
enum dtvcc_window_ed //Effect Direction
{
CCX_DTVCC_WINDOW_ED_LEFT_RIGHT = 0,
CCX_DTVCC_WINDOW_ED_RIGHT_LEFT = 1,
CCX_DTVCC_WINDOW_ED_TOP_BOTTOM = 2,
CCX_DTVCC_WINDOW_ED_BOTTOM_TOP = 3
DTVCC_WINDOW_ED_LEFT_RIGHT = 0,
DTVCC_WINDOW_ED_RIGHT_LEFT = 1,
DTVCC_WINDOW_ED_TOP_BOTTOM = 2,
DTVCC_WINDOW_ED_BOTTOM_TOP = 3
};
enum ccx_dtvcc_window_fo //Fill Opacity
enum dtvcc_window_fo //Fill Opacity
{
CCX_DTVCC_WINDOW_FO_SOLID = 0,
CCX_DTVCC_WINDOW_FO_FLASH = 1,
CCX_DTVCC_WINDOW_FO_TRANSLUCENT = 2,
CCX_DTVCC_WINDOW_FO_TRANSPARENT = 3
DTVCC_WINDOW_FO_SOLID = 0,
DTVCC_WINDOW_FO_FLASH = 1,
DTVCC_WINDOW_FO_TRANSLUCENT = 2,
DTVCC_WINDOW_FO_TRANSPARENT = 3
};
enum ccx_dtvcc_window_border
enum dtvcc_window_border
{
CCX_DTVCC_WINDOW_BORDER_NONE = 0,
CCX_DTVCC_WINDOW_BORDER_RAISED = 1,
CCX_DTVCC_WINDOW_BORDER_DEPRESSED = 2,
CCX_DTVCC_WINDOW_BORDER_UNIFORM = 3,
CCX_DTVCC_WINDOW_BORDER_SHADOW_LEFT = 4,
CCX_DTVCC_WINDOW_BORDER_SHADOW_RIGHT = 5
DTVCC_WINDOW_BORDER_NONE = 0,
DTVCC_WINDOW_BORDER_RAISED = 1,
DTVCC_WINDOW_BORDER_DEPRESSED = 2,
DTVCC_WINDOW_BORDER_UNIFORM = 3,
DTVCC_WINDOW_BORDER_SHADOW_LEFT = 4,
DTVCC_WINDOW_BORDER_SHADOW_RIGHT = 5
};
enum ccx_dtvcc_pen_size
enum dtvcc_pen_size
{
CCX_DTVCC_PEN_SIZE_SMALL = 0,
CCX_DTVCC_PEN_SIZE_STANDART = 1,
CCX_DTVCC_PEN_SIZE_LARGE = 2
DTVCC_PEN_SIZE_SMALL = 0,
DTVCC_PEN_SIZE_STANDART = 1,
DTVCC_PEN_SIZE_LARGE = 2
};
enum ccx_dtvcc_pen_font_style
enum dtvcc_pen_font_style
{
CCX_DTVCC_PEN_FONT_STYLE_DEFAULT_OR_UNDEFINED = 0,
CCX_DTVCC_PEN_FONT_STYLE_MONOSPACED_WITH_SERIFS = 1,
CCX_DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITH_SERIFS = 2,
CCX_DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS = 3,
CCX_DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS = 4,
CCX_DTVCC_PEN_FONT_STYLE_CASUAL_FONT_TYPE = 5,
CCX_DTVCC_PEN_FONT_STYLE_CURSIVE_FONT_TYPE = 6,
CCX_DTVCC_PEN_FONT_STYLE_SMALL_CAPITALS = 7
DTVCC_PEN_FONT_STYLE_DEFAULT_OR_UNDEFINED = 0,
DTVCC_PEN_FONT_STYLE_MONOSPACED_WITH_SERIFS = 1,
DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITH_SERIFS = 2,
DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS = 3,
DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS = 4,
DTVCC_PEN_FONT_STYLE_CASUAL_FONT_TYPE = 5,
DTVCC_PEN_FONT_STYLE_CURSIVE_FONT_TYPE = 6,
DTVCC_PEN_FONT_STYLE_SMALL_CAPITALS = 7
};
enum ccx_dtvcc_pen_text_tag
enum dtvcc_pen_text_tag
{
CCX_DTVCC_PEN_TEXT_TAG_DIALOG = 0,
CCX_DTVCC_PEN_TEXT_TAG_SOURCE_OR_SPEAKER_ID = 1,
CCX_DTVCC_PEN_TEXT_TAG_ELECTRONIC_VOICE = 2,
CCX_DTVCC_PEN_TEXT_TAG_FOREIGN_LANGUAGE = 3,
CCX_DTVCC_PEN_TEXT_TAG_VOICEOVER = 4,
CCX_DTVCC_PEN_TEXT_TAG_AUDIBLE_TRANSLATION = 5,
CCX_DTVCC_PEN_TEXT_TAG_SUBTITLE_TRANSLATION = 6,
CCX_DTVCC_PEN_TEXT_TAG_VOICE_QUALITY_DESCRIPTION = 7,
CCX_DTVCC_PEN_TEXT_TAG_SONG_LYRICS = 8,
CCX_DTVCC_PEN_TEXT_TAG_SOUND_EFFECT_DESCRIPTION = 9,
CCX_DTVCC_PEN_TEXT_TAG_MUSICAL_SCORE_DESCRIPTION = 10,
CCX_DTVCC_PEN_TEXT_TAG_EXPLETIVE = 11,
CCX_DTVCC_PEN_TEXT_TAG_UNDEFINED_12 = 12,
CCX_DTVCC_PEN_TEXT_TAG_UNDEFINED_13 = 13,
CCX_DTVCC_PEN_TEXT_TAG_UNDEFINED_14 = 14,
CCX_DTVCC_PEN_TEXT_TAG_NOT_TO_BE_DISPLAYED = 15
DTVCC_PEN_TEXT_TAG_DIALOG = 0,
DTVCC_PEN_TEXT_TAG_SOURCE_OR_SPEAKER_ID = 1,
DTVCC_PEN_TEXT_TAG_ELECTRONIC_VOICE = 2,
DTVCC_PEN_TEXT_TAG_FOREIGN_LANGUAGE = 3,
DTVCC_PEN_TEXT_TAG_VOICEOVER = 4,
DTVCC_PEN_TEXT_TAG_AUDIBLE_TRANSLATION = 5,
DTVCC_PEN_TEXT_TAG_SUBTITLE_TRANSLATION = 6,
DTVCC_PEN_TEXT_TAG_VOICE_QUALITY_DESCRIPTION = 7,
DTVCC_PEN_TEXT_TAG_SONG_LYRICS = 8,
DTVCC_PEN_TEXT_TAG_SOUND_EFFECT_DESCRIPTION = 9,
DTVCC_PEN_TEXT_TAG_MUSICAL_SCORE_DESCRIPTION = 10,
DTVCC_PEN_TEXT_TAG_EXPLETIVE = 11,
DTVCC_PEN_TEXT_TAG_UNDEFINED_12 = 12,
DTVCC_PEN_TEXT_TAG_UNDEFINED_13 = 13,
DTVCC_PEN_TEXT_TAG_UNDEFINED_14 = 14,
DTVCC_PEN_TEXT_TAG_NOT_TO_BE_DISPLAYED = 15
};
enum ccx_dtvcc_pen_offset
enum dtvcc_pen_offset
{
CCX_DTVCC_PEN_OFFSET_SUBSCRIPT = 0,
CCX_DTVCC_PEN_OFFSET_NORMAL = 1,
CCX_DTVCC_PEN_OFFSET_SUPERSCRIPT = 2
DTVCC_PEN_OFFSET_SUBSCRIPT = 0,
DTVCC_PEN_OFFSET_NORMAL = 1,
DTVCC_PEN_OFFSET_SUPERSCRIPT = 2
};
enum ccx_dtvcc_pen_edge
enum dtvcc_pen_edge
{
CCX_DTVCC_PEN_EDGE_NONE = 0,
CCX_DTVCC_PEN_EDGE_RAISED = 1,
CCX_DTVCC_PEN_EDGE_DEPRESSED = 2,
CCX_DTVCC_PEN_EDGE_UNIFORM = 3,
CCX_DTVCC_PEN_EDGE_LEFT_DROP_SHADOW = 4,
CCX_DTVCC_PEN_EDGE_RIGHT_DROP_SHADOW = 5
DTVCC_PEN_EDGE_NONE = 0,
DTVCC_PEN_EDGE_RAISED = 1,
DTVCC_PEN_EDGE_DEPRESSED = 2,
DTVCC_PEN_EDGE_UNIFORM = 3,
DTVCC_PEN_EDGE_LEFT_DROP_SHADOW = 4,
DTVCC_PEN_EDGE_RIGHT_DROP_SHADOW = 5
};
enum ccx_dtvcc_pen_anchor_point
enum dtvcc_pen_anchor_point
{
CCX_DTVCC_ANCHOR_POINT_TOP_LEFT = 0,
CCX_DTVCC_ANCHOR_POINT_TOP_CENTER = 1,
CCX_DTVCC_ANCHOR_POINT_TOP_RIGHT = 2,
CCX_DTVCC_ANCHOR_POINT_MIDDLE_LEFT = 3,
CCX_DTVCC_ANCHOR_POINT_MIDDLE_CENTER = 4,
CCX_DTVCC_ANCHOR_POINT_MIDDLE_RIGHT = 5,
CCX_DTVCC_ANCHOR_POINT_BOTTOM_LEFT = 6,
CCX_DTVCC_ANCHOR_POINT_BOTTOM_CENTER = 7,
CCX_DTVCC_ANCHOR_POINT_BOTTOM_RIGHT = 8
DTVCC_ANCHOR_POINT_TOP_LEFT = 0,
DTVCC_ANCHOR_POINT_TOP_CENTER = 1,
DTVCC_ANCHOR_POINT_TOP_RIGHT = 2,
DTVCC_ANCHOR_POINT_MIDDLE_LEFT = 3,
DTVCC_ANCHOR_POINT_MIDDLE_CENTER = 4,
DTVCC_ANCHOR_POINT_MIDDLE_RIGHT = 5,
DTVCC_ANCHOR_POINT_BOTTOM_LEFT = 6,
DTVCC_ANCHOR_POINT_BOTTOM_CENTER = 7,
DTVCC_ANCHOR_POINT_BOTTOM_RIGHT = 8
};
typedef struct ccx_dtvcc_pen_color
typedef struct dtvcc_pen_color
{
int fg_color;
int fg_opacity;
int bg_color;
int bg_opacity;
int edge_color;
} ccx_dtvcc_pen_color;
} dtvcc_pen_color;
typedef struct ccx_dtvcc_pen_attribs
typedef struct dtvcc_pen_attribs
{
int pen_size;
int offset;
@@ -228,9 +228,9 @@ typedef struct ccx_dtvcc_pen_attribs
int edge_type;
int underline;
int italic;
} ccx_dtvcc_pen_attribs;
} dtvcc_pen_attribs;
typedef struct ccx_dtvcc_window_attribs
typedef struct dtvcc_window_attribs
{
int justify;
int print_direction;
@@ -243,18 +243,18 @@ typedef struct ccx_dtvcc_window_attribs
int fill_opacity;
int border_type;
int border_color;
} ccx_dtvcc_window_attribs;
} dtvcc_window_attribs;
/**
* Since 1-byte and 2-byte symbols could appear in captions and
* since we have to keep symbols alignment and several windows could appear on a screen at one time,
* we use special structure for holding symbols
*/
typedef struct ccx_dtvcc_symbol
typedef struct dtvcc_symbol
{
unsigned short sym; //symbol itself, at least 16 bit
unsigned char init; //initialized or not. could be 0 or 1
} ccx_dtvcc_symbol;
} dtvcc_symbol;
#define CCX_DTVCC_SYM_SET(x, c) {x.init = 1; x.sym = c;}
#define CCX_DTVCC_SYM_SET_16(x, c1, c2) {x.init = 1; x.sym = (c1 << 8) | c2;}
@@ -262,7 +262,7 @@ typedef struct ccx_dtvcc_symbol
#define CCX_DTVCC_SYM_IS_EMPTY(x) (x.init == 0)
#define CCX_DTVCC_SYM_IS_SET(x) (x.init == 1)
typedef struct ccx_dtvcc_window
typedef struct dtvcc_window
{
int is_defined;
int number;
@@ -279,25 +279,25 @@ typedef struct ccx_dtvcc_window
int pen_style;
int win_style;
unsigned char commands[6]; // Commands used to create this window
ccx_dtvcc_window_attribs attribs;
dtvcc_window_attribs attribs;
int pen_row;
int pen_column;
ccx_dtvcc_symbol *rows[CCX_DTVCC_MAX_ROWS];
ccx_dtvcc_pen_color pen_colors[CCX_DTVCC_MAX_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
ccx_dtvcc_pen_attribs pen_attribs[CCX_DTVCC_MAX_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
ccx_dtvcc_pen_color pen_color_pattern;
ccx_dtvcc_pen_attribs pen_attribs_pattern;
dtvcc_symbol *rows[CCX_DTVCC_MAX_ROWS];
dtvcc_pen_color pen_colors[CCX_DTVCC_MAX_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
dtvcc_pen_attribs pen_attribs[CCX_DTVCC_MAX_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
dtvcc_pen_color pen_color_pattern;
dtvcc_pen_attribs pen_attribs_pattern;
int memory_reserved;
int is_empty;
LLONG time_ms_show;
LLONG time_ms_hide;
} ccx_dtvcc_window;
} dtvcc_window;
typedef struct dtvcc_tv_screen
{
ccx_dtvcc_symbol chars[CCX_DTVCC_SCREENGRID_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
ccx_dtvcc_pen_color pen_colors[CCX_DTVCC_SCREENGRID_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
ccx_dtvcc_pen_attribs pen_attribs[CCX_DTVCC_SCREENGRID_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
dtvcc_symbol chars[CCX_DTVCC_SCREENGRID_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
dtvcc_pen_color pen_colors[CCX_DTVCC_SCREENGRID_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
dtvcc_pen_attribs pen_attribs[CCX_DTVCC_SCREENGRID_ROWS][CCX_DTVCC_SCREENGRID_COLUMNS];
LLONG time_ms_show;
LLONG time_ms_hide;
unsigned int cc_count;
@@ -314,13 +314,13 @@ typedef struct ccx_decoder_dtvcc_report
unsigned services[CCX_DTVCC_MAX_SERVICES];
} ccx_decoder_dtvcc_report;
typedef struct ccx_dtvcc_service_decoder
typedef struct dtvcc_service_decoder
{
ccx_dtvcc_window windows[CCX_DTVCC_MAX_WINDOWS];
dtvcc_window windows[CCX_DTVCC_MAX_WINDOWS];
int current_window;
dtvcc_tv_screen *tv;
int cc_count;
} ccx_dtvcc_service_decoder;
} dtvcc_service_decoder;
typedef struct ccx_decoder_dtvcc_settings
{
@@ -339,7 +339,7 @@ typedef struct ccx_decoder_dtvcc_settings
* decoders have to know nothing about output files
*/
typedef struct ccx_dtvcc_ctx
typedef struct dtvcc_ctx
{
int is_active;
int active_services_count;
@@ -348,7 +348,7 @@ typedef struct ccx_dtvcc_ctx
ccx_decoder_dtvcc_report *report;
ccx_dtvcc_service_decoder decoders[CCX_DTVCC_MAX_SERVICES];
dtvcc_service_decoder decoders[CCX_DTVCC_MAX_SERVICES];
unsigned char current_packet[CCX_DTVCC_MAX_PACKET_LENGTH];
int current_packet_length;
@@ -359,20 +359,84 @@ typedef struct ccx_dtvcc_ctx
void *encoder; //we can't include header, so keeping it this way
int no_rollup;
struct ccx_common_timing_ctx *timing;
} ccx_dtvcc_ctx;
} dtvcc_ctx;
void ccx_dtvcc_clear_packet(ccx_dtvcc_ctx *ctx);
void ccx_dtvcc_windows_reset(ccx_dtvcc_service_decoder *decoder);
void ccx_dtvcc_decoder_flush(ccx_dtvcc_ctx *dtvcc, ccx_dtvcc_service_decoder *decoder);
void dtvcc_clear_packet(dtvcc_ctx *ctx);
void dtvcc_windows_reset(dtvcc_service_decoder *decoder);
void dtvcc_decoder_flush(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder);
void ccx_dtvcc_process_current_packet(ccx_dtvcc_ctx *dtvcc, int len);
void ccx_dtvcc_process_service_block(ccx_dtvcc_ctx *dtvcc,
ccx_dtvcc_service_decoder *decoder,
void dtvcc_process_current_packet(dtvcc_ctx *dtvcc, int len);
void dtvcc_process_service_block(dtvcc_ctx *dtvcc,
dtvcc_service_decoder *decoder,
unsigned char *data,
int data_length);
extern ccx_dtvcc_pen_color ccx_dtvcc_default_pen_color;
extern ccx_dtvcc_pen_attribs ccx_dtvcc_default_pen_attribs;
void dtvcc_tv_clear(dtvcc_service_decoder *decoder);
int dtvcc_decoder_has_visible_windows(dtvcc_service_decoder *decoder);
void dtvcc_window_clear_row(dtvcc_window *window, int row_index);
void dtvcc_window_clear_text(dtvcc_window *window);
void dtvcc_window_clear(dtvcc_service_decoder *decoder, int window_id);
void dtvcc_window_apply_style(dtvcc_window *window, dtvcc_window_attribs *style);
#ifdef DTVCC_PRINT_DEBUG
int dtvcc_is_win_row_empty(dtvcc_window *window, int row_index);
void dtvcc_get_win_write_interval(dtvcc_window *window, int row_index, int *first, int *last);
void dtvcc_window_dump(dtvcc_service_decoder *decoder, dtvcc_window *window);
#endif
void dtvcc_decoders_reset(dtvcc_ctx *dtvcc);
int dtvcc_compare_win_priorities(const void *a, const void *b);
void dtvcc_window_update_time_show(dtvcc_window *window, struct ccx_common_timing_ctx *timing);
void dtvcc_window_update_time_hide(dtvcc_window *window, struct ccx_common_timing_ctx *timing);
void dtvcc_screen_update_time_show(dtvcc_tv_screen *tv, LLONG time);
void dtvcc_screen_update_time_hide(dtvcc_tv_screen *tv, LLONG time);
void dtvcc_get_window_dimensions(dtvcc_window *window, int *x1, int *x2, int *y1, int *y2);
int dtvcc_is_window_overlapping(dtvcc_service_decoder *decoder, dtvcc_window *window);
void dtvcc_window_copy_to_screen(dtvcc_service_decoder *decoder, dtvcc_window *window);
void dtvcc_screen_print(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder);
void dtvcc_process_hcr(dtvcc_service_decoder *decoder);
void dtvcc_process_ff(dtvcc_service_decoder *decoder);
void dtvcc_process_etx(dtvcc_service_decoder *decoder);
void dtvcc_process_bs(dtvcc_service_decoder *decoder);
void dtvcc_window_rollup(dtvcc_service_decoder *decoder, dtvcc_window *window);
void dtvcc_process_cr(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder);
void dtvcc_process_character(dtvcc_service_decoder *decoder, dtvcc_symbol symbol);
void dtvcc_handle_CWx_SetCurrentWindow(dtvcc_service_decoder *decoder, int window_id);
void dtvcc_handle_CLW_ClearWindows(dtvcc_ctx *dtvcc, dtvcc_service_decoder *decoder, int windows_bitmap);
void dtvcc_handle_DSW_DisplayWindows(dtvcc_service_decoder *decoder, int windows_bitmap, struct ccx_common_timing_ctx *timing);
void dtvcc_handle_HDW_HideWindows(dtvcc_ctx *dtvcc,dtvcc_service_decoder *decoder,
int windows_bitmap);
void dtvcc_handle_TGW_ToggleWindows(dtvcc_ctx *dtvcc,
dtvcc_service_decoder *decoder,
int windows_bitmap);
void dtvcc_handle_DFx_DefineWindow(dtvcc_service_decoder *decoder, int window_id, unsigned char *data, struct ccx_common_timing_ctx *timing);
void dtvcc_handle_SWA_SetWindowAttributes(dtvcc_service_decoder *decoder, unsigned char *data);
void dtvcc_handle_DLW_DeleteWindows(dtvcc_ctx *dtvcc,
dtvcc_service_decoder *decoder,
int windows_bitmap);
void dtvcc_handle_SPA_SetPenAttributes(dtvcc_service_decoder *decoder, unsigned char *data);
void dtvcc_handle_SPC_SetPenColor(dtvcc_service_decoder *decoder, unsigned char *data);
void dtvcc_handle_SPL_SetPenLocation(dtvcc_service_decoder *decoder, unsigned char *data);
void dtvcc_handle_RST_Reset(dtvcc_service_decoder *decoder);
void dtvcc_handle_DLY_Delay(dtvcc_service_decoder *decoder, int tenths_of_sec);
void dtvcc_handle_DLC_DelayCancel(dtvcc_service_decoder *decoder);
void dtvcc_handle_C0_P16(dtvcc_service_decoder *decoder, unsigned char *data);
int dtvcc_handle_G0(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
int dtvcc_handle_G1(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
int dtvcc_handle_C0(dtvcc_ctx *dtvcc,
dtvcc_service_decoder *decoder,
unsigned char *data,
int data_length);
int dtvcc_handle_C1(dtvcc_ctx *dtvcc,
dtvcc_service_decoder *decoder,
unsigned char *data,
int data_length);
int dtvcc_handle_C2(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
int dtvcc_handle_C3(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
int dtvcc_handle_extended_char(dtvcc_service_decoder *decoder, unsigned char *data, int data_length);
extern dtvcc_pen_color dtvcc_default_pen_color;
extern dtvcc_pen_attribs dtvcc_default_pen_attribs;
#endif

View File

@@ -4,7 +4,11 @@
#include "utility.h"
#include "ccx_common_common.h"
int _dtvcc_is_row_empty(dtvcc_tv_screen *tv, int row_index)
#if defined(ENABLE_RUST) && defined(WIN32)
extern void ccxr_close_handle(void *handle);
#endif
int dtvcc_is_row_empty(dtvcc_tv_screen *tv, int row_index)
{
for (int j = 0; j < CCX_DTVCC_SCREENGRID_COLUMNS; j++)
{
@@ -14,11 +18,11 @@ int _dtvcc_is_row_empty(dtvcc_tv_screen *tv, int row_index)
return 1;
}
int _dtvcc_is_screen_empty(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
int dtvcc_is_screen_empty(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
{
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
if (!_dtvcc_is_row_empty(tv, i))
if (!dtvcc_is_row_empty(tv, i))
{
// we will write subtitle
encoder->cea_708_counter++;
@@ -28,7 +32,7 @@ int _dtvcc_is_screen_empty(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
return 1;
}
void _dtvcc_get_write_interval(dtvcc_tv_screen *tv, int row_index, int *first, int *last)
void dtvcc_get_write_interval(dtvcc_tv_screen *tv, int row_index, int *first, int *last)
{
for (*first = 0; *first < CCX_DTVCC_SCREENGRID_COLUMNS; (*first)++)
if (CCX_DTVCC_SYM_IS_SET(tv->chars[row_index][*first]))
@@ -38,7 +42,7 @@ void _dtvcc_get_write_interval(dtvcc_tv_screen *tv, int row_index, int *first, i
break;
}
void _dtvcc_color_to_hex(int color, unsigned *hR, unsigned *hG, unsigned *hB)
void dtvcc_color_to_hex(int color, unsigned *hR, unsigned *hG, unsigned *hB)
{
*hR = (unsigned)(color >> 4);
*hG = (unsigned)((color >> 2) & 0x3);
@@ -47,16 +51,16 @@ void _dtvcc_color_to_hex(int color, unsigned *hR, unsigned *hG, unsigned *hB)
color, color, *hR, *hG, *hB);
}
void _dtvcc_change_pen_colors(dtvcc_tv_screen *tv, ccx_dtvcc_pen_color pen_color, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open)
void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open)
{
if (encoder->no_font_color)
return;
char *buf = (char *)encoder->buffer;
ccx_dtvcc_pen_color new_pen_color;
dtvcc_pen_color new_pen_color;
if (column_index >= CCX_DTVCC_SCREENGRID_COLUMNS)
new_pen_color = ccx_dtvcc_default_pen_color;
new_pen_color = dtvcc_default_pen_color;
else
new_pen_color = tv->pen_colors[row_index][column_index];
if (pen_color.fg_color != new_pen_color.fg_color)
@@ -67,7 +71,7 @@ void _dtvcc_change_pen_colors(dtvcc_tv_screen *tv, ccx_dtvcc_pen_color pen_color
if (new_pen_color.fg_color != 0x3f && open)
{
unsigned red, green, blue;
_dtvcc_color_to_hex(new_pen_color.fg_color, &red, &green, &blue);
dtvcc_color_to_hex(new_pen_color.fg_color, &red, &green, &blue);
red = (255 / 3) * red;
green = (255 / 3) * green;
blue = (255 / 3) * blue;
@@ -76,16 +80,16 @@ void _dtvcc_change_pen_colors(dtvcc_tv_screen *tv, ccx_dtvcc_pen_color pen_color
}
}
void _dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, ccx_dtvcc_pen_attribs pen_attribs, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open)
void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open)
{
if (encoder->no_font_color)
return;
char *buf = (char *)encoder->buffer;
ccx_dtvcc_pen_attribs new_pen_attribs;
dtvcc_pen_attribs new_pen_attribs;
if (column_index >= CCX_DTVCC_SCREENGRID_COLUMNS)
new_pen_attribs = ccx_dtvcc_default_pen_attribs;
new_pen_attribs = dtvcc_default_pen_attribs;
else
new_pen_attribs = tv->pen_attribs[row_index][column_index];
if (pen_attribs.italic != new_pen_attribs.italic)
@@ -119,7 +123,7 @@ size_t write_utf16_char(unsigned short utf16_char, char *out)
}
}
void _dtvcc_write_row(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *decoder, int row_index, struct encoder_ctx *encoder, int use_colors)
void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, int row_index, struct encoder_ctx *encoder, int use_colors)
{
dtvcc_tv_screen *tv = decoder->tv;
char *buf = (char *)encoder->buffer;
@@ -129,19 +133,19 @@ void _dtvcc_write_row(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *d
int fd = encoder->dtvcc_writers[tv->service_number - 1].fd;
ccx_dtvcc_pen_color pen_color = ccx_dtvcc_default_pen_color;
ccx_dtvcc_pen_attribs pen_attribs = ccx_dtvcc_default_pen_attribs;
_dtvcc_get_write_interval(tv, row_index, &first, &last);
dtvcc_pen_color pen_color = dtvcc_default_pen_color;
dtvcc_pen_attribs pen_attribs = dtvcc_default_pen_attribs;
dtvcc_get_write_interval(tv, row_index, &first, &last);
for (int i = 0; i < last + 1; i++)
{
if (use_colors)
_dtvcc_change_pen_colors(tv, pen_color, row_index, i, encoder, &buf_len, 0);
_dtvcc_change_pen_attribs(tv, pen_attribs, row_index, i, encoder, &buf_len, 0);
_dtvcc_change_pen_attribs(tv, pen_attribs, row_index, i, encoder, &buf_len, 1);
dtvcc_change_pen_colors(tv, pen_color, row_index, i, encoder, &buf_len, 0);
dtvcc_change_pen_attribs(tv, pen_attribs, row_index, i, encoder, &buf_len, 0);
dtvcc_change_pen_attribs(tv, pen_attribs, row_index, i, encoder, &buf_len, 1);
if (use_colors)
_dtvcc_change_pen_colors(tv, pen_color, row_index, i, encoder, &buf_len, 1);
dtvcc_change_pen_colors(tv, pen_color, row_index, i, encoder, &buf_len, 1);
pen_color = tv->pen_colors[row_index][i];
pen_attribs = tv->pen_attribs[row_index][i];
@@ -159,8 +163,8 @@ void _dtvcc_write_row(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *d
// there can be unclosed tags or colors after the last symbol in a row
if (use_colors)
_dtvcc_change_pen_colors(tv, pen_color, row_index, CCX_DTVCC_SCREENGRID_COLUMNS, encoder, &buf_len, 0);
_dtvcc_change_pen_attribs(tv, pen_attribs, row_index, CCX_DTVCC_SCREENGRID_COLUMNS, encoder, &buf_len, 0);
dtvcc_change_pen_colors(tv, pen_color, row_index, CCX_DTVCC_SCREENGRID_COLUMNS, encoder, &buf_len, 0);
dtvcc_change_pen_attribs(tv, pen_attribs, row_index, CCX_DTVCC_SCREENGRID_COLUMNS, encoder, &buf_len, 0);
// Tags can still be crossed e.g <f><i>text</f></i>, but testing HTML code has shown that they still are handled correctly.
// In case of errors fix it once and for all.
@@ -168,7 +172,7 @@ void _dtvcc_write_row(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *d
{
char *encoded_buf = calloc(INITIAL_ENC_BUFFER_CAPACITY, sizeof(char));
if (!encoded_buf)
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "_dtvcc_write_row");
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "dtvcc_write_row");
char *encoded_buf_start = encoded_buf;
@@ -178,7 +182,7 @@ void _dtvcc_write_row(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *d
size_t result = iconv(writer->cd, &buf, &in_bytes_left, &encoded_buf, &out_bytes_left);
if (result == -1)
ccx_common_logging.log_ftn("[CEA-708] _dtvcc_write_row: "
ccx_common_logging.log_ftn("[CEA-708] dtvcc_write_row: "
"conversion failed: %s\n",
strerror(errno));
@@ -192,10 +196,10 @@ void _dtvcc_write_row(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *d
}
}
void ccx_dtvcc_write_srt(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
void dtvcc_write_srt(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
{
dtvcc_tv_screen *tv = decoder->tv;
if (_dtvcc_is_screen_empty(tv, encoder))
if (dtvcc_is_screen_empty(tv, encoder))
return;
if (tv->time_ms_show + encoder->subs_delay < 0)
@@ -216,9 +220,9 @@ void ccx_dtvcc_write_srt(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
if (!_dtvcc_is_row_empty(tv, i))
if (!dtvcc_is_row_empty(tv, i))
{
_dtvcc_write_row(writer, decoder, i, encoder, 1);
dtvcc_write_row(writer, decoder, i, encoder, 1);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd,
encoder->encoded_crlf, encoder->encoded_crlf_length);
}
@@ -227,7 +231,7 @@ void ccx_dtvcc_write_srt(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder
encoder->encoded_crlf, encoder->encoded_crlf_length);
}
void ccx_dtvcc_write_debug(dtvcc_tv_screen *tv)
void dtvcc_write_debug(dtvcc_tv_screen *tv)
{
char tbuf1[SUBLINESIZE],
tbuf2[SUBLINESIZE];
@@ -238,10 +242,10 @@ void ccx_dtvcc_write_debug(dtvcc_tv_screen *tv)
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "\r%s --> %s\n", tbuf1, tbuf2);
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
if (!_dtvcc_is_row_empty(tv, i))
if (!dtvcc_is_row_empty(tv, i))
{
int first, last;
_dtvcc_get_write_interval(tv, i, &first, &last);
dtvcc_get_write_interval(tv, i, &first, &last);
for (int j = first; j <= last; j++)
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "%c", tv->chars[i][j]);
ccx_common_logging.debug_ftn(CCX_DMT_GENERIC_NOTICES, "\n");
@@ -249,10 +253,10 @@ void ccx_dtvcc_write_debug(dtvcc_tv_screen *tv)
}
}
void ccx_dtvcc_write_transcript(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
void dtvcc_write_transcript(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
{
dtvcc_tv_screen *tv = decoder->tv;
if (_dtvcc_is_screen_empty(tv, encoder))
if (dtvcc_is_screen_empty(tv, encoder))
return;
if (tv->time_ms_show + encoder->subs_delay < 0) // Drop screens that because of subs_delay start too early
@@ -262,7 +266,7 @@ void ccx_dtvcc_write_transcript(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
if (!_dtvcc_is_row_empty(tv, i))
if (!dtvcc_is_row_empty(tv, i))
{
buf[0] = 0;
@@ -284,14 +288,14 @@ void ccx_dtvcc_write_transcript(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_
if (buf_len != 0)
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
_dtvcc_write_row(writer, decoder, i, encoder, 0);
dtvcc_write_row(writer, decoder, i, encoder, 0);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd,
encoder->encoded_crlf, encoder->encoded_crlf_length);
}
}
}
void _dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
void dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
{
char *buf = (char *)encoder->buffer;
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY);
@@ -316,7 +320,7 @@ void _dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
}
void _dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
void dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
{
char *buf = (char *)encoder->buffer;
sprintf(buf, "</body></sami>");
@@ -325,17 +329,17 @@ void _dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
encoder->encoded_crlf, encoder->encoded_crlf_length);
}
void ccx_dtvcc_write_sami(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
{
dtvcc_tv_screen *tv = decoder->tv;
if (_dtvcc_is_screen_empty(tv, encoder))
if (dtvcc_is_screen_empty(tv, encoder))
return;
if (tv->time_ms_show + encoder->subs_delay < 0)
return;
if (tv->cc_count == 1)
_dtvcc_write_sami_header(tv, encoder);
dtvcc_write_sami_header(tv, encoder);
char *buf = (char *)encoder->buffer;
@@ -347,9 +351,9 @@ void ccx_dtvcc_write_sami(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decode
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
if (!_dtvcc_is_row_empty(tv, i))
if (!dtvcc_is_row_empty(tv, i))
{
_dtvcc_write_row(writer, decoder, i, encoder, 1);
dtvcc_write_row(writer, decoder, i, encoder, 1);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd,
encoder->encoded_br, encoder->encoded_br_length);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd,
@@ -363,52 +367,52 @@ void ccx_dtvcc_write_sami(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decode
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
}
void _ccx_dtvcc_write(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
void dtvcc_write(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
{
switch (encoder->write_format)
{
case CCX_OF_NULL:
break;
case CCX_OF_SRT:
ccx_dtvcc_write_srt(writer, decoder, encoder);
dtvcc_write_srt(writer, decoder, encoder);
break;
case CCX_OF_TRANSCRIPT:
ccx_dtvcc_write_transcript(writer, decoder, encoder);
dtvcc_write_transcript(writer, decoder, encoder);
break;
case CCX_OF_SAMI:
ccx_dtvcc_write_sami(writer, decoder, encoder);
dtvcc_write_sami(writer, decoder, encoder);
break;
case CCX_OF_MCC:
printf("REALLY BAD... [%s:%d]\n", __FILE__, __LINE__);
break;
default:
ccx_dtvcc_write_debug(decoder->tv);
dtvcc_write_debug(decoder->tv);
break;
}
}
void ccx_dtvcc_write_done(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
void dtvcc_write_done(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
{
switch (encoder->write_format)
{
case CCX_OF_SAMI:
_dtvcc_write_sami_footer(tv, encoder);
dtvcc_write_sami_footer(tv, encoder);
break;
default:
ccx_common_logging.debug_ftn(
CCX_DMT_708, "[CEA-708] ccx_dtvcc_write_done: no handling required\n");
CCX_DMT_708, "[CEA-708] dtvcc_write_done: no handling required\n");
break;
}
}
void ccx_dtvcc_writer_init(ccx_dtvcc_writer_ctx *writer,
char *base_filename,
int program_number,
int service_number,
enum ccx_output_format write_format,
struct encoder_cfg *cfg)
void dtvcc_writer_init(dtvcc_writer_ctx *writer,
char *base_filename,
int program_number,
int service_number,
enum ccx_output_format write_format,
struct encoder_cfg *cfg)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_init\n");
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_writer_init\n");
writer->fd = -1;
writer->cd = (iconv_t)-1;
if ((write_format == CCX_OF_NULL) || (write_format == CCX_OF_MCC))
@@ -417,7 +421,7 @@ void ccx_dtvcc_writer_init(ccx_dtvcc_writer_ctx *writer,
return;
}
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_init: "
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_writer_init: "
"[%s][%d][%d]\n",
base_filename, program_number, service_number);
@@ -428,12 +432,17 @@ void ccx_dtvcc_writer_init(ccx_dtvcc_writer_ctx *writer,
writer->filename = create_outfilename(base_filename, suffix, ext);
if (!writer->filename)
ccx_common_logging.fatal_ftn(
EXIT_NOT_ENOUGH_MEMORY, "[CEA-708] _dtvcc_decoder_init_write: not enough memory");
EXIT_NOT_ENOUGH_MEMORY, "[CEA-708] dtvcc_decoder_init_write: not enough memory");
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_init: inited [%s]\n", writer->filename);
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_writer_init: inited [%s]\n", writer->filename);
char *charset = cfg->all_services_charset ? cfg->all_services_charset : cfg->services_charsets[service_number - 1];
#ifdef ENABLE_RUST
writer->fhandle = NULL;
writer->charset = charset;
#endif
if (charset)
{
writer->cd = iconv_open("UTF-8", charset);
@@ -446,10 +455,14 @@ void ccx_dtvcc_writer_init(ccx_dtvcc_writer_ctx *writer,
}
}
void ccx_dtvcc_writer_cleanup(ccx_dtvcc_writer_ctx *writer)
void dtvcc_writer_cleanup(dtvcc_writer_ctx *writer)
{
if (writer->fd >= 0 && writer->fd != STDOUT_FILENO)
close(writer->fd);
#if defined(ENABLE_RUST) && defined(WIN32)
ccxr_close_handle(writer->fhandle);
writer->charset = NULL;
#endif
free(writer->filename);
if (writer->cd == (iconv_t)-1)
{
@@ -459,9 +472,9 @@ void ccx_dtvcc_writer_cleanup(ccx_dtvcc_writer_ctx *writer)
iconv_close(writer->cd);
}
void ccx_dtvcc_writer_output(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
void dtvcc_writer_output(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] ccx_dtvcc_writer_output: "
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_writer_output: "
"writing... [%s][%d]\n",
writer->filename, writer->fd);
@@ -471,7 +484,7 @@ void ccx_dtvcc_writer_output(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_dec
if (writer->filename && writer->fd < 0) //first request to write
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] "
"ccx_dtvcc_writer_output: creating %s\n",
"dtvcc_writer_output: creating %s\n",
writer->filename);
writer->fd = open(writer->filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
if (writer->fd == -1)
@@ -483,5 +496,5 @@ void ccx_dtvcc_writer_output(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_dec
write_wrapped(writer->fd, UTF8_BOM, sizeof(UTF8_BOM));
}
_ccx_dtvcc_write(writer, decoder, encoder);
dtvcc_write(writer, decoder, encoder);
}

View File

@@ -5,15 +5,31 @@
#include "ccx_encoders_common.h"
#include "ccx_common_option.h"
void ccx_dtvcc_write_done(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void dtvcc_write_done(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void ccx_dtvcc_writer_init(ccx_dtvcc_writer_ctx *writer,
void dtvcc_writer_init(dtvcc_writer_ctx *writer,
char *base_filename,
int program_number,
int service_number,
enum ccx_output_format write_format,
struct encoder_cfg *cfg);
void ccx_dtvcc_writer_cleanup(ccx_dtvcc_writer_ctx *writer);
void ccx_dtvcc_writer_output(ccx_dtvcc_writer_ctx *writer, ccx_dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
void dtvcc_writer_cleanup(dtvcc_writer_ctx *writer);
void dtvcc_writer_output(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
int dtvcc_is_row_empty(dtvcc_tv_screen *tv, int row_index);
int dtvcc_is_screen_empty(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void dtvcc_get_write_interval(dtvcc_tv_screen *tv, int row_index, int *first, int *last);
void dtvcc_color_to_hex(int color, unsigned *hR, unsigned *hG, unsigned *hB);
void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open);
void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs, int row_index, int column_index, struct encoder_ctx *encoder, size_t *buf_len, int open);
size_t write_utf16_char(unsigned short utf16_char, char *out);
void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, int row_index, struct encoder_ctx *encoder, int use_colors);
void dtvcc_write_srt(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
void dtvcc_write_debug(dtvcc_tv_screen *tv);
void dtvcc_write_transcript(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
void dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
void dtvcc_write(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
#endif /*_CCX_DECODERS_708_OUTPUT_H_*/

View File

@@ -15,6 +15,10 @@ made to reuse, not duplicate, as many functions as possible */
#include "ccx_encoders_mcc.h"
#include "ccx_dtvcc.h"
#ifdef ENABLE_RUST
extern int ccxr_process_cc_data(struct lib_cc_decode *dec_ctx, unsigned char *cc_data, int cc_count);
#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;
@@ -49,6 +53,10 @@ int process_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx,
return 0;
}
#ifdef ENABLE_RUST
ret = ccxr_process_cc_data(dec_ctx, cc_data, cc_count);
#endif
for (int j = 0; j < cc_count * 3; j = j + 3)
{
if (validate_cc_data_pair(cc_data + j))
@@ -195,7 +203,11 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
if (timeok)
{
if (ctx->write_format != CCX_OF_RCWT)
ccx_dtvcc_process_data(ctx, (const unsigned char *)temp, 4);
{
#ifndef ENABLE_RUST
dtvcc_process_data(ctx->dtvcc, (const unsigned char *)temp);
#endif
}
else
writercwtdata(ctx, cc_block, sub);
}
@@ -219,7 +231,7 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
void dinit_cc_decode(struct lib_cc_decode **ctx)
{
struct lib_cc_decode *lctx = *ctx;
ccx_dtvcc_free(&lctx->dtvcc);
dtvcc_free(&lctx->dtvcc);
dinit_avc(&lctx->avc_ctx);
ccx_decoder_608_dinit_library(&lctx->context_cc608_field_1);
ccx_decoder_608_dinit_library(&lctx->context_cc608_field_2);
@@ -248,7 +260,7 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
ctx->no_rollup = setting->no_rollup;
ctx->noscte20 = setting->noscte20;
ctx->dtvcc = ccx_dtvcc_init(setting->settings_dtvcc);
ctx->dtvcc = dtvcc_init(setting->settings_dtvcc);
ctx->dtvcc->is_active = setting->settings_dtvcc->enabled;
if (setting->codec == CCX_CODEC_ATSC_CC)
@@ -432,13 +444,13 @@ void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub)
{
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
{
ccx_dtvcc_service_decoder *decoder = &ctx->dtvcc->decoders[i];
dtvcc_service_decoder *decoder = &ctx->dtvcc->decoders[i];
if (!ctx->dtvcc->services_active[i])
continue;
if (decoder->cc_count > 0)
{
ctx->current_field = 3;
ccx_dtvcc_decoder_flush(ctx->dtvcc, decoder);
dtvcc_decoder_flush(ctx->dtvcc, decoder);
}
}
}
@@ -520,8 +532,8 @@ struct lib_cc_decode *copy_decoder_context(struct lib_cc_decode *ctx)
ctx_copy->private_data = NULL;
if (ctx->dtvcc)
{
ctx_copy->dtvcc = malloc(sizeof(struct ccx_dtvcc_ctx));
memcpy(ctx_copy->dtvcc, ctx->dtvcc, sizeof(struct ccx_dtvcc_ctx));
ctx_copy->dtvcc = malloc(sizeof(struct dtvcc_ctx));
memcpy(ctx_copy->dtvcc, ctx->dtvcc, sizeof(struct dtvcc_ctx));
}
if (ctx->xds_ctx)
{

View File

@@ -208,7 +208,7 @@ struct lib_cc_decode
int stat_divicom;
int false_pict_header;
ccx_dtvcc_ctx *dtvcc;
dtvcc_ctx *dtvcc;
int current_field;
// Analyse/use the picture information
int maxtref; // Use to remember the temporal reference number

View File

@@ -3,9 +3,8 @@
#include "ccx_encoders_common.h"
#include "ccx_decoders_708_output.h"
void ccx_dtvcc_process_data(struct lib_cc_decode *ctx,
const unsigned char *data,
int data_length)
void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
const unsigned char *data)
{
/*
* Note: the data has following format:
@@ -14,78 +13,73 @@ void ccx_dtvcc_process_data(struct lib_cc_decode *ctx,
* 2 bytes for the actual data
*/
ccx_dtvcc_ctx *dtvcc = ctx->dtvcc;
if (!dtvcc->is_active && !dtvcc->report_enabled)
return;
for (int i = 0; i < data_length; i += 4)
unsigned char cc_valid = data[0];
unsigned char cc_type = data[1];
switch (cc_type)
{
unsigned char cc_valid = data[i];
unsigned char cc_type = data[i + 1];
switch (cc_type)
{
case 2:
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Data\n");
if (cc_valid && dtvcc->is_current_packet_header_parsed)
case 2:
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Data\n");
if (cc_valid && dtvcc->is_current_packet_header_parsed)
{
if (dtvcc->current_packet_length > 253)
{
if (dtvcc->current_packet_length > 253)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
"Warning: Legal packet size exceeded (1), data not added.\n");
}
else
{
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 2];
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 3];
int len = dtvcc->current_packet[0] & 0x3F; // 6 least significants bits
if (len == 0) // This is well defined in EIA-708; no magic.
len = 128;
else
len = len * 2;
// Note that len here is the length including the header
if (dtvcc->current_packet_length >= len)
ccx_dtvcc_process_current_packet(dtvcc, len);
}
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
"Warning: Legal packet size exceeded (1), data not added.\n");
}
break;
case 3:
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Start\n");
if (cc_valid)
else
{
if (dtvcc->current_packet_length > CCX_DTVCC_MAX_PACKET_LENGTH - 1)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
"Warning: Legal packet size exceeded (2), data not added.\n");
}
dtvcc->current_packet[dtvcc->current_packet_length++] = data[2];
dtvcc->current_packet[dtvcc->current_packet_length++] = data[3];
int len = dtvcc->current_packet[0] & 0x3F; // 6 least significants bits
if (len == 0) // This is well defined in EIA-708; no magic.
len = 128;
else
{
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 2];
dtvcc->current_packet[dtvcc->current_packet_length++] = data[i + 3];
dtvcc->is_current_packet_header_parsed = 1;
}
len = len * 2;
// Note that len here is the length including the header
if (dtvcc->current_packet_length >= len)
dtvcc_process_current_packet(dtvcc, len);
}
break;
default:
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "[CEA-708] dtvcc_process_data: "
"shouldn't be here - cc_type: %d\n",
cc_type);
}
}
break;
case 3:
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: DTVCC Channel Packet Start\n");
if (cc_valid)
{
if (dtvcc->current_packet_length > CCX_DTVCC_MAX_PACKET_LENGTH - 1)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_data: "
"Warning: Legal packet size exceeded (2), data not added.\n");
}
else
{
dtvcc->current_packet[dtvcc->current_packet_length++] = data[2];
dtvcc->current_packet[dtvcc->current_packet_length++] = data[3];
dtvcc->is_current_packet_header_parsed = 1;
}
}
break;
default:
ccx_common_logging.fatal_ftn(CCX_COMMON_EXIT_BUG_BUG, "[CEA-708] dtvcc_process_data: "
"shouldn't be here - cc_type: %d\n",
cc_type);
}
}
//--------------------------------------------------------------------------------------
ccx_dtvcc_ctx *ccx_dtvcc_init(struct ccx_decoder_dtvcc_settings *opts)
dtvcc_ctx *dtvcc_init(struct ccx_decoder_dtvcc_settings *opts)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] initializing dtvcc decoder\n");
ccx_dtvcc_ctx *ctx = (ccx_dtvcc_ctx *)malloc(sizeof(ccx_dtvcc_ctx));
dtvcc_ctx *ctx = (dtvcc_ctx *)malloc(sizeof(dtvcc_ctx));
if (!ctx)
{
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "[CEA-708] ccx_dtvcc_init");
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "[CEA-708] dtvcc_init");
return NULL;
}
@@ -98,7 +92,7 @@ ccx_dtvcc_ctx *ccx_dtvcc_init(struct ccx_decoder_dtvcc_settings *opts)
memcpy(ctx->services_active, opts->services_enabled, CCX_DTVCC_MAX_SERVICES * sizeof(int));
ccx_dtvcc_clear_packet(ctx);
dtvcc_clear_packet(ctx);
ctx->last_sequence = CCX_DTVCC_NO_LAST_SEQUENCE;
@@ -112,35 +106,35 @@ ccx_dtvcc_ctx *ccx_dtvcc_init(struct ccx_decoder_dtvcc_settings *opts)
if (!ctx->services_active[i])
continue;
ccx_dtvcc_service_decoder *decoder = &ctx->decoders[i];
dtvcc_service_decoder *decoder = &ctx->decoders[i];
decoder->cc_count = 0;
decoder->tv = (dtvcc_tv_screen *)malloc(sizeof(dtvcc_tv_screen));
decoder->tv->service_number = i + 1;
decoder->tv->cc_count = 0;
if (!decoder->tv)
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "ccx_dtvcc_init");
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "dtvcc_init");
for (int j = 0; j < CCX_DTVCC_MAX_WINDOWS; j++)
decoder->windows[j].memory_reserved = 0;
ccx_dtvcc_windows_reset(decoder);
dtvcc_windows_reset(decoder);
}
return ctx;
}
void ccx_dtvcc_free(ccx_dtvcc_ctx **ctx_ptr)
void dtvcc_free(dtvcc_ctx **ctx_ptr)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_free: cleaning up\n");
ccx_dtvcc_ctx *ctx = *ctx_ptr;
dtvcc_ctx *ctx = *ctx_ptr;
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
{
if (!ctx->services_active[i])
continue;
ccx_dtvcc_service_decoder *decoder = &ctx->decoders[i];
dtvcc_service_decoder *decoder = &ctx->decoders[i];
for (int j = 0; j < CCX_DTVCC_MAX_WINDOWS; j++)
if (decoder->windows[j].memory_reserved)

View File

@@ -4,11 +4,10 @@
#include "ccx_decoders_708.h"
#include "ccx_common_option.h"
void ccx_dtvcc_process_data(struct lib_cc_decode *ctx,
const unsigned char *data,
int data_length);
void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
const unsigned char *data);
ccx_dtvcc_ctx *ccx_dtvcc_init(ccx_decoder_dtvcc_settings *opts);
void ccx_dtvcc_free(ccx_dtvcc_ctx **);
dtvcc_ctx *dtvcc_init(ccx_decoder_dtvcc_settings *opts);
void dtvcc_free(dtvcc_ctx **);
#endif //CCEXTRACTOR_CCX_DTVCC_H

View File

@@ -764,7 +764,7 @@ static void dinit_output_ctx(struct encoder_ctx *ctx)
if (ctx->dtvcc_extract)
{
for (i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
ccx_dtvcc_writer_cleanup(&ctx->dtvcc_writers[i]);
dtvcc_writer_cleanup(&ctx->dtvcc_writers[i]);
}
}
@@ -869,6 +869,10 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
if (!cfg->services_enabled[i])
{
ctx->dtvcc_writers[i].fd = -1;
#ifdef ENABLE_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;
@@ -877,6 +881,10 @@ 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;
#ifdef ENABLE_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;
}
@@ -888,8 +896,8 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
else
basefilename = get_basename(ctx->first_input_file);
ccx_dtvcc_writer_init(&ctx->dtvcc_writers[i], basefilename,
ctx->program_number, i + 1, cfg->write_format, cfg);
dtvcc_writer_init(&ctx->dtvcc_writers[i], basefilename,
ctx->program_number, i + 1, cfg->write_format, cfg);
free(basefilename);
}
}

View File

@@ -2,7 +2,7 @@
#define _CC_ENCODER_COMMON_H
#ifdef WIN32
#include "..\\win_iconv\\iconv.h"
#include "..\\thirdparty\\win_iconv\\iconv.h"
#else
#include "iconv.h"
#endif
@@ -21,12 +21,18 @@ if (ctx->buffer == NULL) { fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for
#define ROWS 15
#define COLUMNS 32
typedef struct ccx_dtvcc_writer_ctx
typedef struct dtvcc_writer_ctx
{
int fd;
#ifdef ENABLE_RUST
// File handle used to work with files on windows
void *fhandle;
// Charset of the subtitle
char *charset;
#endif
char *filename;
iconv_t cd;
} ccx_dtvcc_writer_ctx;
} dtvcc_writer_ctx;
typedef struct ccx_sbs_utf8_character
{
@@ -106,7 +112,7 @@ struct encoder_ctx
int extract;
int dtvcc_extract; // 1 or 0 depending if we have to handle dtvcc
ccx_dtvcc_writer_ctx dtvcc_writers[CCX_DTVCC_MAX_SERVICES];
dtvcc_writer_ctx dtvcc_writers[CCX_DTVCC_MAX_SERVICES];
/* Timing related variables*/
/* start time of previous sub */

View File

@@ -1,7 +1,7 @@
#ifndef CCX_CCEXTRACTOR_H
#define CCX_CCEXTRACTOR_H
#define VERSION "0.89"
#define VERSION "0.92"
// Load common includes and constants for library usage
#include "ccx_common_platform.h"

View File

@@ -395,8 +395,7 @@ static int process_clcp(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx,
}
// WARN: otherwise cea-708 will not work
dec_ctx->dtvcc->encoder = (void *)enc_ctx;
// TODO: is it really always 4-bytes long?
ccx_dtvcc_process_data(dec_ctx, (unsigned char *)temp, 4);
dtvcc_process_data(dec_ctx->dtvcc, (unsigned char *)temp);
cb_708++;
}
if (ctx->write_format == CCX_OF_MCC)

View File

@@ -35,6 +35,10 @@
#define DEFAULT_FONT_PATH_ITALICS "/usr/share/fonts/truetype/noto/NotoSans-Italic.ttf"
#endif
#ifdef ENABLE_RUST
extern void ccxr_init_logger();
#endif
static int inputfile_capacity = 0;
size_t remove_trailing_whitespace(char *line)
@@ -2179,6 +2183,9 @@ int parse_parameters(struct ccx_s_options *opt, int argc, char *argv[])
if (strcmp(argv[i], "-708") == 0)
{
opt->debug_mask |= CCX_DMT_708;
#ifdef ENABLE_RUST
ccxr_init_logger();
#endif
continue;
}
if (strcmp(argv[i], "-goppts") == 0)

View File

@@ -4,7 +4,7 @@
#include "utility.h"
#include <stdbool.h>
#ifdef WIN32
#include "..\\win_iconv\\iconv.h"
#include "..\\thirdparty\\win_iconv\\iconv.h"
#else
#include "iconv.h"
#endif

20
src/rust/CMakeLists.txt Normal file
View File

@@ -0,0 +1,20 @@
if (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CARGO_CMD cargo build)
set(TARGET_DIR "debug")
else ()
set(CARGO_CMD cargo build --release)
set(TARGET_DIR "release")
endif ()
set(CCX_RUST_SO "${CMAKE_CURRENT_BINARY_DIR}/${TARGET_DIR}/libccx_rust.so")
add_custom_target(ccx_rust ALL
COMMENT "Compiling ccx_rust module"
COMMAND CARGO_TARGET_DIR=${CMAKE_CURRENT_BINARY_DIR} ${CARGO_CMD}
COMMAND cp ${CCX_RUST_SO} ${CMAKE_CURRENT_BINARY_DIR}
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
set_target_properties(ccx_rust PROPERTIES LOCATION ${CCX_RUST_SO})
add_test(NAME ccx_rust_test
COMMAND cargo test
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})

354
src/rust/Cargo.lock generated Normal file
View File

@@ -0,0 +1,354 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
[[package]]
name = "aho-corasick"
version = "0.7.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e37cfd5e7657ada45f742d6e99ca5788580b5c529dc78faf11ece6dc702656f"
dependencies = [
"memchr",
]
[[package]]
name = "ansi_term"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ee49baf6cb617b853aa8d93bf420db2383fab46d314482ca2803b40d5fde979b"
dependencies = [
"winapi",
]
[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9b39be18770d11421cdb1b9947a45dd3f37e93092cbf377614828a319d5fee8"
dependencies = [
"hermit-abi",
"libc",
"winapi",
]
[[package]]
name = "bindgen"
version = "0.58.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0f8523b410d7187a43085e7e064416ea32ded16bd0a4e6fc025e21616d01258f"
dependencies = [
"bitflags",
"cexpr",
"clang-sys",
"clap",
"env_logger",
"lazy_static",
"lazycell",
"log",
"peeking_take_while",
"proc-macro2",
"quote",
"regex",
"rustc-hash",
"shlex",
"which",
]
[[package]]
name = "bitflags"
version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf1de2fe8c75bc145a2f577add951f8134889b4795d47466a54a5c846d691693"
[[package]]
name = "ccx_rust"
version = "0.1.0"
dependencies = [
"bindgen",
"env_logger",
"iconv",
"log",
]
[[package]]
name = "cexpr"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f4aedb84272dbe89af497cf81375129abda4fc0a9e7c5d317498c15cc30c0d27"
dependencies = [
"nom",
]
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "clang-sys"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "853eda514c284c2287f4bf20ae614f8781f40a81d32ecda6e91449304dfe077c"
dependencies = [
"glob",
"libc",
"libloading",
]
[[package]]
name = "clap"
version = "2.33.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "37e58ac78573c40708d45522f0d80fa2f01cc4f9b4e2bf749807255454312002"
dependencies = [
"ansi_term",
"atty",
"bitflags",
"strsim",
"textwrap",
"unicode-width",
"vec_map",
]
[[package]]
name = "dyn_buf"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74c57ab96715773d9cb9789b38eb7cbf04b3c6f5624a9d98f51761603376767c"
[[package]]
name = "env_logger"
version = "0.8.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a19187fea3ac7e84da7dacf48de0c45d63c6a76f9490dae389aead16c243fce3"
dependencies = [
"atty",
"humantime",
"log",
"regex",
"termcolor",
]
[[package]]
name = "glob"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
[[package]]
name = "hermit-abi"
version = "0.1.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "322f4de77956e22ed0e5032c359a0f1273f1f7f0d79bfa3b8ffbc730d7fbcc5c"
dependencies = [
"libc",
]
[[package]]
name = "humantime"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
[[package]]
name = "iconv"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07e6a7db0df823ef299ef75b6951975c7a1f9019910b3665614bac4161bab1a9"
dependencies = [
"dyn_buf",
"libc",
]
[[package]]
name = "lazy_static"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lazycell"
version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
[[package]]
name = "libc"
version = "0.2.98"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320cfe77175da3a483efed4bc0adc1968ca050b098ce4f2f1c13a56626128790"
[[package]]
name = "libloading"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f84d96438c15fcd6c3f244c8fce01d1e2b9c6b5623e9c711dc9286d8fc92d6a"
dependencies = [
"cfg-if",
"winapi",
]
[[package]]
name = "log"
version = "0.4.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
dependencies = [
"cfg-if",
]
[[package]]
name = "memchr"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b16bd47d9e329435e309c58469fe0791c2d0d1ba96ec0954152a5ae2b04387dc"
[[package]]
name = "nom"
version = "5.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ffb4262d26ed83a1c0a33a38fe2bb15797329c85770da05e6b828ddb782627af"
dependencies = [
"memchr",
"version_check",
]
[[package]]
name = "peeking_take_while"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b17cddbe7ec3f8bc800887bab5e717348c95ea2ca0b1bf0837fb964dc67099"
[[package]]
name = "proc-macro2"
version = "1.0.27"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0d8caf72986c1a598726adc988bb5984792ef84f5ee5aa50209145ee8077038"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d0b9745dc2debf507c8422de05d7226cc1f0644216dfdfead988f9b1ab32a7"
dependencies = [
"proc-macro2",
]
[[package]]
name = "regex"
version = "1.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d07a8629359eb56f1e2fb1652bb04212c072a87ba68546a04065d525673ac461"
dependencies = [
"aho-corasick",
"memchr",
"regex-syntax",
]
[[package]]
name = "regex-syntax"
version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rustc-hash"
version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2"
[[package]]
name = "shlex"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "42a568c8f2cd051a4d283bd6eb0343ac214c1b0f1ac19f93e1175b2dee38c73d"
[[package]]
name = "strsim"
version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a"
[[package]]
name = "termcolor"
version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2dfed899f0eb03f32ee8c6a0aabdb8a7949659e3466561fc0adf54e26d88c5f4"
dependencies = [
"winapi-util",
]
[[package]]
name = "textwrap"
version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d326610f408c7a4eb6f51c37c330e496b08506c9457c9d34287ecc38809fb060"
dependencies = [
"unicode-width",
]
[[package]]
name = "unicode-width"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9337591893a19b88d8d87f2cec1e73fad5cdfd10e5a6f349f498ad6ea2ffb1e3"
[[package]]
name = "unicode-xid"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "vec_map"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
[[package]]
name = "version_check"
version = "0.9.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fecdca9a5291cc2b8dcf7dc02453fee791a280f3743cb0905f8822ae463b3fe"
[[package]]
name = "which"
version = "3.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d011071ae14a2f6671d0b74080ae0cd8ebf3a6f8c9589a2cd45f23126fe29724"
dependencies = [
"libc",
]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-util"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70ec6ce85bb158151cae5e5c87f95a8e97d2c0c4b001223f33a334e3ce5de178"
dependencies = [
"winapi",
]
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"

19
src/rust/Cargo.toml Normal file
View File

@@ -0,0 +1,19 @@
[package]
name = "ccx_rust"
version = "0.1.0"
authors = ["PunitLodha <punitlodha@pm.me>"]
description = "Rust library for CCExtractor"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
crate-type = ["cdylib"]
[dependencies]
log = "0.4.0"
env_logger = "0.8.4"
iconv = "0.1.1"
[build-dependencies]
bindgen = "0.58.1"

47
src/rust/build.rs Normal file
View File

@@ -0,0 +1,47 @@
use std::env;
use std::path::PathBuf;
fn main() {
let mut builder = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header("wrapper.h")
.clang_arg("-DENABLE_RUST")
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks));
for type_name in ALLOWLIST_TYPES {
builder = builder.allowlist_type(type_name);
}
for fn_name in ALLOWLIST_FUNCTIONS {
builder = builder.allowlist_function(fn_name);
}
for rust_enum in RUSTIFIED_ENUMS {
builder = builder.rustified_enum(rust_enum);
}
let bindings = builder
// Finish the builder and generate the bindings.
.generate()
// Unwrap the Result and panic on failure.
.expect("Unable to generate bindings");
// Write the bindings to the $OUT_DIR/bindings.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("bindings.rs"))
.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

@@ -0,0 +1,186 @@
//! Different code sets as defined in CEA-708-E
//!
//! Refer section 7.1 CEA-708-E
//! Different code sets are:
//!
//! | Name | Description | Space |
//! | --- | --- | --- |
//! | C0 | Miscellaneous Control Codes | 0x00 to 0x1F |
//! | C2 | Extended Control Code Set 1 | 0x00 to 0x1F |
//! | G0 | ASCII Printable Characters | 0x20 to 0x7F |
//! | G2 | Extended Miscellaneous Characters | 0x20 to 0x7F |
//! | C1 | Captioning Command Control Codes | 0x80 to 0x9F |
//! | C3 | Extended Control Code Set 2 | 0x80 to 0x9F |
//! | G1 | ISO 8859-1 Latin-1 Character Set | 0xA0 to 0xFF |
//! | G3 | Future Expansion | 0xA0 to 0xFF |
/// Different C0 commands
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)]
pub enum C0CodeSet {
NUL,
ETX,
BS,
FF,
CR,
HCR,
EXT1,
P16,
RESERVED,
}
impl C0CodeSet {
/// Create a new command from the byte value
pub fn new(data: u8) -> Self {
match data {
0 => C0CodeSet::NUL,
0x3 => C0CodeSet::ETX,
0x8 => C0CodeSet::BS,
0xC => C0CodeSet::FF,
0xD => C0CodeSet::CR,
0xE => C0CodeSet::HCR,
0x10 => C0CodeSet::EXT1,
0x18 => C0CodeSet::P16,
_ => C0CodeSet::RESERVED,
}
}
}
/// C0 command with its length
pub struct C0Command {
pub command: C0CodeSet,
pub length: u8,
}
impl C0Command {
/// Create a new command from the byte value
pub fn new(data: u8) -> Self {
let command = C0CodeSet::new(data);
// lengths are defined in CEA-708-E section 7.1.4
let length = match data {
0..=0xF => 1,
0x10..=0x17 => 2,
0x18..=0x1F => 3,
// Unreachable arm as C0 code set is only from 0 - 0x1F
_ => 1,
};
C0Command { command, length }
}
}
/// Different C1 commands
#[allow(clippy::upper_case_acronyms)]
#[derive(Debug)]
pub enum C1CodeSet {
CW0,
CW1,
CW2,
CW3,
CW4,
CW5,
CW6,
CW7,
CLW,
DSW,
HDW,
TGW,
DLW,
DLY,
DLC,
RST,
SPA,
SPC,
SPL,
SWA,
DF0,
DF1,
DF2,
DF3,
DF4,
DF5,
DF6,
DF7,
RESERVED,
}
/// C1 command with its length
pub struct C1Command {
pub command: C1CodeSet,
pub length: u8,
pub name: String,
}
impl C1Command {
/// Create a new command from the byte value
pub fn new(data: u8) -> Self {
let (command, name, length) = match data {
0x80 => (C1CodeSet::CW0, "SetCurrentWindow0", 1),
0x81 => (C1CodeSet::CW1, "SetCurrentWindow1", 1),
0x82 => (C1CodeSet::CW2, "SetCurrentWindow2", 1),
0x83 => (C1CodeSet::CW3, "SetCurrentWindow3", 1),
0x84 => (C1CodeSet::CW4, "SetCurrentWindow4", 1),
0x85 => (C1CodeSet::CW5, "SetCurrentWindow5", 1),
0x86 => (C1CodeSet::CW6, "SetCurrentWindow6", 1),
0x87 => (C1CodeSet::CW7, "SetCurrentWindow7", 1),
0x88 => (C1CodeSet::CLW, "ClearWindows", 2),
0x89 => (C1CodeSet::DSW, "DisplayWindows", 2),
0x8A => (C1CodeSet::HDW, "HideWindows", 2),
0x8B => (C1CodeSet::TGW, "ToggleWindows", 2),
0x8C => (C1CodeSet::DLW, "DeleteWindows", 2),
0x8D => (C1CodeSet::DLY, "Delay", 2),
0x8E => (C1CodeSet::DLC, "DelayCancel", 1),
0x8F => (C1CodeSet::RST, "Reset", 1),
0x90 => (C1CodeSet::SPA, "SetPenAttributes", 3),
0x91 => (C1CodeSet::SPC, "SetPenColor", 4),
0x92 => (C1CodeSet::SPL, "SetPenLocation", 3),
0x97 => (C1CodeSet::SWA, "SetWindowAttributes", 5),
0x98 => (C1CodeSet::DF0, "DefineWindow0", 7),
0x99 => (C1CodeSet::DF1, "DefineWindow1", 7),
0x9A => (C1CodeSet::DF2, "DefineWindow2", 7),
0x9B => (C1CodeSet::DF3, "DefineWindow3", 7),
0x9C => (C1CodeSet::DF4, "DefineWindow4", 7),
0x9D => (C1CodeSet::DF5, "DefineWindow5", 7),
0x9E => (C1CodeSet::DF6, "DefineWindow6", 7),
0x9F => (C1CodeSet::DF7, "DefineWindow7", 7),
_ => (C1CodeSet::RESERVED, "Reserved", 1),
};
let name = name.to_owned();
Self {
command,
length,
name,
}
}
}
/// Handle C2 - Code Set - Extended Control Code Set 1
pub fn handle_C2(code: u8) -> u8 {
match code {
// ... Single-byte control bytes (0 additional bytes)
0..=0x07 => 1,
// ..two-byte control codes (1 additional byte)
0x08..=0x0F => 2,
// ..three-byte control codes (2 additional bytes)
0x10..=0x17 => 3,
// 18-1F => four-byte control codes (3 additional bytes)
_ => 4,
}
}
/// Handle C3 - Code Set - Extended Control Code Set 2
pub fn handle_C3(code: u8, next_code: u8) -> u8 {
match code {
// Five-byte control bytes (4 additional bytes)
0x80..=0x87 => 5,
// Six-byte control codes (5 additional byte)
0x88..=0x8F => 6,
// 90-9F variable length commands
// Refer Section 7.1.11.2
_ => {
// next code is the header which specifies additional bytes
let length = (next_code & 0x3F) + 1;
// + 1 for current code
length + 1
}
}
}

213
src/rust/src/decoder/mod.rs Normal file
View File

@@ -0,0 +1,213 @@
//! CEA 708 decoder
//!
//! This module provides a CEA 708 decoder as defined by ANSI/CTA-708-E R-2018
mod commands;
mod output;
mod service_decoder;
mod timing;
mod tv_screen;
mod window;
use crate::{bindings::*, utils::is_true};
use log::{debug, warn};
const CCX_DTVCC_MAX_PACKET_LENGTH: u8 = 128;
const CCX_DTVCC_NO_LAST_SEQUENCE: i32 = -1;
const CCX_DTVCC_SCREENGRID_ROWS: u8 = 75;
const CCX_DTVCC_SCREENGRID_COLUMNS: u8 = 210;
const CCX_DTVCC_MAX_ROWS: u8 = 15;
const CCX_DTVCC_MAX_COLUMNS: u8 = 32 * 2;
/// Stores the context required for processing 708 data
pub struct Dtvcc<'a> {
pub is_active: bool,
pub active_services_count: u8,
pub services_active: Vec<i32>,
pub report_enabled: bool,
pub report: &'a mut ccx_decoder_dtvcc_report,
pub decoders: Vec<&'a mut dtvcc_service_decoder>,
pub packet: Vec<u8>,
pub packet_length: u8,
pub is_header_parsed: bool,
pub last_sequence: i32,
pub encoder: &'a mut encoder_ctx,
pub no_rollup: bool,
pub timing: &'a mut ccx_common_timing_ctx,
}
impl<'a> Dtvcc<'a> {
/// Create a new dtvcc context
pub fn new(ctx: &'a mut dtvcc_ctx) -> Self {
let report = unsafe { &mut *ctx.report };
let encoder = unsafe { &mut *(ctx.encoder as *mut encoder_ctx) };
let timing = unsafe { &mut *ctx.timing };
Self {
is_active: is_true(ctx.is_active),
active_services_count: ctx.active_services_count as u8,
services_active: ctx.services_active.iter().copied().collect(),
report_enabled: is_true(ctx.report_enabled),
report,
decoders: ctx.decoders.iter_mut().collect(),
packet: ctx.current_packet.to_vec(),
packet_length: ctx.current_packet_length as u8,
is_header_parsed: is_true(ctx.is_current_packet_header_parsed),
last_sequence: ctx.last_sequence,
encoder,
no_rollup: is_true(ctx.no_rollup),
timing,
}
}
/// Process cc data to generate dtvcc packet
pub fn process_cc_data(&mut self, cc_valid: u8, cc_type: u8, data1: u8, data2: u8) {
if !self.is_active && !self.report_enabled {
return;
}
match cc_type {
// type 0 and 1 are for CEA 608 data and are handled before calling this function
// valid types for CEA 708 data are only 2 and 3
2 => {
debug!("dtvcc_process_data: DTVCC Channel Packet Data");
if cc_valid == 1 && self.is_header_parsed {
if self.packet_length > 253 {
warn!("dtvcc_process_data: Warning: Legal packet size exceeded (1), data not added.");
} else {
self.add_data_to_packet(data1, data2);
let mut max_len = self.packet[0] & 0x3F;
if max_len == 0 {
// This is well defined in EIA-708; no magic.
max_len = 128;
} else {
max_len *= 2;
}
if self.packet_length >= max_len {
self.process_current_packet(max_len);
}
}
}
}
3 => {
debug!("dtvcc_process_data: DTVCC Channel Packet Start");
if cc_valid == 1 {
if self.packet_length > (CCX_DTVCC_MAX_PACKET_LENGTH - 1) {
warn!("dtvcc_process_data: Warning: Legal packet size exceeded (2), data not added.");
} else {
self.add_data_to_packet(data1, data2);
self.is_header_parsed = true;
}
}
}
_ => warn!(
"dtvcc_process_data: shouldn't be here - cc_type: {}",
cc_type
),
}
}
pub fn add_data_to_packet(&mut self, data1: u8, data2: u8) {
self.packet[self.packet_length as usize] = data1;
self.packet_length += 1;
self.packet[self.packet_length as usize] = data2;
self.packet_length += 1;
}
/// Process current packet into service blocks
pub fn process_current_packet(&mut self, len: u8) {
let seq = (self.packet[0] & 0xC0) >> 6;
debug!(
"dtvcc_process_current_packet: Sequence: {}, packet length: {}",
seq, len
);
if self.packet_length == 0 {
return;
}
if self.last_sequence != CCX_DTVCC_NO_LAST_SEQUENCE
&& (self.last_sequence + 1) % 4 != seq as i32
{
warn!("dtvcc_process_current_packet: Unexpected sequence number, it is {} but should be {}", seq, (self.last_sequence +1) % 4);
}
self.last_sequence = seq as i32;
let mut pos: u8 = 1;
while pos < len {
let mut service_number = (self.packet[pos as usize] & 0xE0) >> 5; // 3 more significant bits
let block_length = self.packet[pos as usize] & 0x1F; // 5 less significant bits
debug!("dtvcc_process_current_packet: Standard header Service number: {}, Block length: {}", service_number, block_length);
if service_number == 7 {
// There is an extended header
// CEA-708-E 6.2.2 Extended Service Block Header
pos += 1;
service_number = self.packet[pos as usize] & 0x3F; // 6 more significant bits
if service_number > 7 {
warn!("dtvcc_process_current_packet: Illegal service number in extended header: {}", service_number);
}
}
pos += 1;
if service_number == 0 && block_length != 0 {
// Illegal, but specs say what to do...
pos = len; // Move to end
break;
}
if block_length != 0 {
self.report.services[service_number as usize] = 1;
}
if service_number > 0 && is_true(self.services_active[(service_number - 1) as usize]) {
let decoder = &mut self.decoders[(service_number - 1) as usize];
decoder.process_service_block(
&self.packet[pos as usize..(pos + block_length) as usize],
self.encoder,
self.timing,
self.no_rollup,
);
}
pos += block_length // Skip data
}
self.clear_packet();
if len < 128 && self.packet[pos as usize] != 0 {
// Null header is mandatory if there is room
warn!("dtvcc_process_current_packet: Warning: Null header expected but not found.");
}
}
/// Clear current packet
pub fn clear_packet(&mut self) {
self.packet_length = 0;
self.is_header_parsed = false;
self.packet.iter_mut().for_each(|x| *x = 0);
}
}
impl dtvcc_symbol {
/// Create a new symbol
pub fn new(sym: u16) -> Self {
Self { init: 1, sym }
}
/// Create a new 16 bit symbol
pub fn new_16(data1: u8, data2: u8) -> Self {
let sym = (data1 as u16) << 8 | data2 as u16;
Self { init: 1, sym }
}
/// Check if symbol is initialized
pub fn is_set(&self) -> bool {
is_true(self.init)
}
}
impl Default for dtvcc_symbol {
/// Create a blank uninitialized symbol
fn default() -> Self {
Self { sym: 0, init: 0 }
}
}

View File

@@ -0,0 +1,81 @@
#[cfg(windows)]
use std::os::windows::io::{FromRawHandle, IntoRawHandle, RawHandle};
use std::{
fs::File,
io::Write,
os::unix::prelude::{FromRawFd, IntoRawFd},
};
use crate::{bindings::*, utils::is_true};
use log::debug;
// Context for writing subtitles to file
pub struct Writer<'a> {
pub cea_708_counter: &'a mut u32,
pub subs_delay: LLONG,
pub crlf: String,
pub write_format: ccx_output_format,
pub writer_ctx: &'a mut dtvcc_writer_ctx,
pub no_font_color: bool,
pub transcript_settings: &'a ccx_encoders_transcript_format,
pub no_bom: i32,
}
impl<'a> Writer<'a> {
pub fn new(
cea_708_counter: &'a mut u32,
subs_delay: LLONG,
write_format: ccx_output_format,
writer_ctx: &'a mut dtvcc_writer_ctx,
no_font_color: i32,
transcript_settings: &'a ccx_encoders_transcript_format,
no_bom: i32,
) -> Self {
Self {
cea_708_counter,
subs_delay,
crlf: "\r\n".to_owned(),
write_format,
writer_ctx,
no_font_color: is_true(no_font_color),
transcript_settings,
no_bom,
}
}
pub fn write_to_file(&mut self, buf: &[u8]) -> Result<(), String> {
#[cfg(unix)]
{
let mut file = unsafe { File::from_raw_fd(self.writer_ctx.fd) };
file.write_all(buf).map_err(|err| err.to_string())?;
self.writer_ctx.fd = file.into_raw_fd();
}
#[cfg(windows)]
{
let mut file = unsafe { File::from_raw_handle(self.writer_ctx.fhandle) };
file.write_all(buf).map_err(|err| err.to_string())?;
self.writer_ctx.fhandle = file.into_raw_handle();
}
Ok(())
}
}
pub fn write_char(sym: &dtvcc_symbol, buf: &mut Vec<u8>) {
if sym.sym >> 8 != 0 {
buf.push((sym.sym >> 8) as u8);
buf.push((sym.sym & 0xff) as u8);
} else {
buf.push(sym.sym as u8);
}
}
pub fn color_to_hex(color: u8) -> (u8, u8, u8) {
let red = color >> 4;
let green = (color >> 2) & 0x3;
let blue = color & 0x3;
debug!(
"Color: {} [{:06x}] {} {} {}",
color, color, red, green, blue
);
(red, green, blue)
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,43 @@
use crate::{bindings::*, cb_708, cb_field1, cb_field2};
use log::{debug, error};
impl ccx_common_timing_ctx {
pub fn get_fts(&self, current_field: u8) -> LLONG {
unsafe {
match current_field {
1 => self.fts_now + self.fts_global + cb_field1 as i64 * 1001 / 30,
2 => self.fts_now + self.fts_global + cb_field2 as i64 * 1001 / 30,
3 => self.fts_now + self.fts_global + cb_708 as i64 * 1001 / 30,
_ => {
error!("get_fts: Unknown field");
0
}
}
}
}
pub fn get_visible_end(&mut self, current_field: u8) -> LLONG {
let fts = self.get_fts(current_field);
if fts > self.minimum_fts {
self.minimum_fts = fts;
}
debug!("Visible End time={}", get_time_str(fts));
fts
}
pub fn get_visible_start(self, current_field: u8) -> LLONG {
let mut fts = self.get_fts(current_field);
if fts <= self.minimum_fts {
fts = self.minimum_fts + 1;
}
debug!("Visible Start time={}", get_time_str(fts));
fts
}
}
pub fn get_time_str(time: LLONG) -> String {
let hh = time / 1000 / 60 / 60;
let mm = time / 1000 / 60 - 60 * hh;
let ss = time / 1000 - 60 * (mm + 60 * hh);
let ms = time - 1000 * (ss + 60 * (mm + 60 * hh));
format!("{:02}:{:02}:{:02},{:03}", hh, mm, ss, ms)
}

View File

@@ -0,0 +1,431 @@
#[cfg(windows)]
use std::os::windows::io::IntoRawHandle;
use std::{ffi::CStr, fs::File, os::unix::prelude::IntoRawFd};
use super::output::{color_to_hex, write_char, Writer};
use super::timing::get_time_str;
use super::{CCX_DTVCC_SCREENGRID_COLUMNS, CCX_DTVCC_SCREENGRID_ROWS};
use crate::{
bindings::*,
utils::{is_false, is_true},
};
use log::{debug, warn};
impl dtvcc_tv_screen {
/// Clear text from tv screen
pub fn clear(&mut self) {
for row in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
self.chars[row].fill(dtvcc_symbol::default());
}
self.time_ms_hide = -1;
self.time_ms_show = -1;
}
pub fn update_time_show(&mut self, time: LLONG) {
let prev_time_str = get_time_str(self.time_ms_show);
let curr_time_str = get_time_str(time);
debug!("Screen show time: {} -> {}", prev_time_str, curr_time_str);
if self.time_ms_show == -1 || self.time_ms_show > time {
self.time_ms_show = time;
}
}
pub fn update_time_hide(&mut self, time: LLONG) {
let prev_time_str = get_time_str(self.time_ms_hide);
let curr_time_str = get_time_str(time);
debug!("Screen hide time: {} -> {}", prev_time_str, curr_time_str);
if self.time_ms_hide == -1 || self.time_ms_hide < time {
self.time_ms_hide = time;
}
}
pub fn writer_output(&self, writer: &mut Writer) -> Result<(), String> {
debug!(
"dtvcc_writer_output: writing... [{:?}][{}]",
unsafe { CStr::from_ptr(writer.writer_ctx.filename) },
writer.writer_ctx.fd
);
#[cfg(unix)]
if writer.writer_ctx.filename.is_null() && writer.writer_ctx.fd < 0 {
return Err("Filename missing".to_owned());
} else if writer.writer_ctx.fd < 0 {
let filename = unsafe {
CStr::from_ptr(writer.writer_ctx.filename)
.to_str()
.map_err(|err| err.to_string())
}?;
debug!("dtvcc_writer_output: creating {}", filename);
let file = File::create(filename).map_err(|err| err.to_string())?;
if is_false(writer.no_bom) {
let BOM = [0xef, 0xbb, 0xbf];
writer.write_to_file(&BOM)?;
}
writer.writer_ctx.fd = file.into_raw_fd();
}
#[cfg(windows)]
if writer.writer_ctx.filename.is_null() && writer.fhandle.is_null() {
return Err("Filename missing".to_owned())?;
} else if writer.fhandle.is_null() {
let filename = unsafe {
CStr::from_ptr(writer.writer_ctx.filename)
.to_str()
.map_err(|err| err.to_string())
}?;
debug!("dtvcc_writer_output: creating {}", filename);
let file = File::create(filename).map_err(|err| err.to_string())?;
if is_false(writer.no_bom) {
let BOM = [0xef, 0xbb, 0xbf];
writer.write_to_file(&BOM)?;
}
writer.fhandle = file.into_raw_handle();
}
self.write(writer);
Ok(())
}
pub fn get_write_interval(&self, row_index: usize) -> (usize, usize) {
let mut first = 0;
let mut last = CCX_DTVCC_SCREENGRID_COLUMNS as usize - 1;
for col in 0..CCX_DTVCC_SCREENGRID_COLUMNS as usize {
if self.chars[row_index][col].is_set() {
first = col;
break;
}
}
for col in (0..(CCX_DTVCC_SCREENGRID_COLUMNS as usize - 1)).rev() {
if self.chars[row_index][col].is_set() {
last = col;
break;
}
}
(first, last)
}
pub fn write(&self, writer: &mut Writer) {
let result = match writer.write_format {
ccx_output_format::CCX_OF_SRT => self.write_srt(writer),
ccx_output_format::CCX_OF_SAMI => self.write_sami(writer),
ccx_output_format::CCX_OF_TRANSCRIPT => self.write_transcript(writer),
_ => {
self.write_debug();
Err("Unsupported write format".to_owned())
}
};
if let Err(err) = result {
warn!("{}", err);
}
}
pub fn write_row(
&self,
writer: &mut Writer,
row_index: usize,
use_colors: bool,
) -> Result<(), String> {
let mut buf = Vec::new();
let mut pen_color = dtvcc_pen_color::default();
let mut pen_attribs = dtvcc_pen_attribs::default();
let (first, last) = self.get_write_interval(row_index);
debug!("First: {}, Last: {}", first, last);
for i in 0..last + 1 {
if use_colors {
self.change_pen_color(
&pen_color,
writer.no_font_color,
row_index,
i,
false,
&mut buf,
);
}
self.change_pen_attribs(
&pen_attribs,
writer.no_font_color,
row_index,
i,
false,
&mut buf,
);
self.change_pen_attribs(
&pen_attribs,
writer.no_font_color,
row_index,
i,
true,
&mut buf,
);
if use_colors {
self.change_pen_color(
&pen_color,
writer.no_font_color,
row_index,
i,
true,
&mut buf,
)
}
pen_color = self.pen_colors[row_index][i];
pen_attribs = self.pen_attribs[row_index][i];
if i < first {
buf.push(b' ');
} else {
write_char(&self.chars[row_index][i], &mut buf)
}
}
// there can be unclosed tags or colors after the last symbol in a row
if use_colors {
self.change_pen_color(
&pen_color,
writer.no_font_color,
row_index,
CCX_DTVCC_SCREENGRID_COLUMNS as usize,
false,
&mut buf,
)
}
self.change_pen_attribs(
&pen_attribs,
writer.no_font_color,
row_index,
CCX_DTVCC_SCREENGRID_COLUMNS as usize,
false,
&mut buf,
);
// Tags can still be crossed e.g <f><i>text</f></i>, but testing HTML code has shown that they still are handled correctly.
if writer.writer_ctx.cd != (-1_isize) as iconv_t {
if writer.writer_ctx.charset.is_null() {
debug!("Charset: null");
} else {
let charset = unsafe {
CStr::from_ptr(writer.writer_ctx.charset)
.to_str()
.map_err(|err| err.to_string())?
};
debug!("Charset: {}", charset);
let op = iconv::decode(&buf, charset).map_err(|err| err.to_string())?;
writer.write_to_file(op.as_bytes())?;
}
} else {
writer.write_to_file(&buf)?;
}
Ok(())
}
pub fn write_srt(&self, writer: &mut Writer) -> Result<(), String> {
if self.is_screen_empty(writer) {
return Ok(());
}
if self.time_ms_show + writer.subs_delay < 0 {
return Ok(());
}
let time_show = get_time_str(self.time_ms_show);
let time_hide = get_time_str(self.time_ms_hide);
let counter = *writer.cea_708_counter;
let line = format!(
"{}{}{} --> {}{}",
counter, "\r\n", time_show, time_hide, "\r\n"
);
writer.write_to_file(line.as_bytes())?;
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
self.write_row(writer, row_index, true)?;
writer.write_to_file(b"\r\n")?;
}
}
writer.write_to_file(b"\r\n")?;
Ok(())
}
pub fn write_transcript(&self, writer: &mut Writer) -> Result<(), String> {
if self.is_screen_empty(writer) {
return Ok(());
}
if self.time_ms_show + writer.subs_delay < 0 {
return Ok(());
}
let time_show = get_time_str(self.time_ms_show);
let time_hide = get_time_str(self.time_ms_hide);
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
let mut buf = String::new();
if is_true(writer.transcript_settings.showStartTime) {
buf.push_str(&time_show);
buf.push('|');
}
if is_true(writer.transcript_settings.showEndTime) {
buf.push_str(&time_hide);
buf.push('|');
}
if is_true(writer.transcript_settings.showCC) {
buf.push_str("CC1|"); //always CC1 because CEA-708 is field-independent
}
if is_true(writer.transcript_settings.showMode) {
buf.push_str("POP|"); //TODO caption mode(pop, rollup, etc.)
}
writer.write_to_file(buf.as_bytes())?;
self.write_row(writer, row_index, false)?;
writer.write_to_file(b"\r\n")?;
}
}
Ok(())
}
pub fn write_sami(&self, writer: &mut Writer) -> Result<(), String> {
if self.is_screen_empty(writer) {
return Err("Sami:- Screen is empty".to_owned());
}
if self.time_ms_show + writer.subs_delay < 0 {
return Err(format!(
"Sami:- timing is -ve, {}:{}",
self.time_ms_show, writer.subs_delay
));
}
if self.cc_count == 1 {
self.write_sami_header(writer)?;
}
let buf = format!(
"<sync start={}><p class=\"unknowncc\">\r\n",
self.time_ms_show + writer.subs_delay
);
writer.write_to_file(buf.as_bytes())?;
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
self.write_row(writer, row_index, true)?;
writer.write_to_file("<br>\r\n".as_bytes())?;
}
}
let buf = format!(
"<sync start={}><p class=\"unknowncc\">&nbsp;</p></sync>\r\n\r\n",
self.time_ms_hide + writer.subs_delay
);
writer.write_to_file(buf.as_bytes())?;
Ok(())
}
pub fn write_sami_header(&self, writer: &mut Writer) -> Result<(), String> {
let mut buf = String::new();
buf.push_str("<sami>\r\n");
buf.push_str("<head>\r\n");
buf.push_str("<style type=\"text/css\">\r\n");
buf.push_str("<!--\r\n");
buf.push_str(
"p {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;\r\n",
);
buf.push_str("text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}\r\n");
buf.push_str(".unknowncc {Name:Unknown; lang:en-US; SAMIType:CC;}\r\n");
buf.push_str("-->\r\n");
buf.push_str("</style>\r\n");
buf.push_str("</head>\r\n\r\n");
buf.push_str("<body>\r\n");
writer.write_to_file(buf.as_bytes())?;
Ok(())
}
pub fn write_debug(&self) {
let time_show = get_time_str(self.time_ms_show);
let time_hide = get_time_str(self.time_ms_hide);
debug!("{} --> {}", time_show, time_hide);
for row_index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(row_index) {
let mut buf = String::new();
let (first, last) = self.get_write_interval(row_index);
for sym in self.chars[row_index][first..=last].iter() {
buf.push_str(&format!("{:04X},", sym.sym));
}
debug!("{}", buf);
}
}
}
pub fn is_screen_empty(&self, writer: &mut Writer) -> bool {
for index in 0..CCX_DTVCC_SCREENGRID_ROWS as usize {
if !self.is_row_empty(index) {
// we will write subtitle
*writer.cea_708_counter += 1;
return false;
}
}
true
}
pub fn is_row_empty(&self, row_index: usize) -> bool {
for col_index in 0..CCX_DTVCC_SCREENGRID_COLUMNS as usize {
if self.chars[row_index][col_index].is_set() {
return false;
}
}
true
}
pub fn change_pen_attribs(
&self,
pen_attribs: &dtvcc_pen_attribs,
no_font_color: bool,
row_index: usize,
col_index: usize,
open: bool,
buf: &mut Vec<u8>,
) {
if no_font_color {
return;
}
let new_pen_attribs = if col_index >= CCX_DTVCC_SCREENGRID_COLUMNS as usize {
dtvcc_pen_attribs::default()
} else {
self.pen_attribs[row_index][col_index]
};
if pen_attribs.italic != new_pen_attribs.italic {
if is_true(pen_attribs.italic) && !open {
buf.extend_from_slice(b"</i>");
} else if is_false(pen_attribs.italic) && open {
buf.extend_from_slice(b"<i>");
}
}
if pen_attribs.underline != new_pen_attribs.underline {
if is_true(pen_attribs.underline) && !open {
buf.extend_from_slice(b"</u>");
} else if is_false(pen_attribs.underline) && open {
buf.extend_from_slice(b"<u>");
}
}
}
pub fn change_pen_color(
&self,
pen_color: &dtvcc_pen_color,
no_font_color: bool,
row_index: usize,
col_index: usize,
open: bool,
buf: &mut Vec<u8>,
) {
if no_font_color {
return;
}
let new_pen_color = if col_index >= CCX_DTVCC_SCREENGRID_COLUMNS as usize {
dtvcc_pen_color::default()
} else {
self.pen_colors[row_index][col_index]
};
if pen_color.fg_color != new_pen_color.fg_color {
if pen_color.fg_color != 0x3F && !open {
// should close older non-white color
buf.extend_from_slice(b"</font>");
} else if new_pen_color.fg_color != 0x3F && open {
debug!("Colors: {}", col_index);
let (mut red, mut green, mut blue) = color_to_hex(new_pen_color.fg_color as u8);
red *= 255 / 3;
green *= 255 / 3;
blue *= 255 / 3;
let font_tag = format!("<font color=\"{:02x}{:02x}{:02x}\">", red, green, blue);
buf.extend_from_slice(font_tag.as_bytes());
}
}
}
}

View File

@@ -0,0 +1,542 @@
use std::{
alloc::{alloc_zeroed, dealloc, Layout},
intrinsics::copy_nonoverlapping,
};
use super::timing::get_time_str;
use super::{
CCX_DTVCC_MAX_COLUMNS, CCX_DTVCC_MAX_ROWS, CCX_DTVCC_SCREENGRID_COLUMNS,
CCX_DTVCC_SCREENGRID_ROWS,
};
use crate::{bindings::*, utils::is_true};
use log::{debug, error};
impl dtvcc_window {
pub fn set_style(&mut self, preset: WindowPreset) {
let style_id = preset as i32;
let window_style = WindowStyle::new(preset);
self.win_style = style_id;
self.attribs.border_color = window_style.border_color as i32;
self.attribs.border_type = window_style.border_type as i32;
self.attribs.display_effect = window_style.display_effect as i32;
self.attribs.effect_direction = window_style.effect_direction as i32;
self.attribs.effect_speed = window_style.effect_speed as i32;
self.attribs.fill_color = window_style.fill_color as i32;
self.attribs.fill_opacity = window_style.fill_opacity as i32;
self.attribs.justify = window_style.justify as i32;
self.attribs.print_direction = window_style.print_direction as i32;
self.attribs.scroll_direction = window_style.scroll_direction as i32;
self.attribs.word_wrap = window_style.word_wrap as i32;
}
pub fn set_pen_style(&mut self, preset: PenPreset) {
let pen_style = PenStyle::new(preset);
let pen = &mut self.pen_attribs_pattern;
pen.pen_size = pen_style.pen_size as i32;
pen.offset = pen_style.offset as i32;
pen.edge_type = pen_style.edge_type as i32;
pen.underline = pen_style.underline as i32;
pen.italic = pen_style.italics as i32;
let pen_color = &mut self.pen_color_pattern;
pen_color.fg_color = pen_style.color.fg_color as i32;
pen_color.fg_opacity = pen_style.color.fg_opacity as i32;
pen_color.bg_color = pen_style.color.bg_color as i32;
pen_color.bg_opacity = pen_style.color.bg_opacity as i32;
pen_color.edge_color = pen_style.color.edge_color as i32;
}
pub fn update_time_show(&mut self, timing: &mut ccx_common_timing_ctx) {
self.time_ms_show = timing.get_visible_start(3);
let time = get_time_str(self.time_ms_show);
debug!("[W-{}] show time updated to {}", self.number, time);
}
pub fn update_time_hide(&mut self, timing: &mut ccx_common_timing_ctx) {
self.time_ms_hide = timing.get_visible_end(3);
let time = get_time_str(self.time_ms_hide);
debug!("[W-{}] hide time updated to {}", self.number, time);
}
pub fn get_dimensions(&self) -> Result<(i32, i32, i32, i32), String> {
let anchor = dtvcc_pen_anchor_point::new(self.anchor_point)?;
let (mut x1, mut x2, mut y1, mut y2) = match anchor {
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_LEFT => (
self.anchor_vertical,
self.anchor_vertical + self.row_count,
self.anchor_horizontal,
self.anchor_horizontal + self.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_CENTER => (
self.anchor_vertical,
self.anchor_vertical + self.row_count,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal + self.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_RIGHT => (
self.anchor_vertical,
self.anchor_vertical + self.row_count,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_LEFT => (
self.anchor_vertical - self.row_count / 2,
self.anchor_vertical + self.row_count / 2,
self.anchor_horizontal,
self.anchor_horizontal + self.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_CENTER => (
self.anchor_vertical - self.row_count / 2,
self.anchor_vertical + self.row_count / 2,
self.anchor_horizontal - self.col_count / 2,
self.anchor_horizontal + self.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_RIGHT => (
self.anchor_vertical - self.row_count / 2,
self.anchor_vertical + self.row_count / 2,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_LEFT => (
self.anchor_vertical - self.row_count,
self.anchor_vertical,
self.anchor_horizontal,
self.anchor_horizontal + self.col_count,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_CENTER => (
self.anchor_vertical - self.row_count,
self.anchor_vertical,
self.anchor_horizontal - self.col_count / 2,
self.anchor_horizontal + self.col_count / 2,
),
dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_RIGHT => (
self.anchor_vertical - self.row_count,
self.anchor_vertical,
self.anchor_horizontal - self.col_count,
self.anchor_horizontal,
),
};
if x1 < 0 {
x1 = 0
}
if y1 < 0 {
y1 = 0
}
if x2 > CCX_DTVCC_SCREENGRID_ROWS as i32 {
x2 = CCX_DTVCC_SCREENGRID_ROWS as i32
}
if y2 > CCX_DTVCC_SCREENGRID_COLUMNS as i32 {
y2 = CCX_DTVCC_SCREENGRID_COLUMNS as i32
}
Ok((x1, x2, y1, y2))
}
pub fn clear_text(&mut self) {
// Set pen color to default value
self.pen_color_pattern = dtvcc_pen_color::default();
// Set pen attributes to default value
self.pen_attribs_pattern = dtvcc_pen_attribs::default();
for row in 0..CCX_DTVCC_MAX_ROWS as usize {
self.clear_row(row);
}
self.is_empty = 1;
}
pub fn clear_row(&mut self, row_index: usize) {
if is_true(self.memory_reserved) {
unsafe {
let layout = Layout::array::<dtvcc_symbol>(CCX_DTVCC_MAX_COLUMNS as usize);
if let Err(e) = layout {
error!("clear_row: Incorrect Layout, {}", e);
} else {
let layout = layout.unwrap();
// deallocate previous memory
dealloc(self.rows[row_index] as *mut u8, layout);
// allocate new zero initialized memory
let ptr = alloc_zeroed(layout);
if ptr.is_null() {
error!("clear_row: Not enough memory",);
}
self.rows[row_index] = ptr as *mut dtvcc_symbol;
}
}
for col in 0..CCX_DTVCC_MAX_COLUMNS as usize {
// Set pen attributes to default value
self.pen_attribs[row_index][col] = dtvcc_pen_attribs {
pen_size: dtvcc_pen_size::DTVCC_PEN_SIZE_STANDART as i32,
offset: 0,
text_tag: dtvcc_pen_text_tag::DTVCC_PEN_TEXT_TAG_UNDEFINED_12 as i32,
font_tag: 0,
edge_type: dtvcc_pen_edge::DTVCC_PEN_EDGE_NONE as i32,
underline: 0,
italic: 0,
};
// Set pen color to default value
self.pen_colors[row_index][col] = dtvcc_pen_color {
fg_color: 0x3F,
fg_opacity: 0,
bg_color: 0,
bg_opacity: 0,
edge_color: 0,
};
}
}
}
pub fn rollup(&mut self) {
debug!("roller");
for row_index in 0..(self.row_count - 1) as usize {
let curr_row = self.rows[row_index];
let next_row = self.rows[row_index + 1] as *const dtvcc_symbol;
unsafe { copy_nonoverlapping(next_row, curr_row, CCX_DTVCC_MAX_COLUMNS as usize) };
for col_index in 0..CCX_DTVCC_MAX_COLUMNS as usize {
self.pen_colors[row_index][col_index] = self.pen_colors[row_index + 1][col_index];
self.pen_attribs[row_index][col_index] = self.pen_attribs[row_index + 1][col_index];
}
}
self.clear_row((self.row_count - 1) as usize);
}
}
impl dtvcc_window_pd {
pub fn new(direction: i32) -> Result<Self, String> {
match direction {
0 => Ok(dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT),
1 => Ok(dtvcc_window_pd::DTVCC_WINDOW_PD_RIGHT_LEFT),
2 => Ok(dtvcc_window_pd::DTVCC_WINDOW_PD_TOP_BOTTOM),
3 => Ok(dtvcc_window_pd::DTVCC_WINDOW_PD_BOTTOM_TOP),
_ => Err(String::from("Invalid print direction")),
}
}
}
impl dtvcc_pen_anchor_point {
pub fn new(anchor: i32) -> Result<Self, String> {
match anchor {
0 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_LEFT),
1 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_CENTER),
2 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_TOP_RIGHT),
3 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_LEFT),
4 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_CENTER),
5 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_MIDDLE_RIGHT),
6 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_LEFT),
7 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_CENTER),
8 => Ok(dtvcc_pen_anchor_point::DTVCC_ANCHOR_POINT_BOTTOM_RIGHT),
_ => Err(String::from("Invalid pen anchor")),
}
}
}
/// Window style for a specific window preset
struct WindowStyle {
justify: dtvcc_window_justify,
print_direction: dtvcc_window_pd,
scroll_direction: dtvcc_window_sd,
/// Either 0 or 1
word_wrap: u8,
/// always snap
display_effect: dtvcc_window_sde,
/// N/A always 0
effect_direction: u8,
/// N/A always 0
effect_speed: u8,
/// Either N/A or black, still always 0
fill_color: u8,
fill_opacity: dtvcc_window_fo,
/// always border_none
border_type: dtvcc_window_border,
/// N/A always 0
border_color: u8,
}
impl WindowStyle {
pub fn new(preset: WindowPreset) -> Self {
let effect_direction = 0;
let effect_speed = 0;
let fill_color = 0;
let border_color = 0;
let display_effect = dtvcc_window_sde::DTVCC_WINDOW_SDE_SNAP;
let border_type = dtvcc_window_border::DTVCC_WINDOW_BORDER_NONE;
match preset {
WindowPreset::NtscPopup => Self {
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
word_wrap: 0,
display_effect,
effect_direction,
effect_speed,
fill_color,
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
border_type,
border_color,
},
WindowPreset::Popup => Self {
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
word_wrap: 0,
display_effect,
effect_direction,
effect_speed,
fill_color,
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_TRANSPARENT,
border_type,
border_color,
},
WindowPreset::NtscCenteredPopup => Self {
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_CENTER,
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
word_wrap: 0,
display_effect,
effect_direction,
effect_speed,
fill_color,
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
border_type,
border_color,
},
WindowPreset::NtscRollup => Self {
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
word_wrap: 1,
display_effect,
effect_direction,
effect_speed,
fill_color,
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
border_type,
border_color,
},
WindowPreset::Rollup => Self {
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
word_wrap: 1,
display_effect,
effect_direction,
effect_speed,
fill_color,
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_TRANSPARENT,
border_type,
border_color,
},
WindowPreset::NtscCenteredRollup => Self {
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_CENTER,
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_LEFT_RIGHT,
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_BOTTOM_TOP,
word_wrap: 1,
display_effect,
effect_direction,
effect_speed,
fill_color,
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
border_type,
border_color,
},
WindowPreset::TickerTape => Self {
justify: dtvcc_window_justify::DTVCC_WINDOW_JUSTIFY_LEFT,
print_direction: dtvcc_window_pd::DTVCC_WINDOW_PD_TOP_BOTTOM,
scroll_direction: dtvcc_window_sd::DTVCC_WINDOW_SD_RIGHT_LEFT,
word_wrap: 0,
display_effect,
effect_direction,
effect_speed,
fill_color,
fill_opacity: dtvcc_window_fo::DTVCC_WINDOW_FO_SOLID,
border_type,
border_color,
},
}
}
}
/// Predefined window style ids
#[derive(Clone, Copy)]
pub enum WindowPreset {
/// #1 NTSC Style Popup Captions
NtscPopup = 1,
/// #2 Popup Captions w/o Black Background
Popup = 2,
/// #3 NTSC Style Centered Popup Captions
NtscCenteredPopup = 3,
/// #4 NTSC Style Rollup Captions
NtscRollup = 4,
/// #4 Rollup Captions w/o Black Background
Rollup = 5,
/// #6 NTSC Style Centered Rollup Captions
NtscCenteredRollup = 6,
/// #7 Ticker Tape
TickerTape = 7,
}
impl WindowPreset {
pub fn get_style(style_id: u8) -> Result<Self, String> {
match style_id {
1 => Ok(WindowPreset::NtscPopup),
2 => Ok(WindowPreset::Popup),
3 => Ok(WindowPreset::NtscCenteredPopup),
4 => Ok(WindowPreset::NtscRollup),
5 => Ok(WindowPreset::Rollup),
6 => Ok(WindowPreset::NtscCenteredRollup),
7 => Ok(WindowPreset::TickerTape),
_ => Err("Invalid style".to_owned()),
}
}
}
#[derive(PartialEq)]
pub enum PenPreset {
/// #1 Default NTSC Style
NtscStyle,
/// #2 NTSC Style Mono w/ Serif
NtscStyleMonoSerif,
/// #3 NTSC Style Prop w/ Serif
NtscStylePropSerif,
/// #4 NTSC Style Mono w/o Serif
NtscStyleMono,
/// #5 NTSC Style Prop w/o Serif
NtscStyleProp,
/// #6 Mono w/o Serif, Bordered Text, No bg
MonoBordered,
/// #7 Prop w/o Serif, Bordered Text, No bg
PropBordered,
}
impl PenPreset {
pub fn get_style(style_id: u8) -> Result<Self, String> {
match style_id {
1 => Ok(PenPreset::NtscStyle),
2 => Ok(PenPreset::NtscStyleMonoSerif),
3 => Ok(PenPreset::NtscStylePropSerif),
4 => Ok(PenPreset::NtscStyleMono),
5 => Ok(PenPreset::NtscStyleProp),
6 => Ok(PenPreset::MonoBordered),
7 => Ok(PenPreset::PropBordered),
_ => Err("Invalid style".to_owned()),
}
}
}
pub struct PenStyle {
/// always standard pen size
pen_size: dtvcc_pen_size,
/// Font style, ranged from 1-7
/// Not being used current in the C code(bindings)
_font_style: dtvcc_pen_font_style,
offset: dtvcc_pen_offset,
/// always no, i.e. 0
italics: u8,
/// always no, i.e. 0
underline: u8,
edge_type: dtvcc_pen_edge,
color: PenColor,
}
impl PenStyle {
pub fn new(preset: PenPreset) -> Self {
let pen_size = dtvcc_pen_size::DTVCC_PEN_SIZE_STANDART;
let offset = dtvcc_pen_offset::DTVCC_PEN_OFFSET_NORMAL;
let italics = 0;
let underline = 0;
let bg_opacity = match preset {
PenPreset::MonoBordered | PenPreset::PropBordered => Opacity::Transparent,
_ => Opacity::Solid,
};
let color = PenColor {
/// White(2,2,2) i.e 10,10,10 i.e 42
fg_color: 42,
fg_opacity: Opacity::Solid,
/// Either N/A or black, still always 0
bg_color: 0,
bg_opacity,
/// Either N/A or black, still always 0
edge_color: 0,
};
let (font_style, edge_type) = match preset {
PenPreset::NtscStyle => (
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_DEFAULT_OR_UNDEFINED,
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
),
PenPreset::NtscStyleMonoSerif => (
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_MONOSPACED_WITH_SERIFS,
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
),
PenPreset::NtscStylePropSerif => (
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITH_SERIFS,
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
),
PenPreset::NtscStyleMono => (
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS,
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
),
PenPreset::NtscStyleProp => (
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS,
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
),
PenPreset::MonoBordered => (
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_MONOSPACED_WITHOUT_SERIFS,
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
),
PenPreset::PropBordered => (
dtvcc_pen_font_style::DTVCC_PEN_FONT_STYLE_PROPORTIONALLY_SPACED_WITHOUT_SERIFS,
dtvcc_pen_edge::DTVCC_PEN_EDGE_UNIFORM,
),
};
Self {
pen_size,
_font_style: font_style,
offset,
italics,
underline,
edge_type,
color,
}
}
}
struct PenColor {
/// Color of text forground body
fg_color: u8,
/// Opacity of text foreground body
fg_opacity: Opacity,
/// Color of background box surrounding the text
bg_color: u8,
/// Opacity of background box surrounding the text
bg_opacity: Opacity,
/// Color of the outlined edges of text
edge_color: u8,
}
enum Opacity {
Solid = 0,
_Flash = 1,
_Translucent = 2,
Transparent = 3,
}
impl Default for dtvcc_pen_color {
fn default() -> Self {
Self {
fg_color: 0x3F,
fg_opacity: 0,
bg_color: 0,
bg_opacity: 0,
edge_color: 0,
}
}
}
impl Default for dtvcc_pen_attribs {
fn default() -> Self {
Self {
pen_size: dtvcc_pen_size::DTVCC_PEN_SIZE_STANDART as i32,
offset: 0,
text_tag: dtvcc_pen_text_tag::DTVCC_PEN_TEXT_TAG_UNDEFINED_12 as i32,
font_tag: 0,
edge_type: dtvcc_pen_edge::DTVCC_PEN_EDGE_NONE as i32,
underline: 0,
italic: 0,
}
}
}

138
src/rust/src/lib.rs Normal file
View File

@@ -0,0 +1,138 @@
//! Rust library for CCExtractor
//!
//! Currently we are in the process of porting the 708 decoder to rust. See [decoder]
// Allow C naming style
#![allow(non_upper_case_globals)]
#![allow(non_camel_case_types)]
#![allow(non_snake_case)]
/// CCExtractor C bindings generated by bindgen
#[allow(clippy::all)]
pub mod bindings {
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
}
pub mod decoder;
pub mod utils;
#[cfg(windows)]
use std::os::windows::io::{FromRawHandle, RawHandle};
use std::{io::Write, os::raw::c_int};
use bindings::*;
use decoder::Dtvcc;
use utils::is_true;
use env_logger::{builder, Target};
use log::{warn, LevelFilter};
extern "C" {
static mut cb_708: c_int;
static mut cb_field1: c_int;
static mut cb_field2: c_int;
}
/// Initialize env logger with custom format, using stdout as target
#[no_mangle]
pub extern "C" fn ccxr_init_logger() {
builder()
.format(|buf, record| writeln!(buf, "[CEA-708] {}", record.args()))
.filter_level(LevelFilter::Debug)
.target(Target::Stdout)
.init();
}
/// Process cc_data
///
/// # Safety
/// dec_ctx should not be a null pointer
/// data should point to cc_data of length cc_count
#[no_mangle]
extern "C" fn ccxr_process_cc_data(
dec_ctx: *mut lib_cc_decode,
data: *const ::std::os::raw::c_uchar,
cc_count: c_int,
) -> c_int {
let mut ret = -1;
let mut cc_data: Vec<u8> = (0..cc_count * 3)
.map(|x| unsafe { *data.add(x as usize) })
.collect();
let dec_ctx = unsafe { &mut *dec_ctx };
let dtvcc_ctx = unsafe { &mut *dec_ctx.dtvcc };
let mut dtvcc = Dtvcc::new(dtvcc_ctx);
for cc_block in cc_data.chunks_exact_mut(3) {
if validate_cc_pair(cc_block) {
continue;
}
let success = do_cb(dec_ctx, &mut dtvcc, cc_block);
if success {
ret = 0;
}
}
ret
}
pub fn validate_cc_pair(cc_block: &[u8]) -> bool {
let cc_valid = (cc_block[0] & 4) >> 2;
if cc_valid == 0 {
return true;
}
false
}
pub fn do_cb(ctx: &mut lib_cc_decode, dtvcc: &mut Dtvcc, cc_block: &[u8]) -> bool {
let cc_valid = (cc_block[0] & 4) >> 2;
let cc_type = cc_block[0] & 3;
let mut timeok = true;
if ctx.write_format != ccx_output_format::CCX_OF_DVDRAW
&& ctx.write_format != ccx_output_format::CCX_OF_RAW
&& (cc_block[0] == 0xFA || cc_block[0] == 0xFC || cc_block[0] == 0xFD)
&& (cc_block[1] & 0x7F) == 0
&& (cc_block[2] & 0x7F) == 0
{
return true;
}
if cc_valid == 1 || cc_type == 3 {
ctx.cc_stats[cc_type as usize] += 1;
match cc_type {
0 | 1 => {}
2 | 3 => {
let current_time = unsafe { (*ctx.timing).get_fts(ctx.current_field as u8) };
ctx.current_field = 3;
if is_true(ctx.extraction_start.set)
&& current_time < ctx.extraction_start.time_in_ms
{
timeok = false;
}
if is_true(ctx.extraction_end.set) && current_time > ctx.extraction_end.time_in_ms {
timeok = false;
ctx.processed_enough = 1;
}
if timeok && ctx.write_format != ccx_output_format::CCX_OF_RAW {
dtvcc.process_cc_data(cc_valid, cc_type, cc_block[1], cc_block[2]);
}
unsafe { cb_708 += 1 }
}
_ => warn!("Invalid cc_type"),
}
}
true
}
#[cfg(windows)]
#[no_mangle]
extern "C" fn ccxr_close_handle(handle: RawHandle) {
use std::fs::File;
if handle.is_null() {
return;
}
unsafe {
// File will close automatically (due to Drop) once it goes out of scope
let file = from_raw_handle(handle);
}
}

11
src/rust/src/utils.rs Normal file
View File

@@ -0,0 +1,11 @@
//! Some utility functions to deal with values from C bindings
/// Check if the value is true (Set to 1). Use only for values from C bindings
pub fn is_true<T: Into<i32>>(val: T) -> bool {
val.into() == 1
}
/// Check if the value is false (Set to 0). Use only for values from C bindings
pub fn is_false<T: Into<i32>>(val: T) -> bool {
val.into() == 0
}

7
src/rust/wrapper.h Normal file
View File

@@ -0,0 +1,7 @@
#include "../lib_ccx/ccx_decoders_708.h"
#include "../lib_ccx/ccx_decoders_common.h"
#include "../lib_ccx/ccx_dtvcc.h"
#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"

183
windows/installer.wxs Normal file
View File

@@ -0,0 +1,183 @@
<Wix xmlns="http://wixtoolset.org/schemas/v4/wxs">
<Package Name="CCExtractor" Language="1033" Version="$(AppVersion)" Manufacturer="CCExtractor development" UpgradeCode="e70dbe37-bb04-4c39-bedc-966a6b073bcf" InstallerVersion="200">
<MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed."/>
<MediaTemplate EmbedCab="yes"/>
<Feature Id="CCX" Title="CCExtractor Setup" Level="1">
<ComponentGroupRef Id="CCX_Components_MainFolder"/>
<ComponentGroupRef Id="CCX_Components_MainFolder_data"/>
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets"/>
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_assets"/>
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_fonts"/>
<ComponentGroupRef Id="CCX_Components_MainFolder_data_flutter_assets_cupertino"/>
<ComponentRef Id="ApplicationShortcutDesktop"/>
</Feature>
<Icon Id="ccxgui.exe" SourceFile="./installer/ccxgui.exe"/>
<!-- Derives from the regular install dir, but skips the license dialog. -->
<UI Id="WixUI_InstallDir">
<TextStyle Id="WixUI_Font_Normal" FaceName="Tahoma" Size="8" />
<TextStyle Id="WixUI_Font_Bigger" FaceName="Tahoma" Size="12" />
<TextStyle Id="WixUI_Font_Title" FaceName="Tahoma" Size="9" Bold="yes" />
<Property Id="DefaultUIFont" Value="WixUI_Font_Normal" />
<Property Id="WixUI_Mode" Value="InstallDir" />
<DialogRef Id="BrowseDlg" />
<DialogRef Id="DiskCostDlg" />
<DialogRef Id="ErrorDlg" />
<DialogRef Id="FatalError" />
<DialogRef Id="FilesInUse" />
<DialogRef Id="MsiRMFilesInUse" />
<DialogRef Id="PrepareDlg" />
<DialogRef Id="ProgressDlg" />
<DialogRef Id="ResumeDlg" />
<DialogRef Id="UserExit" />
<Publish Dialog="BrowseDlg" Control="OK" Event="DoAction" Value="WixUIValidatePath" Order="3" />
<Publish Dialog="BrowseDlg" Control="OK" Event="SpawnDialog" Value="InvalidDirDlg" Order="4" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
<Publish Dialog="ExitDialog" Control="Finish" Event="EndDialog" Value="Return" Order="999" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="InstallDirDlg" Condition="NOT Installed" />
<Publish Dialog="WelcomeDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Condition="Installed AND PATCH" />
<Publish Dialog="InstallDirDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" />
<Publish Dialog="InstallDirDlg" Control="Next" Event="SetTargetPath" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="InstallDirDlg" Control="Next" Event="DoAction" Value="WixUIValidatePath" Order="2" Condition="NOT WIXUI_DONTVALIDATEPATH" />
<Publish Dialog="InstallDirDlg" Control="Next" Event="SpawnDialog" Value="InvalidDirDlg" Order="3" Condition="NOT WIXUI_DONTVALIDATEPATH AND WIXUI_INSTALLDIR_VALID&lt;&gt;&quot;1&quot;" />
<Publish Dialog="InstallDirDlg" Control="Next" Event="NewDialog" Value="VerifyReadyDlg" Order="4" Condition="WIXUI_DONTVALIDATEPATH OR WIXUI_INSTALLDIR_VALID=&quot;1&quot;" />
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Property="_BrowseProperty" Value="[WIXUI_INSTALLDIR]" Order="1" />
<Publish Dialog="InstallDirDlg" Control="ChangeFolder" Event="SpawnDialog" Value="BrowseDlg" Order="2" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="InstallDirDlg" Order="1" Condition="NOT Installed" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="MaintenanceTypeDlg" Order="2" Condition="Installed AND NOT PATCH" />
<Publish Dialog="VerifyReadyDlg" Control="Back" Event="NewDialog" Value="WelcomeDlg" Order="2" Condition="Installed AND PATCH" />
<Publish Dialog="MaintenanceWelcomeDlg" Control="Next" Event="NewDialog" Value="MaintenanceTypeDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RepairButton" Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="RemoveButton" Event="NewDialog" Value="VerifyReadyDlg" />
<Publish Dialog="MaintenanceTypeDlg" Control="Back" Event="NewDialog" Value="MaintenanceWelcomeDlg" />
<Property Id="ARPNOMODIFY" Value="1" />
</UI>
<UIRef Id="WixUI_Common" />
<Property Id="WIXUI_INSTALLDIR" Value="INSTALLFOLDER"/>
</Package>
<Fragment>
<Directory Id="DesktopFolder" Name="Desktop">
<Component Id="ApplicationShortcutDesktop" Guid="*">
<Shortcut Id="ApplicationDesktopShortcut"
Name="CCExtractor"
Description="Run CCExtractor"
Target="[INSTALLFOLDER]ccxgui.exe"
WorkingDirectory="INSTALLFOLDER"/>
<RemoveFolder Id="DesktopFolder" On="uninstall"/>
<RegistryValue
Root="HKCU"
Key="Software\CCExtractor\ccextractor"
Name="installed"
Type="integer"
Value="1"
KeyPath="yes"/>
</Component>
</Directory>
<StandardDirectory Id="ProgramFiles6432Folder">
<Directory Id="INSTALLFOLDER" Name="CCExtractor">
<Directory Id="CCX_data" Name="data">
<Directory Id="CCX_data_flutter_assets" Name="flutter_assets">
<Directory Id="CCX_data_flutter_assets_assets" Name="assets"/>
<Directory Id="CCX_data_flutter_assets_fonts" Name="fonts"/>
<Directory Id="dirEE44DD2D485FE70BEAFB55755745AB6E" Name="packages">
<Directory Id="dir382F01892F72FB688A688BE6E01B93A3" Name="cupertino_icons">
<Directory Id="CCX_data_flutter_assets_cupertino" Name="assets"/>
</Directory>
</Directory>
</Directory>
</Directory>
</Directory>
</StandardDirectory>
</Fragment>
<Fragment>
<ComponentGroup Id="CCX_Components_MainFolder" Directory="INSTALLFOLDER">
<Component Guid="{2D6CDFAD-A645-4929-9518-B43788B9E5F3}">
<File Id="CCX_Full_executable" Source="./installer/ccextractorwinfull.exe" KeyPath="yes"/>
</Component>
<Component Guid="{5DA8F5F9-922B-45E1-B0A9-CB241996D9EE}">
<File Id="CCX_avcodec" Source="./installer/avcodec-57.dll" KeyPath="yes"/>
</Component>
<Component Guid="{00E5F91A-D4D9-4A3D-B498-1CF45D18770F}">
<File Id="CCX_avformat" Source="./installer/avformat-57.dll" KeyPath="yes"/>
</Component>
<Component Guid="{AA5FE610-7566-483C-84D0-70B0435E08B0}">
<File Id="CCX_avutil" Source="./installer/avutil-55.dll" KeyPath="yes"/>
</Component>
<Component Guid="{334ABDD6-FDBB-41EB-87FE-DAD3295DAE2D}">
<File Id="CCX_swresample" Source="./installer/swresample-2.dll" KeyPath="yes"/>
</Component>
<Component Guid="{888B47CF-0377-490A-8B7E-341D2C71EAD0}">
<File Id="CCX_swscale" Source="./installer/swscale-4.dll" KeyPath="yes"/>
</Component>
<Component Guid="{8B69210B-5091-4C63-8902-E0ADDBE2C080}">
<File Source="./installer/ccxgui.exe" KeyPath="yes"/>
</Component>
<Component Guid="{1B37F14A-3BA6-4837-8A6F-6EA01A25DA26}">
<File Source="./installer/file_selector_windows_plugin.dll" KeyPath="yes"/>
</Component>
<Component Guid="{B276C96D-9737-4B8C-B55B-60F392DED331}">
<File Source="./installer/url_launcher_windows_plugin.dll" KeyPath="yes"/>
</Component>
<Component Guid="{4B627AA9-55DD-40ED-99F9-54F67EC73887}">
<File Source="./installer/flutter_windows.dll" KeyPath="yes"/>
</Component>
<Component Guid="{32F0A64B-0C07-4807-A48C-714B2533A03C}">
<File Source="./installer/msvcp140.dll" KeyPath="yes"/>
</Component>
<Component Guid="{8425DEB5-8CF4-4672-AE45-561CF76B2072}">
<File Source="./installer/vcruntime140.dll" KeyPath="yes"/>
</Component>
<Component Guid="{1F3DBC7E-25D5-441D-9B41-96C33FEA5157}">
<File Source="./installer/vcruntime140_1.dll" KeyPath="yes"/>
</Component>
<Component Guid="{BE7FE765-EBA8-4FAB-8864-8561C50D39CF}">
<File Source="./installer/window_size_plugin.dll" KeyPath="yes"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="CCX_Components_MainFolder_data" Directory="CCX_data">
<Component Id="cmp205CD51FCB7438CA9DDF9D318D5BDD93" Guid="{50D63F41-6529-4009-9092-F4D5B60DDF1B}">
<File Id="filBBF0A8ED8BF5DEEA3D5FE3E0B96C18FF" KeyPath="yes" Source="./installer/data/app.so"/>
</Component>
<Component Id="cmp3BB4C81F1F7BAB8E1AEB858EBC85CC1E" Guid="{88E94229-FBE7-4DC8-B9E2-B5E27FFB844C}">
<File Id="fil3F969272E8EAC0F75CC6D464590D2FBE" KeyPath="yes" Source="./installer/data/icudtl.dat"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="CCX_Components_MainFolder_data_flutter_assets" Directory="CCX_data_flutter_assets">
<Component Id="cmp1C63471C238EEA92D0AE37BC7DF9E605" Guid="{DEAF277D-3D05-4B37-A732-9514B503B74A}">
<File Id="fil3F43BEEF0A85EE6619E4674CA43CB616" KeyPath="yes" Source="./installer/data/flutter_assets/AssetManifest.json"/>
</Component>
<Component Id="cmp414DC13EF11DED7194619DD3FC5F4CA7" Guid="{E66832FA-6880-4249-92A0-A26A4FF0A1F9}">
<File Id="filD6CD37343140A4FABEA77420A8B86B57" KeyPath="yes" Source="./installer/data/flutter_assets/FontManifest.json"/>
</Component>
<Component Id="cmp4AD05995360EA9B961C2D73FE38274D2" Guid="{B09977A1-1C36-4F5D-B3BC-394D10AC950D}">
<File Id="filC4871B1F6646D8021500E7E62CF93FAC" KeyPath="yes" Source="./installer/data/flutter_assets/NOTICES.Z"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="CCX_Components_MainFolder_data_flutter_assets_assets" Directory="CCX_data_flutter_assets_assets">
<Component Id="cmp5F6AEEA1DB53EC5D0D51921A0B62098C" Guid="{CECCAC58-B818-42E2-849A-BFA4A8CA4FBE}">
<File Id="fil0E47318EAF44F6EEF60A89C92FCD2FAA" KeyPath="yes" Source="./installer/data/flutter_assets/assets/ccextractor"/>
</Component>
<Component Id="cmpDA8A6D10314F9C5A931AB86118A106CC" Guid="{75DE9E26-1024-4E83-8995-078624187483}">
<File Id="fil72BB9315DC954394F9980ECD2DA6D21F" KeyPath="yes" Source="./installer/data/flutter_assets/assets/ccx.svg"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="CCX_Components_MainFolder_data_flutter_assets_fonts" Directory="CCX_data_flutter_assets_fonts">
<Component Id="cmp97B1795B6861175BEF48B71FC703EDA6" Guid="{292269F8-9FC7-464A-A9AB-AF5BA97D7F06}">
<File Id="fil26D47F100D1B90F2759D0EAE797393FC" KeyPath="yes" Source="./installer/data/flutter_assets/fonts/MaterialIcons-Regular.otf"/>
</Component>
</ComponentGroup>
<ComponentGroup Id="CCX_Components_MainFolder_data_flutter_assets_cupertino" Directory="CCX_data_flutter_assets_cupertino">
<Component Id="cmp18EDD5C842814546AE2A43759CD36C77" Guid="{30AFB9BB-1C7D-4CDB-ADCA-D0773F152B45}">
<File Id="fil341334402AF57DB92DF3F7C92E983317" KeyPath="yes" Source="./installer/data/flutter_assets/packages/cupertino_icons/assets/CupertinoIcons.ttf"/>
</Component>
</ComponentGroup>
</Fragment>
</Wix>