Compare commits

..

2 Commits

Author SHA1 Message Date
Willem
b4257cd970 Split up pre-build.sh 2025-10-25 19:33:28 +02:00
Willem
a0f2fef31a Update release.yml 2025-10-25 19:18:00 +02:00
235 changed files with 7068 additions and 28732 deletions

View File

@@ -1,37 +0,0 @@
# Build artifacts
linux/ccextractor
linux/rust/
linux/*.o
linux/*.a
mac/ccextractor
mac/rust/
build/
build_*/
# Git
.git/
.github/
# IDE
.vscode/
.idea/
*.swp
*.swo
# Docker
docker/
# Documentation (not needed for build)
docs/
*.md
!README.md
# Test files
*.ts
*.mp4
*.mkv
*.srt
*.vtt
# Plans
plans/

View File

@@ -1,157 +0,0 @@
name: Build Linux AppImage
on:
# Build on releases
release:
types: [published]
# Allow manual trigger
workflow_dispatch:
inputs:
build_type:
description: 'Build type (all, minimal, ocr, hardsubx)'
required: false
default: 'all'
# Build on pushes to workflow file for testing
push:
paths:
- '.github/workflows/build_appimage.yml'
- 'linux/build_appimage.sh'
jobs:
build-appimage:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
build_type: [minimal, ocr, hardsubx]
steps:
- name: Check if should build this variant
id: should_build
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_TYPE="${{ github.event.inputs.build_type }}"
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
echo "should_build=true" >> $GITHUB_OUTPUT
else
echo "should_build=false" >> $GITHUB_OUTPUT
fi
else
echo "should_build=true" >> $GITHUB_OUTPUT
fi
- name: Checkout repository
if: steps.should_build.outputs.should_build == 'true'
uses: actions/checkout@v6
- name: Install base dependencies
if: steps.should_build.outputs.should_build == 'true'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential \
cmake \
pkg-config \
wget \
file \
libfuse2 \
zlib1g-dev \
libpng-dev \
libjpeg-dev \
libfreetype-dev \
libxml2-dev \
libcurl4-gnutls-dev \
libssl-dev \
clang \
libclang-dev
- name: Install OCR dependencies
if: steps.should_build.outputs.should_build == 'true' && (matrix.build_type == 'ocr' || matrix.build_type == 'hardsubx')
run: |
sudo apt-get install -y --no-install-recommends \
tesseract-ocr \
libtesseract-dev \
libleptonica-dev \
tesseract-ocr-eng
- name: Install FFmpeg dependencies (HardSubX)
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
run: |
sudo apt-get install -y --no-install-recommends \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libswresample-dev \
libavfilter-dev \
libavdevice-dev
- name: Install Rust toolchain
if: steps.should_build.outputs.should_build == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Cache GPAC build
if: steps.should_build.outputs.should_build == 'true'
id: cache-gpac
uses: actions/cache@v5
with:
path: /usr/local/lib/libgpac*
key: gpac-v2.4.0-ubuntu22
- name: Build and install GPAC
if: steps.should_build.outputs.should_build == 'true' && steps.cache-gpac.outputs.cache-hit != 'true'
run: |
git clone -b v2.4.0 --depth 1 https://github.com/gpac/gpac
cd gpac
./configure
make -j$(nproc) lib
sudo make install-lib
sudo ldconfig
- name: Update library cache
if: steps.should_build.outputs.should_build == 'true'
run: sudo ldconfig
- name: Build AppImage
if: steps.should_build.outputs.should_build == 'true'
run: |
cd linux
chmod +x build_appimage.sh
BUILD_TYPE=${{ matrix.build_type }} ./build_appimage.sh
- name: Get AppImage name
if: steps.should_build.outputs.should_build == 'true'
id: appimage_name
run: |
case "${{ matrix.build_type }}" in
minimal)
echo "name=ccextractor-minimal-x86_64.AppImage" >> $GITHUB_OUTPUT
;;
ocr)
echo "name=ccextractor-x86_64.AppImage" >> $GITHUB_OUTPUT
;;
hardsubx)
echo "name=ccextractor-hardsubx-x86_64.AppImage" >> $GITHUB_OUTPUT
;;
esac
- name: Test AppImage
if: steps.should_build.outputs.should_build == 'true'
run: |
chmod +x linux/${{ steps.appimage_name.outputs.name }}
linux/${{ steps.appimage_name.outputs.name }} --version
- name: Upload AppImage artifact
if: steps.should_build.outputs.should_build == 'true'
uses: actions/upload-artifact@v6
with:
name: ${{ steps.appimage_name.outputs.name }}
path: linux/${{ steps.appimage_name.outputs.name }}
- name: Upload to Release
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: linux/${{ steps.appimage_name.outputs.name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,283 +0,0 @@
name: Build Linux .deb Package
on:
# Build on releases
release:
types: [published]
# Allow manual trigger
workflow_dispatch:
inputs:
build_type:
description: 'Build type (all, basic, hardsubx)'
required: false
default: 'all'
# Build on pushes to workflow file for testing
push:
paths:
- '.github/workflows/build_deb.yml'
jobs:
build-deb:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
build_type: [basic, hardsubx]
steps:
- name: Check if should build this variant
id: should_build
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_TYPE="${{ github.event.inputs.build_type }}"
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
echo "should_build=true" >> $GITHUB_OUTPUT
else
echo "should_build=false" >> $GITHUB_OUTPUT
fi
else
echo "should_build=true" >> $GITHUB_OUTPUT
fi
- name: Checkout repository
if: steps.should_build.outputs.should_build == 'true'
uses: actions/checkout@v6
- name: Get version
if: steps.should_build.outputs.should_build == 'true'
id: version
run: |
# Extract version from source or use tag
if [ "${{ github.event_name }}" = "release" ]; then
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}" # Remove 'v' prefix if present
else
# Extract version from lib_ccx.h (e.g., #define VERSION "0.96.5")
VERSION=$(grep -oP '#define VERSION "\K[^"]+' src/lib_ccx/lib_ccx.h || echo "0.96")
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Building version: $VERSION"
- name: Install base dependencies
if: steps.should_build.outputs.should_build == 'true'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential \
cmake \
pkg-config \
zlib1g-dev \
libpng-dev \
libjpeg-dev \
libfreetype-dev \
libxml2-dev \
libcurl4-gnutls-dev \
libssl-dev \
clang \
libclang-dev \
tesseract-ocr \
libtesseract-dev \
libleptonica-dev \
patchelf
- name: Install FFmpeg dependencies (HardSubX)
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
run: |
sudo apt-get install -y --no-install-recommends \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libswresample-dev \
libavfilter-dev \
libavdevice-dev
- name: Install Rust toolchain
if: steps.should_build.outputs.should_build == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Cache GPAC build
if: steps.should_build.outputs.should_build == 'true'
id: cache-gpac
uses: actions/cache@v5
with:
path: ~/gpac-install
key: gpac-abi-16.4-ubuntu24-deb
- name: Build GPAC
if: steps.should_build.outputs.should_build == 'true' && steps.cache-gpac.outputs.cache-hit != 'true'
run: |
git clone -b abi-16.4 --depth 1 https://github.com/gpac/gpac
cd gpac
./configure --prefix=/usr
make -j$(nproc)
make DESTDIR=$HOME/gpac-install install-lib
- name: Install GPAC to system
if: steps.should_build.outputs.should_build == 'true'
run: |
sudo cp -r $HOME/gpac-install/usr/lib/* /usr/lib/
sudo cp -r $HOME/gpac-install/usr/include/* /usr/include/
sudo ldconfig
- name: Build CCExtractor
if: steps.should_build.outputs.should_build == 'true'
run: |
mkdir build && cd build
if [ "${{ matrix.build_type }}" = "hardsubx" ]; then
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON -DWITH_HARDSUBX=ON
else
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON
fi
make -j$(nproc)
- name: Test build
if: steps.should_build.outputs.should_build == 'true'
run: ./build/ccextractor --version
- name: Create .deb package structure
if: steps.should_build.outputs.should_build == 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
VARIANT="${{ matrix.build_type }}"
if [ "$VARIANT" = "basic" ]; then
PKG_NAME="ccextractor_${VERSION}_amd64"
else
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_amd64"
fi
mkdir -p ${PKG_NAME}/DEBIAN
mkdir -p ${PKG_NAME}/usr/bin
mkdir -p ${PKG_NAME}/usr/lib/ccextractor
mkdir -p ${PKG_NAME}/usr/share/doc/ccextractor
mkdir -p ${PKG_NAME}/usr/share/man/man1
# Copy binary
cp build/ccextractor ${PKG_NAME}/usr/bin/
# Copy GPAC library
cp $HOME/gpac-install/usr/lib/libgpac.so* ${PKG_NAME}/usr/lib/ccextractor/
# Set rpath so ccextractor finds bundled libgpac
patchelf --set-rpath '/usr/lib/ccextractor:$ORIGIN/../lib/ccextractor' ${PKG_NAME}/usr/bin/ccextractor
# Copy documentation
cp docs/CHANGES.TXT ${PKG_NAME}/usr/share/doc/ccextractor/changelog
cp LICENSE.txt ${PKG_NAME}/usr/share/doc/ccextractor/copyright
gzip -9 -n ${PKG_NAME}/usr/share/doc/ccextractor/changelog
# Generate man page
help2man --no-info --name="closed captions and teletext subtitle extractor" \
./build/ccextractor > ${PKG_NAME}/usr/share/man/man1/ccextractor.1 2>/dev/null || true
if [ -f ${PKG_NAME}/usr/share/man/man1/ccextractor.1 ]; then
gzip -9 -n ${PKG_NAME}/usr/share/man/man1/ccextractor.1
fi
# Create control file
if [ "$VARIANT" = "basic" ]; then
PKG_DESCRIPTION="CCExtractor - closed captions and teletext subtitle extractor"
else
PKG_DESCRIPTION="CCExtractor (with HardSubX) - closed captions and teletext subtitle extractor"
fi
INSTALLED_SIZE=$(du -sk ${PKG_NAME}/usr | cut -f1)
# Determine dependencies based on build variant (Ubuntu 24.04)
if [ "$VARIANT" = "hardsubx" ]; then
DEPENDS="libc6, libtesseract5, liblept5, libcurl3t64-gnutls, libavcodec60, libavformat60, libavutil58, libswscale7, libavdevice60, libswresample4, libavfilter9"
else
DEPENDS="libc6, libtesseract5, liblept5, libcurl3t64-gnutls"
fi
cat > ${PKG_NAME}/DEBIAN/control << CTRL
Package: ccextractor
Version: ${VERSION}
Section: utils
Priority: optional
Architecture: amd64
Installed-Size: ${INSTALLED_SIZE}
Depends: ${DEPENDS}
Maintainer: CCExtractor Development Team <carlos@ccextractor.org>
Homepage: https://www.ccextractor.org
Description: ${PKG_DESCRIPTION}
CCExtractor is a tool that extracts closed captions and teletext subtitles
from video files and streams. It supports a wide variety of input formats
including MPEG, H.264/AVC, H.265/HEVC, MP4, MKV, WTV, and transport streams.
.
This package includes a bundled GPAC library for MP4 support.
CTRL
# Remove leading spaces from control file
sed -i 's/^ //' ${PKG_NAME}/DEBIAN/control
# Create postinst to update library cache
cat > ${PKG_NAME}/DEBIAN/postinst << 'POSTINST'
#!/bin/sh
set -e
ldconfig
POSTINST
chmod 755 ${PKG_NAME}/DEBIAN/postinst
# Create postrm to update library cache
cat > ${PKG_NAME}/DEBIAN/postrm << 'POSTRM'
#!/bin/sh
set -e
ldconfig
POSTRM
chmod 755 ${PKG_NAME}/DEBIAN/postrm
# Set permissions
chmod 755 ${PKG_NAME}/usr/bin/ccextractor
chmod 755 ${PKG_NAME}/usr/lib/ccextractor
find ${PKG_NAME}/usr/lib/ccextractor -name "*.so*" -exec chmod 644 {} \;
# Build the .deb
dpkg-deb --build --root-owner-group ${PKG_NAME}
echo "deb_name=${PKG_NAME}.deb" >> $GITHUB_OUTPUT
- name: Test .deb package
if: steps.should_build.outputs.should_build == 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
VARIANT="${{ matrix.build_type }}"
if [ "$VARIANT" = "basic" ]; then
PKG_NAME="ccextractor_${VERSION}_amd64"
else
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_amd64"
fi
# Install and test (apt handles dependencies automatically)
sudo apt-get update
sudo apt-get install -y ./${PKG_NAME}.deb
ccextractor --version
- name: Get .deb filename
if: steps.should_build.outputs.should_build == 'true'
id: deb_name
run: |
VERSION="${{ steps.version.outputs.version }}"
VARIANT="${{ matrix.build_type }}"
if [ "$VARIANT" = "basic" ]; then
echo "name=ccextractor_${VERSION}_amd64.deb" >> $GITHUB_OUTPUT
else
echo "name=ccextractor-${VARIANT}_${VERSION}_amd64.deb" >> $GITHUB_OUTPUT
fi
- name: Upload .deb artifact
if: steps.should_build.outputs.should_build == 'true'
uses: actions/upload-artifact@v6
with:
name: ${{ steps.deb_name.outputs.name }}
path: ${{ steps.deb_name.outputs.name }}
- name: Upload to Release
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.deb_name.outputs.name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,275 +0,0 @@
name: Build Debian 13 .deb Package
on:
# Build on releases
release:
types: [published]
# Allow manual trigger
workflow_dispatch:
inputs:
build_type:
description: 'Build type (all, basic, hardsubx)'
required: false
default: 'all'
# Build on pushes to workflow file for testing
push:
paths:
- '.github/workflows/build_deb_debian13.yml'
jobs:
build-deb:
runs-on: ubuntu-latest
container:
image: debian:trixie
strategy:
fail-fast: false
matrix:
build_type: [basic, hardsubx]
steps:
- name: Check if should build this variant
id: should_build
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_TYPE="${{ github.event.inputs.build_type }}"
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
echo "should_build=true" >> $GITHUB_OUTPUT
else
echo "should_build=false" >> $GITHUB_OUTPUT
fi
else
echo "should_build=true" >> $GITHUB_OUTPUT
fi
- name: Install git and dependencies for checkout
if: steps.should_build.outputs.should_build == 'true'
run: |
apt-get update
apt-get install -y git ca-certificates
- name: Checkout repository
if: steps.should_build.outputs.should_build == 'true'
uses: actions/checkout@v6
- name: Get version
if: steps.should_build.outputs.should_build == 'true'
id: version
run: |
# Extract version from source or use tag
if [ "${{ github.event_name }}" = "release" ]; then
VERSION="${{ github.event.release.tag_name }}"
VERSION="${VERSION#v}" # Remove 'v' prefix if present
else
# Extract version from lib_ccx.h (e.g., #define VERSION "0.96.5")
VERSION=$(grep -oP '#define VERSION "\K[^"]+' src/lib_ccx/lib_ccx.h || echo "0.96")
fi
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "Building version: $VERSION"
- name: Install base dependencies
if: steps.should_build.outputs.should_build == 'true'
run: |
apt-get install -y --no-install-recommends \
build-essential \
cmake \
pkg-config \
zlib1g-dev \
libpng-dev \
libjpeg-dev \
libfreetype-dev \
libxml2-dev \
libcurl4-gnutls-dev \
libssl-dev \
clang \
libclang-dev \
tesseract-ocr \
libtesseract-dev \
libleptonica-dev \
patchelf \
curl
- name: Install FFmpeg dependencies (HardSubX)
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
run: |
apt-get install -y --no-install-recommends \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libswresample-dev \
libavfilter-dev \
libavdevice-dev
- name: Install Rust toolchain
if: steps.should_build.outputs.should_build == 'true'
run: |
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
echo "$HOME/.cargo/bin" >> $GITHUB_PATH
- name: Build GPAC
if: steps.should_build.outputs.should_build == 'true'
run: |
git clone -b abi-16.4 --depth 1 https://github.com/gpac/gpac
cd gpac
./configure --prefix=/usr
make -j$(nproc)
make install-lib
ldconfig
- name: Build CCExtractor
if: steps.should_build.outputs.should_build == 'true'
run: |
export PATH="$HOME/.cargo/bin:$PATH"
mkdir build && cd build
if [ "${{ matrix.build_type }}" = "hardsubx" ]; then
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON -DWITH_HARDSUBX=ON
else
cmake ../src -DCMAKE_BUILD_TYPE=Release -DWITH_OCR=ON
fi
make -j$(nproc)
- name: Test build
if: steps.should_build.outputs.should_build == 'true'
run: ./build/ccextractor --version
- name: Create .deb package structure
if: steps.should_build.outputs.should_build == 'true'
id: create_deb
run: |
VERSION="${{ steps.version.outputs.version }}"
VARIANT="${{ matrix.build_type }}"
if [ "$VARIANT" = "basic" ]; then
PKG_NAME="ccextractor_${VERSION}_debian13_amd64"
else
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_debian13_amd64"
fi
mkdir -p ${PKG_NAME}/DEBIAN
mkdir -p ${PKG_NAME}/usr/bin
mkdir -p ${PKG_NAME}/usr/lib/ccextractor
mkdir -p ${PKG_NAME}/usr/share/doc/ccextractor
mkdir -p ${PKG_NAME}/usr/share/man/man1
# Copy binary
cp build/ccextractor ${PKG_NAME}/usr/bin/
# Copy GPAC library
cp /usr/lib/libgpac.so* ${PKG_NAME}/usr/lib/ccextractor/
# Set rpath so ccextractor finds bundled libgpac
patchelf --set-rpath '/usr/lib/ccextractor:$ORIGIN/../lib/ccextractor' ${PKG_NAME}/usr/bin/ccextractor
# Copy documentation
cp docs/CHANGES.TXT ${PKG_NAME}/usr/share/doc/ccextractor/changelog
cp LICENSE.txt ${PKG_NAME}/usr/share/doc/ccextractor/copyright
gzip -9 -n ${PKG_NAME}/usr/share/doc/ccextractor/changelog
# Create control file
if [ "$VARIANT" = "basic" ]; then
PKG_DESCRIPTION="CCExtractor - closed captions and teletext subtitle extractor"
else
PKG_DESCRIPTION="CCExtractor (with HardSubX) - closed captions and teletext subtitle extractor"
fi
INSTALLED_SIZE=$(du -sk ${PKG_NAME}/usr | cut -f1)
# Determine dependencies based on build variant (Debian 13 Trixie)
if [ "$VARIANT" = "hardsubx" ]; then
DEPENDS="libc6, libtesseract5, libleptonica6, libcurl3t64-gnutls, libavcodec61, libavformat61, libavutil59, libswscale8, libavdevice61, libswresample5, libavfilter10"
else
DEPENDS="libc6, libtesseract5, libleptonica6, libcurl3t64-gnutls"
fi
cat > ${PKG_NAME}/DEBIAN/control << CTRL
Package: ccextractor
Version: ${VERSION}
Section: utils
Priority: optional
Architecture: amd64
Installed-Size: ${INSTALLED_SIZE}
Depends: ${DEPENDS}
Maintainer: CCExtractor Development Team <carlos@ccextractor.org>
Homepage: https://www.ccextractor.org
Description: ${PKG_DESCRIPTION}
CCExtractor is a tool that extracts closed captions and teletext subtitles
from video files and streams. It supports a wide variety of input formats
including MPEG, H.264/AVC, H.265/HEVC, MP4, MKV, WTV, and transport streams.
.
This package includes a bundled GPAC library for MP4 support.
Built for Debian 13 (Trixie).
CTRL
# Remove leading spaces from control file
sed -i 's/^ //' ${PKG_NAME}/DEBIAN/control
# Create postinst to update library cache
cat > ${PKG_NAME}/DEBIAN/postinst << 'POSTINST'
#!/bin/sh
set -e
ldconfig
POSTINST
chmod 755 ${PKG_NAME}/DEBIAN/postinst
# Create postrm to update library cache
cat > ${PKG_NAME}/DEBIAN/postrm << 'POSTRM'
#!/bin/sh
set -e
ldconfig
POSTRM
chmod 755 ${PKG_NAME}/DEBIAN/postrm
# Set permissions
chmod 755 ${PKG_NAME}/usr/bin/ccextractor
chmod 755 ${PKG_NAME}/usr/lib/ccextractor
find ${PKG_NAME}/usr/lib/ccextractor -name "*.so*" -exec chmod 644 {} \;
# Build the .deb
dpkg-deb --build --root-owner-group ${PKG_NAME}
echo "deb_name=${PKG_NAME}.deb" >> $GITHUB_OUTPUT
- name: Test .deb package
if: steps.should_build.outputs.should_build == 'true'
run: |
VERSION="${{ steps.version.outputs.version }}"
VARIANT="${{ matrix.build_type }}"
if [ "$VARIANT" = "basic" ]; then
PKG_NAME="ccextractor_${VERSION}_debian13_amd64"
else
PKG_NAME="ccextractor-${VARIANT}_${VERSION}_debian13_amd64"
fi
# Install and test (apt handles dependencies automatically)
apt-get update
apt-get install -y ./${PKG_NAME}.deb
ccextractor --version
- name: Get .deb filename
if: steps.should_build.outputs.should_build == 'true'
id: deb_name
run: |
VERSION="${{ steps.version.outputs.version }}"
VARIANT="${{ matrix.build_type }}"
if [ "$VARIANT" = "basic" ]; then
echo "name=ccextractor_${VERSION}_debian13_amd64.deb" >> $GITHUB_OUTPUT
else
echo "name=ccextractor-${VARIANT}_${VERSION}_debian13_amd64.deb" >> $GITHUB_OUTPUT
fi
- name: Upload .deb artifact
if: steps.should_build.outputs.should_build == 'true'
uses: actions/upload-artifact@v6
with:
name: ${{ steps.deb_name.outputs.name }}
path: ${{ steps.deb_name.outputs.name }}
- name: Upload to Release
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.deb_name.outputs.name }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -1,96 +0,0 @@
name: Build CCExtractor Docker Images
on:
workflow_dispatch:
push:
paths:
- '.github/workflows/build_docker.yml'
- 'docker/**'
- '**.c'
- '**.h'
- '**CMakeLists.txt'
- '**.cmake'
- 'src/rust/**'
pull_request:
types: [opened, synchronize, reopened]
paths:
- '.github/workflows/build_docker.yml'
- 'docker/**'
- '**.c'
- '**.h'
- '**CMakeLists.txt'
- '**.cmake'
- 'src/rust/**'
jobs:
build_minimal:
name: Docker build (minimal)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build minimal image
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
build-args: |
BUILD_TYPE=minimal
USE_LOCAL_SOURCE=1
tags: ccextractor:minimal
load: true
cache-from: type=gha,scope=docker-minimal
cache-to: type=gha,mode=max,scope=docker-minimal
- name: Test minimal image
run: |
docker run --rm ccextractor:minimal --version
echo "Minimal build successful"
build_ocr:
name: Docker build (ocr)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build OCR image
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
build-args: |
BUILD_TYPE=ocr
USE_LOCAL_SOURCE=1
tags: ccextractor:ocr
load: true
cache-from: type=gha,scope=docker-ocr
cache-to: type=gha,mode=max,scope=docker-ocr
- name: Test OCR image
run: |
docker run --rm ccextractor:ocr --version
echo "OCR build successful"
build_hardsubx:
name: Docker build (hardsubx)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build HardSubX image
uses: docker/build-push-action@v6
with:
context: .
file: docker/Dockerfile
build-args: |
BUILD_TYPE=hardsubx
USE_LOCAL_SOURCE=1
tags: ccextractor:hardsubx
load: true
cache-from: type=gha,scope=docker-hardsubx
cache-to: type=gha,mode=max,scope=docker-hardsubx
- name: Test HardSubX image
run: |
docker run --rm ccextractor:hardsubx --version
echo "HardSubX build successful"

View File

@@ -7,8 +7,6 @@ on:
- '.github/workflows/build_linux.yml'
- '**.c'
- '**.h'
- '**CMakeLists.txt'
- '**.cmake'
- '**Makefile**'
- 'linux/**'
- 'package_creators/**'
@@ -19,8 +17,6 @@ on:
- '.github/workflows/build_linux.yml'
- '**.c'
- '**.h'
- '**CMakeLists.txt'
- '**.cmake'
- '**Makefile**'
- 'linux/**'
- 'package_creators/**'
@@ -31,7 +27,7 @@ jobs:
steps:
- name: Install dependencies
run: sudo apt update && sudo apt-get install libgpac-dev libtesseract-dev libavcodec-dev libavdevice-dev libx11-dev libxcb1-dev libxcb-shm0-dev
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: build
run: ./build -hardsubx
working-directory: ./linux
@@ -42,7 +38,7 @@ jobs:
run: mkdir ./linux/artifacts
- name: Copy release artifact
run: cp ./linux/ccextractor ./linux/artifacts/
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v4
with:
name: CCExtractor Linux build
path: ./linux/artifacts
@@ -51,7 +47,7 @@ jobs:
steps:
- name: Install dependencies
run: sudo apt update && sudo apt-get install libgpac-dev
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: run autogen
run: ./autogen.sh
working-directory: ./linux
@@ -69,7 +65,7 @@ jobs:
steps:
- name: Install dependencies
run: sudo apt update && sudo apt-get install libgpac-dev
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: cmake
run: mkdir build && cd build && cmake ../src
- name: build
@@ -80,7 +76,7 @@ jobs:
cmake_ocr_hardsubx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install dependencies
run: sudo apt update && sudo apt install libgpac-dev libtesseract-dev libavformat-dev libavdevice-dev libswscale-dev yasm
- name: cmake
@@ -98,9 +94,9 @@ jobs:
steps:
- name: Install dependencies
run: sudo apt update && sudo apt-get install libgpac-dev
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: |
src/rust/.cargo/registry

View File

@@ -1,154 +0,0 @@
name: Build Linux (System Libs)
on:
# Build on releases
release:
types: [published]
# Allow manual trigger
workflow_dispatch:
inputs:
build_type:
description: 'Build type (all, basic, hardsubx)'
required: false
default: 'all'
# Build on pushes to workflow file for testing
push:
paths:
- '.github/workflows/build_linux_systemlibs.yml'
- 'linux/build'
permissions:
contents: write
jobs:
build-systemlibs:
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
build_type: [basic, hardsubx]
steps:
- name: Check if should build this variant
id: should_build
run: |
if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then
INPUT_TYPE="${{ github.event.inputs.build_type }}"
if [ "$INPUT_TYPE" = "all" ] || [ "$INPUT_TYPE" = "${{ matrix.build_type }}" ]; then
echo "should_build=true" >> $GITHUB_OUTPUT
else
echo "should_build=false" >> $GITHUB_OUTPUT
fi
else
echo "should_build=true" >> $GITHUB_OUTPUT
fi
- name: Checkout repository
if: steps.should_build.outputs.should_build == 'true'
uses: actions/checkout@v6
- name: Install base dependencies
if: steps.should_build.outputs.should_build == 'true'
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
build-essential \
pkg-config \
zlib1g-dev \
libpng-dev \
libfreetype-dev \
libutf8proc-dev \
libgpac-dev \
libtesseract-dev \
libleptonica-dev \
tesseract-ocr-eng \
clang \
libclang-dev
- name: Install FFmpeg dependencies (HardSubX)
if: steps.should_build.outputs.should_build == 'true' && matrix.build_type == 'hardsubx'
run: |
sudo apt-get install -y --no-install-recommends \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libswresample-dev \
libavfilter-dev \
libavdevice-dev \
libxcb1-dev \
libxcb-shm0-dev \
libx11-dev \
liblzma-dev
- name: Install Rust toolchain
if: steps.should_build.outputs.should_build == 'true'
uses: dtolnay/rust-toolchain@stable
- name: Build with system libraries
if: steps.should_build.outputs.should_build == 'true'
run: |
cd linux
if [ "${{ matrix.build_type }}" = "hardsubx" ]; then
./build -system-libs -hardsubx
else
./build -system-libs
fi
- name: Verify build
if: steps.should_build.outputs.should_build == 'true'
run: |
./linux/ccextractor --version
echo "=== Library dependencies ==="
ldd ./linux/ccextractor | grep -E 'freetype|png|utf8proc|tesseract|leptonica' || true
- name: Get output name
if: steps.should_build.outputs.should_build == 'true'
id: output_name
run: |
case "${{ matrix.build_type }}" in
basic)
echo "name=ccextractor-linux-systemlibs-x86_64" >> $GITHUB_OUTPUT
;;
hardsubx)
echo "name=ccextractor-linux-systemlibs-hardsubx-x86_64" >> $GITHUB_OUTPUT
;;
esac
- name: Package binary
if: steps.should_build.outputs.should_build == 'true'
run: |
mkdir -p package
cp linux/ccextractor package/
# Create a simple README for the package
cat > package/README.txt << 'EOF'
CCExtractor - System Libraries Build
=====================================
This build uses system libraries (dynamic linking).
Required system packages (Debian/Ubuntu):
sudo apt install libgpac12 libtesseract5 libleptonica6 \
libpng16-16 libfreetype6 libutf8proc3
For HardSubX builds, also install:
sudo apt install libavcodec60 libavformat60 libswscale7 libavfilter9
Run with: ./ccextractor --help
EOF
tar -czvf ${{ steps.output_name.outputs.name }}.tar.gz -C package .
- name: Upload artifact
if: steps.should_build.outputs.should_build == 'true'
uses: actions/upload-artifact@v6
with:
name: ${{ steps.output_name.outputs.name }}
path: ${{ steps.output_name.outputs.name }}.tar.gz
- name: Upload to Release
if: steps.should_build.outputs.should_build == 'true' && github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: ${{ steps.output_name.outputs.name }}.tar.gz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -7,8 +7,6 @@ on:
- '.github/workflows/build_mac.yml'
- '**.c'
- '**.h'
- '**CMakeLists.txt'
- '**.cmake'
- '**Makefile**'
- 'mac/**'
- 'package_creators/**'
@@ -19,8 +17,6 @@ on:
- '.github/workflows/build_mac.yml'
- '**.c'
- '**.h'
- '**CMakeLists.txt'
- '**.cmake'
- '**Makefile**'
- 'mac/**'
- 'package_creators/**'
@@ -31,7 +27,7 @@ jobs:
steps:
- name: Install dependencies
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: build
run: ./build.command
working-directory: ./mac
@@ -42,27 +38,14 @@ jobs:
run: mkdir ./mac/artifacts
- name: Copy release artifact
run: cp ./mac/ccextractor ./mac/artifacts/
- uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v4
with:
name: CCExtractor mac build
path: ./mac/artifacts
build_shell_system_libs:
# Test building with system libraries via pkg-config (for Homebrew formula compatibility)
runs-on: macos-latest
steps:
- name: Install dependencies
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac freetype libpng protobuf-c utf8proc zlib
- uses: actions/checkout@v6
- name: build with system libs
run: ./build.command -system-libs
working-directory: ./mac
- name: Display version information
run: ./ccextractor --version
working-directory: ./mac
build_autoconf:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Install dependencies
run: brew install pkg-config autoconf automake libtool gpac
- name: run autogen
@@ -80,10 +63,10 @@ jobs:
cmake:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: dependencies
run: brew install gpac
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: cmake
run: mkdir build && cd build && cmake ../src
- name: build
@@ -91,76 +74,12 @@ jobs:
working-directory: build
- name: Display version information
run: ./build/ccextractor --version
cmake_ocr_hardsubx:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: Install dependencies
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg
- name: cmake
run: |
mkdir build && cd build
cmake -DWITH_OCR=ON -DWITH_HARDSUBX=ON ../src
- name: build
run: |
make -j$(nproc)
working-directory: build
- name: Display version information
run: ./build/ccextractor --version
build_shell_hardsubx:
# Test build.command with -hardsubx flag (burned-in subtitle extraction)
runs-on: macos-latest
steps:
- name: Install dependencies
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg
- uses: actions/checkout@v6
- name: build with hardsubx
run: ./build.command -hardsubx
working-directory: ./mac
- name: Display version information
run: ./ccextractor --version
working-directory: ./mac
- name: Verify hardsubx support
run: |
# Check that -hardsubx is recognized (will fail if not compiled in)
./ccextractor -hardsubx --help 2>&1 | head -20 || true
working-directory: ./mac
build_autoconf_hardsubx:
# Test autoconf build with HARDSUBX enabled (fixes issue #1173)
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: Install dependencies
run: brew install pkg-config autoconf automake libtool tesseract leptonica gpac ffmpeg
- name: run autogen
run: ./autogen.sh
working-directory: ./mac
- name: configure with hardsubx
run: |
# Set Homebrew paths for configure to find libraries
export HOMEBREW_PREFIX="$(brew --prefix)"
export LDFLAGS="-L${HOMEBREW_PREFIX}/lib"
export CPPFLAGS="-I${HOMEBREW_PREFIX}/include"
export PKG_CONFIG_PATH="${HOMEBREW_PREFIX}/lib/pkgconfig"
./configure --enable-hardsubx --enable-ocr
working-directory: ./mac
- name: make
run: make
working-directory: ./mac
- name: Display version information
run: ./ccextractor --version
working-directory: ./mac
- name: Verify hardsubx support
run: |
# Check that -hardsubx is recognized
./ccextractor -hardsubx --help 2>&1 | head -20 || true
working-directory: ./mac
build_rust:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: cache
uses: actions/cache@v5
- uses: actions/checkout@v4
- name: cache
uses: actions/cache@v4
with:
path: |
src/rust/.cargo/registry
@@ -173,5 +92,5 @@ jobs:
toolchain: stable
override: true
- name: build
run: cargo build
run: cargo build
working-directory: ./src/rust

View File

@@ -1,51 +0,0 @@
name: Build CCExtractor Snap
on:
workflow_dispatch:
release:
types: [published]
jobs:
build_snap:
name: Build Snap package
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Install snapd
run: |
sudo apt update
sudo apt install -y snapd
- name: Start snapd
run: |
sudo systemctl start snapd.socket
sudo systemctl start snapd
- name: Install Snapcraft
run: |
sudo snap install core22
sudo snap install snapcraft --classic
- name: Show Snapcraft version
run: snapcraft --version
- name: Build snap
run: sudo snapcraft --destructive-mode
- name: List generated snap
run: ls -lh *.snap
- name: Upload snap as workflow artifact
uses: actions/upload-artifact@v6
with:
name: CCExtractor Snap
path: "*.snap"
- name: Upload snap to GitHub Release
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: "*.snap"

View File

@@ -3,7 +3,8 @@ name: Build CCExtractor on Windows
env:
RUSTFLAGS: -Ctarget-feature=+crt-static
VCPKG_DEFAULT_TRIPLET: x64-windows-static
VCPKG_COMMIT: ab2977be50c702126336e5088f4836060733c899
VCPKG_DEFAULT_BINARY_CACHE: C:\vcpkg\.cache
VCPKG_COMMIT: fba75d09065fcc76a25dcf386b1d00d33f5175af
on:
workflow_dispatch:
@@ -12,8 +13,6 @@ on:
- ".github/workflows/build_windows.yml"
- "**.c"
- "**.h"
- "**CMakeLists.txt"
- "**.cmake"
- "windows/**"
- "src/rust/**"
pull_request:
@@ -22,118 +21,108 @@ on:
- ".github/workflows/build_windows.yml"
- "**.c"
- "**.h"
- "**CMakeLists.txt"
- "**.cmake"
- "windows/**"
- "src/rust/**"
jobs:
build:
build_release:
runs-on: windows-2022
steps:
- name: Check out repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2.0.0
with:
msbuild-architecture: x64
# Install GPAC (fast, ~30s, not worth caching complexity)
- name: Install gpac
run: choco install gpac --version 2.4.0 --no-progress
# Use lukka/run-vcpkg for better caching
run: choco install gpac --version 2.4.0
- name: Setup vcpkg
uses: lukka/run-vcpkg@v11
id: runvcpkg
with:
vcpkgGitCommitId: ${{ env.VCPKG_COMMIT }}
vcpkgDirectory: ${{ github.workspace }}/vcpkg
vcpkgJsonGlob: 'windows/vcpkg.json'
# Cache vcpkg installed packages separately for faster restores
- name: Cache vcpkg installed packages
id: vcpkg-installed-cache
uses: actions/cache@v5
with:
path: ${{ github.workspace }}/vcpkg/installed
key: vcpkg-installed-${{ runner.os }}-${{ env.VCPKG_COMMIT }}-${{ hashFiles('windows/vcpkg.json') }}
restore-keys: |
vcpkg-installed-${{ runner.os }}-${{ env.VCPKG_COMMIT }}-
- name: Install vcpkg dependencies
if: steps.vcpkg-installed-cache.outputs.cache-hit != 'true'
run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/
working-directory: windows
# Cache Rust/Cargo artifacts
- name: Cache Cargo registry
uses: actions/cache@v5
run: mkdir C:\vcpkg\.cache
- name: Cache vcpkg
id: cache
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
${{ runner.os }}-cargo-registry-
# Cache Cargo build artifacts - rust.bat sets CARGO_TARGET_DIR to windows/
# which results in artifacts at windows/x86_64-pc-windows-msvc/
- name: Cache Cargo build artifacts
uses: actions/cache@v5
C:\vcpkg\.cache
key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }}
- name: Build vcpkg
run: |
git clone https://github.com/microsoft/vcpkg
./vcpkg/bootstrap-vcpkg.bat
- name: Install dependencies
run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/
working-directory: windows
- uses: actions-rs/toolchain@v1
with:
path: ${{ github.workspace }}/windows/x86_64-pc-windows-msvc
key: ${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-${{ hashFiles('src/rust/**/*.rs') }}
restore-keys: |
${{ runner.os }}-cargo-build-${{ hashFiles('**/Cargo.lock') }}-
${{ runner.os }}-cargo-build-
- name: Setup Rust toolchain
uses: dtolnay/rust-toolchain@stable
toolchain: stable
override: true
- name: Install Win 10 SDK
uses: ilammy/msvc-dev-cmd@v1
# Build Release-Full
- name: Build Release-Full
- name: build Release-Full
env:
LIBCLANG_PATH: "C:\\Program Files\\LLVM\\lib"
LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config"
CARGO_TARGET_DIR: "..\\..\\windows"
BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
run: msbuild ccextractor.sln /p:Configuration=Release-Full /p:Platform=x64
working-directory: ./windows
- name: Display Release version information
- name: Display version information
run: ./ccextractorwinfull.exe --version
working-directory: ./windows/x64/Release-Full
- name: Upload Release artifact
uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v4
with:
name: CCExtractor Windows Release build
path: |
./windows/x64/Release-Full/ccextractorwinfull.exe
./windows/x64/Release-Full/*.dll
# Build Debug-Full (reuses cached Cargo artifacts)
- name: Build Debug-Full
build_debug:
runs-on: windows-2022
steps:
- name: Check out repository
uses: actions/checkout@v4
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2.0.0
with:
msbuild-architecture: x64
- name: Install gpac
run: choco install gpac --version 2.4.0
- name: Setup vcpkg
run: mkdir C:\vcpkg\.cache
- name: Cache vcpkg
id: cache
uses: actions/cache@v4
with:
path: |
C:\vcpkg\.cache
key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }}
- name: Build vcpkg
run: |
git clone https://github.com/microsoft/vcpkg
./vcpkg/bootstrap-vcpkg.bat
- name: Install dependencies
run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/
working-directory: windows
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
- name: Install Win 10 SDK
uses: ilammy/msvc-dev-cmd@v1
- name: build Debug-Full
env:
LIBCLANG_PATH: "C:\\Program Files\\LLVM\\lib"
LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config"
CARGO_TARGET_DIR: "..\\..\\windows"
BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
run: msbuild ccextractor.sln /p:Configuration=Debug-Full /p:Platform=x64
working-directory: ./windows
- name: Display Debug version information
- name: Display version information
continue-on-error: true
run: ./ccextractorwinfull.exe --version
working-directory: ./windows/x64/Debug-Full
- name: Upload Debug artifact
uses: actions/upload-artifact@v6
- uses: actions/upload-artifact@v4
with:
name: CCExtractor Windows Debug build
path: |

View File

@@ -19,7 +19,7 @@ jobs:
format:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: Format code
run: |
find src/ -type f -not -path "src/thirdparty/*" -not -path "src/lib_ccx/zvbi/*" -name '*.c' -not -path "src/GUI/icon_data.c" | xargs clang-format -i
@@ -33,9 +33,9 @@ jobs:
run:
working-directory: ${{ matrix.workdir }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: |
${{ matrix.workdir }}/.cargo/registry

View File

@@ -1,15 +0,0 @@
name: Bump Homebrew Formula
on:
release:
types: [published]
jobs:
homebrew:
runs-on: ubuntu-latest
steps:
- name: Update Homebrew formula
uses: dawidd6/action-homebrew-bump-formula@v7
with:
token: ${{ secrets.HOMEBREW_GITHUB_API_TOKEN }}
formula: ccextractor

View File

@@ -1,136 +0,0 @@
# Publish to Chocolatey Community Repository
#
# PREREQUISITES:
# 1. Create a Chocolatey account at https://community.chocolatey.org/account/Register
# 2. Get your API key from https://community.chocolatey.org/account
# 3. Add the API key as repository secret: CHOCOLATEY_API_KEY
#
# Reference: https://docs.chocolatey.org/en-us/create/create-packages-quick-start
name: Publish to Chocolatey
on:
release:
types: [released]
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to publish (e.g., v0.96.1)'
required: true
type: string
jobs:
publish:
runs-on: windows-latest
steps:
- name: Checkout repository
uses: actions/checkout@v6
- name: Get version from tag
id: version
shell: bash
run: |
TAG="${{ github.event.inputs.release_tag || github.event.release.tag_name }}"
# Strip 'v' prefix if present
VERSION="${TAG#v}"
echo "version=$VERSION" >> $GITHUB_OUTPUT
echo "tag=$TAG" >> $GITHUB_OUTPUT
- name: Download MSI from release
shell: pwsh
run: |
$version = "${{ steps.version.outputs.version }}"
$tag = "${{ steps.version.outputs.tag }}"
$msiUrl = "https://github.com/CCExtractor/ccextractor/releases/download/$tag/CCExtractor.$version.msi"
Write-Host "Downloading MSI from: $msiUrl"
Invoke-WebRequest -Uri $msiUrl -OutFile "CCExtractor.msi"
# Calculate SHA256 checksum
$hash = (Get-FileHash -Path "CCExtractor.msi" -Algorithm SHA256).Hash
Write-Host "SHA256: $hash"
echo "MSI_CHECKSUM=$hash" >> $env:GITHUB_ENV
- name: Update nuspec version
shell: pwsh
run: |
$version = "${{ steps.version.outputs.version }}"
$nuspecPath = "packaging/chocolatey/ccextractor.nuspec"
$content = Get-Content $nuspecPath -Raw
$content = $content -replace '<version>.*</version>', "<version>$version</version>"
Set-Content -Path $nuspecPath -Value $content
Write-Host "Updated nuspec to version $version"
- name: Update install script
shell: pwsh
run: |
$version = "${{ steps.version.outputs.version }}"
$tag = "${{ steps.version.outputs.tag }}"
$checksum = $env:MSI_CHECKSUM
$installScript = "packaging/chocolatey/tools/chocolateyInstall.ps1"
$content = Get-Content $installScript -Raw
# Update URL
$newUrl = "https://github.com/CCExtractor/ccextractor/releases/download/$tag/CCExtractor.$version.msi"
$content = $content -replace "url64bit\s*=\s*'[^']*'", "url64bit = '$newUrl'"
# Update checksum
$content = $content -replace "checksum64\s*=\s*'[^']*'", "checksum64 = '$checksum'"
Set-Content -Path $installScript -Value $content
Write-Host "Updated install script with URL and checksum"
- name: Build Chocolatey package
shell: pwsh
run: |
cd packaging/chocolatey
choco pack ccextractor.nuspec
# List the generated package
Get-ChildItem *.nupkg
- name: Test package locally
shell: pwsh
run: |
cd packaging/chocolatey
$nupkg = Get-ChildItem *.nupkg | Select-Object -First 1
Write-Host "Testing package: $($nupkg.Name)"
# Install from local package
choco install ccextractor --source="'.;https://community.chocolatey.org/api/v2/'" --yes --force
# Verify installation
$ccx = Get-Command ccextractor -ErrorAction SilentlyContinue
if ($ccx) {
Write-Host "CCExtractor found at: $($ccx.Source)"
& ccextractor --version
} else {
Write-Host "CCExtractor not found in PATH, checking Program Files..."
$exePath = Join-Path $env:ProgramFiles "CCExtractor\ccextractor.exe"
if (Test-Path $exePath) {
& $exePath --version
}
}
- name: Push to Chocolatey
shell: pwsh
env:
CHOCOLATEY_API_KEY: ${{ secrets.CHOCOLATEY_API_KEY }}
run: |
cd packaging/chocolatey
$nupkg = Get-ChildItem *.nupkg | Select-Object -First 1
Write-Host "Pushing $($nupkg.Name) to Chocolatey..."
choco push $nupkg.Name --source="https://push.chocolatey.org/" --api-key="$env:CHOCOLATEY_API_KEY"
Write-Host "Package submitted to Chocolatey! It may take some time to be moderated and published."
- name: Upload package artifact
uses: actions/upload-artifact@v6
with:
name: chocolatey-package
path: packaging/chocolatey/*.nupkg

View File

@@ -1,38 +0,0 @@
# Publish to Windows Package Manager (winget)
#
# PREREQUISITES:
# 1. CCExtractor must already have ONE version in winget-pkgs before this works
# - Submit the initial manifest manually from packaging/winget/
# - PR to: https://github.com/microsoft/winget-pkgs
#
# 2. Create a fork of microsoft/winget-pkgs under the CCExtractor organization
# - https://github.com/CCExtractor/winget-pkgs (needs to be created)
#
# 3. Create a GitHub Personal Access Token (classic) with 'public_repo' scope
# - Add as repository secret: WINGET_TOKEN
#
# Reference: https://github.com/vedantmgoyal9/winget-releaser
name: Publish to WinGet
on:
release:
types: [released]
workflow_dispatch:
inputs:
release_tag:
description: 'Release tag to publish (e.g., v0.96.1)'
required: true
type: string
jobs:
publish:
runs-on: windows-latest
steps:
- name: Publish to WinGet
uses: vedantmgoyal9/winget-releaser@v2
with:
identifier: CCExtractor.CCExtractor
installers-regex: '\.msi$' # Only use the MSI installer
token: ${{ secrets.WINGET_TOKEN }}
release-tag: ${{ github.event.inputs.release_tag || github.event.release.tag_name }}

View File

@@ -5,64 +5,23 @@ on:
types:
- created
permissions:
contents: write
env:
RUSTFLAGS: -Ctarget-feature=+crt-static
VCPKG_DEFAULT_TRIPLET: x64-windows-static
VCPKG_DEFAULT_BINARY_CACHE: C:\vcpkg\.cache
VCPKG_COMMIT: ab2977be50c702126336e5088f4836060733c899
jobs:
build_windows:
runs-on: windows-2022
runs-on: windows-latest
steps:
- name: Check out repository
uses: actions/checkout@v6
uses: actions/checkout@v4
- name: Get the version
id: get_version
run: |
# Extract version from tag, strip 'v' prefix and everything after first dash
VERSION=${GITHUB_REF/refs\/tags\/v/}
VERSION=${VERSION%%-*}
# Save display version for filenames (e.g., 0.96.1)
echo ::set-output name=DISPLAY_VERSION::$VERSION
# Count dots to determine version format
DOTS="${VERSION//[^.]}"
PART_COUNT=$((${#DOTS} + 1))
# MSI requires 4-part version (major.minor.build.revision)
if [ "$PART_COUNT" -eq 2 ]; then
MSI_VERSION="${VERSION}.0.0"
elif [ "$PART_COUNT" -eq 3 ]; then
MSI_VERSION="${VERSION}.0"
else
MSI_VERSION="${VERSION}"
fi
echo ::set-output name=VERSION::$MSI_VERSION
run: echo ::set-output name=VERSION::${GITHUB_REF/refs\/tags\/v/}
shell: bash
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v2.0.0
- name: Install llvm and clang
uses: egor-tensin/setup-clang@v1
with:
msbuild-architecture: x64
- name: Install gpac
run: choco install gpac --version 2.4.0
- name: Setup vcpkg
run: mkdir C:\vcpkg\.cache
- name: Cache vcpkg
id: cache
uses: actions/cache@v5
with:
path: |
C:\vcpkg\.cache
key: vcpkg-${{ runner.os }}-${{ env.VCPKG_COMMIT }}
- name: Build vcpkg
run: |
git clone https://github.com/microsoft/vcpkg
./vcpkg/bootstrap-vcpkg.bat
- name: Install dependencies
run: ${{ github.workspace }}/vcpkg/vcpkg.exe install --x-install-root ${{ github.workspace }}/vcpkg/installed/
working-directory: windows
version: latest
platform: x64
- uses: actions-rs/toolchain@v1
with:
toolchain: stable
@@ -75,24 +34,15 @@ jobs:
LLVM_CONFIG_PATH: "C:\\Program Files\\LLVM\\bin\\llvm-config"
CARGO_TARGET_DIR: "..\\..\\windows"
BINDGEN_EXTRA_CLANG_ARGS: -fmsc-version=0
VCPKG_ROOT: ${{ github.workspace }}/vcpkg
run: msbuild ccextractor.sln /p:Configuration=Release-Full /p:Platform=x64
run: msbuild ccextractor.sln /p:Configuration=Release-Full /p:Platform=Win32
working-directory: ./windows
- name: Copy files to directory for installer
run: mkdir installer; cp ./x64/Release-Full/ccextractorwinfull.exe ./installer; cp ./x64/Release-Full/*.dll ./installer
working-directory: ./windows
- name: Download tessdata for OCR support
run: |
mkdir -p ./installer/tessdata
# Download English traineddata from tessdata_fast (smaller, faster, good for most use cases)
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_fast/raw/main/eng.traineddata" -OutFile "./installer/tessdata/eng.traineddata"
# Download OSD (Orientation and Script Detection) for automatic script detection
Invoke-WebRequest -Uri "https://github.com/tesseract-ocr/tessdata_fast/raw/main/osd.traineddata" -OutFile "./installer/tessdata/osd.traineddata"
run: mkdir installer; cp ./Release-Full/ccextractorwinfull.exe ./installer; cp ./Release-Full/*.dll ./installer
working-directory: ./windows
- name: install WiX
run: dotnet tool uninstall --global wix; dotnet tool install --global wix --version 6.0.2 && wix extension add -g WixToolset.UI.wixext/6.0.2
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 list -g
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
@@ -100,38 +50,34 @@ jobs:
run: ls
working-directory: ./windows
- name: Unzip Flutter GUI
run: Expand-Archive -Path ./windows.zip -DestinationPath ./installer -Force
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: Create portable zip
run: Compress-Archive -Path ./installer/* -DestinationPath ./CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}_win_portable.zip
run: Compress-Archive -Path ./installer/* -DestinationPath ./CCExtractor_win_portable.zip
working-directory: ./windows
- name: Build installer
run: wix build -arch x64 -ext WixToolset.UI.wixext -d "AppVersion=${{ steps.get_version.outputs.VERSION }}" -o CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.msi installer.wxs CustomUI.wxs
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@v3.0
with:
files: './windows/CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.msi;./windows/CCExtractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}_win_portable.zip'
repo-token: ${{ secrets.GITHUB_TOKEN }}
files: './windows/CCExtractor.msi;./windows/CCExtractor_win_portable.zip'
repo-token: ${{ secrets.GITHUB_TOKEN }}
create_linux_package:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
path: ./ccextractor
- name: Get the version
id: get_version
run: |
VERSION=${GITHUB_REF/refs\/tags\/v/}
VERSION=${VERSION%%-*}
echo ::set-output name=DISPLAY_VERSION::$VERSION
- name: Generate version info for this release
run: ./ccextractor/linux/generate_version_info.sh
- name: Create .tar.gz without git and windows folders
run: tar -pczf ./ccextractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.tar.gz --exclude "ccextractor/windows" --exclude "ccextractor/.git" ccextractor
run: tar -pczf ./ccextractor_minimal.tar.gz --exclude "ccextractor/windows" --exclude "ccextractor/.git" ccextractor
- name: Upload as asset
uses: AButler/upload-release-assets@v3.0
with:
files: './ccextractor.${{ steps.get_version.outputs.DISPLAY_VERSION }}.tar.gz'
files: './ccextractor_minimal.tar.gz'
repo-token: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -18,9 +18,9 @@ jobs:
run:
working-directory: ./src/rust
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- name: cache
uses: actions/cache@v5
uses: actions/cache@v4
with:
path: |
src/rust/.cargo/registry

11
.gitignore vendored
View File

@@ -17,7 +17,6 @@ CVS
mac/ccextractor
linux/ccextractor
linux/depend
linux/build_scan/
windows/x86_64-pc-windows-msvc/**
windows/Debug/**
windows/Debug-OCR/**
@@ -29,7 +28,6 @@ windows/Debug-Full/**
windows/x64/**
windows/ccextractor.VC.db
build/
build_*/
####
# Python
@@ -145,9 +143,6 @@ bazel*
#Intellij IDEs
.idea/
# Plans (local only)
plans/
# Rust build and MakeFiles (and CMake files)
src/rust/CMakeFiles/
src/rust/CMakeCache.txt
@@ -160,9 +155,3 @@ windows/*/debug/*
windows/*/CACHEDIR.TAG
windows/.rustc_info.json
linux/configure~
# Plans and temporary files
plans/
tess.log
**/tess.log
ut=srt*

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.96.5
V = 0.94
DISTFILES = ccextractor.${V:S/.//}-src.zip
MASTER_SITES = ${MASTER_SITE_SOURCEFORGE:=ccextractor/}
DISTNAME = ccextractor-$V

View File

@@ -2,6 +2,7 @@
# CCExtractor
<a href="https://travis-ci.org/CCExtractor/ccextractor"><img src="https://raw.githubusercontent.com/CCExtractor/ccextractor-org-media/master/static/macOS-build-badge-logo.png" width="20"></a> [![Build Status](https://travis-ci.org/CCExtractor/ccextractor.svg?branch=master)](https://travis-ci.org/CCExtractor/ccextractor)
[![Sample-Platform Build Status Windows](https://sampleplatform.ccextractor.org/static/img/status/build-windows.svg?maxAge=1800)](https://sampleplatform.ccextractor.org/test/master/windows)
[![Sample-Platform Build Status Linux](https://sampleplatform.ccextractor.org/static/img/status/build-linux.svg?maxAge=1800)](https://sampleplatform.ccextractor.org/test/master/linux)
[![SourceForge](https://img.shields.io/badge/SourceForge%20downloads-213k%2Ftotal-brightgreen.svg)](https://sourceforge.net/projects/ccextractor/)
@@ -28,25 +29,6 @@ The core functionality is written in C. Other languages used include C++ and Pyt
Downloads for precompiled binaries and source code can be found [on our website](https://ccextractor.org/public/general/downloads/).
### Windows Package Managers
**WinGet:**
```powershell
winget install CCExtractor.CCExtractor
```
**Chocolatey:**
```powershell
choco install ccextractor
```
**Scoop:**
```powershell
scoop bucket add extras
scoop install ccextractor
```
Extracting subtitles is relatively simple. Just run the following command:
`ccextractor <input>`
@@ -62,34 +44,6 @@ You can also find the list of parameters and their brief description by running
You can find sample files on [our website](https://ccextractor.org/public/general/tvsamples/) to test the software.
### Building from Source
- [Building on Windows using WSL](docs/build-wsl.md)
#### Linux (Autotools) build notes
CCExtractor also supports an autotools-based build system under the `linux/`
directory.
Important notes:
- The autotools workflow lives inside `linux/`. The `configure` script is
generated there and should be run from that directory.
- Typical build steps are:
```
cd linux
./autogen.sh
./configure
make
```
- Rust support is enabled automatically if `cargo` and `rustc` are available
on the system. In that case, Rust components are built and linked during
`make`.
- If you encounter unexpected build or linking issues, a clean rebuild
(`make clean` or a fresh clone) is recommended, especially when Rust is
involved.
This build flow has been tested on Linux and WSL.
## Compiling CCExtractor
To learn more about how to compile and build CCExtractor for your platform check the [compilation guide](https://github.com/CCExtractor/ccextractor/blob/master/docs/COMPILATION.MD).

View File

@@ -1,239 +0,0 @@
# CCExtractor Docker Build
#
# Build variants via BUILD_TYPE argument:
# - minimal: Basic CCExtractor without OCR
# - ocr: CCExtractor with OCR support (default)
# - hardsubx: CCExtractor with burned-in subtitle extraction (requires FFmpeg)
#
# Source options via USE_LOCAL_SOURCE argument:
# - 0 (default): Clone from GitHub (standalone Dockerfile usage)
# - 1: Use local source (when building from cloned repo)
#
# Build examples:
#
# # Standalone (just the Dockerfile, clones from GitHub):
# docker build -t ccextractor docker/
# docker build --build-arg BUILD_TYPE=hardsubx -t ccextractor docker/
#
# # From cloned repository (faster, uses local source):
# docker build --build-arg USE_LOCAL_SOURCE=1 -f docker/Dockerfile -t ccextractor .
# docker build --build-arg USE_LOCAL_SOURCE=1 --build-arg BUILD_TYPE=minimal -f docker/Dockerfile -t ccextractor .
ARG DEBIAN_VERSION=bookworm-slim
FROM debian:${DEBIAN_VERSION} AS base
FROM base AS builder
# Build arguments
ARG BUILD_TYPE=ocr
ARG USE_LOCAL_SOURCE=0
# BUILD_TYPE: minimal, ocr, hardsubx
# USE_LOCAL_SOURCE: 0 = git clone, 1 = copy local source
# Avoid interactive prompts during package installation
ENV DEBIAN_FRONTEND=noninteractive
# Install base build dependencies
RUN apt-get update && apt-get install -y --no-install-recommends \
git \
curl \
ca-certificates \
gcc \
g++ \
cmake \
make \
pkg-config \
bash \
zlib1g-dev \
libpng-dev \
libjpeg-dev \
libssl-dev \
libfreetype-dev \
libxml2-dev \
libcurl4-gnutls-dev \
clang \
libclang-dev \
&& rm -rf /var/lib/apt/lists/*
# Install Rust toolchain
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
ENV PATH="/root/.cargo/bin:${PATH}"
# Install OCR dependencies (for ocr and hardsubx builds)
RUN if [ "$BUILD_TYPE" = "ocr" ] || [ "$BUILD_TYPE" = "hardsubx" ]; then \
apt-get update && apt-get install -y --no-install-recommends \
tesseract-ocr \
libtesseract-dev \
libleptonica-dev \
&& rm -rf /var/lib/apt/lists/*; \
fi
# Install FFmpeg dependencies (for hardsubx build)
RUN if [ "$BUILD_TYPE" = "hardsubx" ]; then \
apt-get update && apt-get install -y --no-install-recommends \
libavcodec-dev \
libavformat-dev \
libavutil-dev \
libswscale-dev \
libswresample-dev \
libavfilter-dev \
libavdevice-dev \
&& rm -rf /var/lib/apt/lists/*; \
fi
# Build and install GPAC library
WORKDIR /root
RUN git clone -b v2.4.0 --depth 1 https://github.com/gpac/gpac
WORKDIR /root/gpac
RUN ./configure && make -j$(nproc) lib && make install-lib && ldconfig
WORKDIR /root
RUN rm -rf /root/gpac
# Get CCExtractor source (either clone or copy based on USE_LOCAL_SOURCE)
WORKDIR /root
# First, copy local source if provided (will be empty dir if building standalone)
COPY . /root/ccextractor-local/
# Then get source: use local copy if USE_LOCAL_SOURCE=1 and source exists,
# otherwise clone from GitHub
RUN if [ "$USE_LOCAL_SOURCE" = "1" ] && [ -f /root/ccextractor-local/src/ccextractor.c ]; then \
echo "Using local source"; \
mv /root/ccextractor-local /root/ccextractor; \
else \
echo "Cloning from GitHub"; \
rm -rf /root/ccextractor-local; \
git clone --depth 1 https://github.com/CCExtractor/ccextractor.git /root/ccextractor; \
fi
WORKDIR /root/ccextractor/linux
# Generate build info
RUN ./pre-build.sh
# Build Rust library with appropriate features
RUN if [ "$BUILD_TYPE" = "hardsubx" ]; then \
cd ../src/rust && \
CARGO_TARGET_DIR=../../linux/rust cargo build --release --features hardsubx_ocr; \
else \
cd ../src/rust && \
CARGO_TARGET_DIR=../../linux/rust cargo build --release; \
fi
RUN cp rust/release/libccx_rust.a ./libccx_rust.a
# Compile CCExtractor
RUN if [ "$BUILD_TYPE" = "minimal" ]; then \
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DGPAC_64_BITS"; \
BLD_INCLUDE="-I../src -I../src/lib_ccx/ -I /usr/include/gpac/ -I../src/thirdparty/libpng -I../src/thirdparty/zlib -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash -I../src/thirdparty -I../src/thirdparty/freetype/include"; \
BLD_LINKER="-lm -Wl,--allow-multiple-definition -lpthread -ldl -lgpac ./libccx_rust.a"; \
elif [ "$BUILD_TYPE" = "hardsubx" ]; then \
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DENABLE_HARDSUBX -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DGPAC_64_BITS"; \
BLD_INCLUDE="-I../src -I /usr/include/leptonica/ -I /usr/include/tesseract/ -I../src/lib_ccx/ -I /usr/include/gpac/ -I../src/thirdparty/libpng -I../src/thirdparty/zlib -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash -I../src/thirdparty -I../src/thirdparty/freetype/include"; \
BLD_LINKER="-lm -Wl,--allow-multiple-definition -ltesseract -lleptonica -lpthread -ldl -lgpac -lswscale -lavutil -lavformat -lavcodec -lavfilter -lswresample ./libccx_rust.a"; \
else \
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP -DGPAC_64_BITS"; \
BLD_INCLUDE="-I../src -I /usr/include/leptonica/ -I /usr/include/tesseract/ -I../src/lib_ccx/ -I /usr/include/gpac/ -I../src/thirdparty/libpng -I../src/thirdparty/zlib -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash -I../src/thirdparty -I../src/thirdparty/freetype/include"; \
BLD_LINKER="-lm -Wl,--allow-multiple-definition -ltesseract -lleptonica -lpthread -ldl -lgpac ./libccx_rust.a"; \
fi && \
SRC_LIBPNG="$(find ../src/thirdparty/libpng/ -name '*.c')" && \
SRC_ZLIB="$(find ../src/thirdparty/zlib/ -name '*.c')" && \
SRC_CCX="$(find ../src/lib_ccx/ -name '*.c')" && \
SRC_GPAC="$(find /usr/include/gpac/ -name '*.c' 2>/dev/null || true)" && \
SRC_HASH="$(find ../src/thirdparty/lib_hash/ -name '*.c')" && \
SRC_UTF8PROC="../src/thirdparty/utf8proc/utf8proc.c" && \
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \
../src/thirdparty/freetype/base/ftbase.c \
../src/thirdparty/freetype/base/ftbbox.c \
../src/thirdparty/freetype/base/ftbdf.c \
../src/thirdparty/freetype/base/ftbitmap.c \
../src/thirdparty/freetype/base/ftcid.c \
../src/thirdparty/freetype/base/ftfntfmt.c \
../src/thirdparty/freetype/base/ftfstype.c \
../src/thirdparty/freetype/base/ftgasp.c \
../src/thirdparty/freetype/base/ftglyph.c \
../src/thirdparty/freetype/base/ftgxval.c \
../src/thirdparty/freetype/base/ftinit.c \
../src/thirdparty/freetype/base/ftlcdfil.c \
../src/thirdparty/freetype/base/ftmm.c \
../src/thirdparty/freetype/base/ftotval.c \
../src/thirdparty/freetype/base/ftpatent.c \
../src/thirdparty/freetype/base/ftpfr.c \
../src/thirdparty/freetype/base/ftstroke.c \
../src/thirdparty/freetype/base/ftsynth.c \
../src/thirdparty/freetype/base/ftsystem.c \
../src/thirdparty/freetype/base/fttype1.c \
../src/thirdparty/freetype/base/ftwinfnt.c \
../src/thirdparty/freetype/bdf/bdf.c \
../src/thirdparty/freetype/bzip2/ftbzip2.c \
../src/thirdparty/freetype/cache/ftcache.c \
../src/thirdparty/freetype/cff/cff.c \
../src/thirdparty/freetype/cid/type1cid.c \
../src/thirdparty/freetype/gzip/ftgzip.c \
../src/thirdparty/freetype/lzw/ftlzw.c \
../src/thirdparty/freetype/pcf/pcf.c \
../src/thirdparty/freetype/pfr/pfr.c \
../src/thirdparty/freetype/psaux/psaux.c \
../src/thirdparty/freetype/pshinter/pshinter.c \
../src/thirdparty/freetype/psnames/psnames.c \
../src/thirdparty/freetype/raster/raster.c \
../src/thirdparty/freetype/sfnt/sfnt.c \
../src/thirdparty/freetype/smooth/smooth.c \
../src/thirdparty/freetype/truetype/truetype.c \
../src/thirdparty/freetype/type1/type1.c \
../src/thirdparty/freetype/type42/type42.c \
../src/thirdparty/freetype/winfonts/winfnt.c" && \
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG $SRC_HASH $SRC_UTF8PROC $SRC_FREETYPE" && \
gcc $BLD_FLAGS $BLD_INCLUDE -o ccextractor $BLD_SOURCES $BLD_LINKER
# Copy binary to known location
RUN cp /root/ccextractor/linux/ccextractor /ccextractor
# Final minimal image
FROM base AS final
ARG BUILD_TYPE=ocr
# Avoid interactive prompts
ENV DEBIAN_FRONTEND=noninteractive
# Install runtime dependencies based on build type
RUN apt-get update && apt-get install -y --no-install-recommends \
libpng16-16 \
libjpeg62-turbo \
zlib1g \
libssl3 \
libcurl4 \
&& rm -rf /var/lib/apt/lists/*
# OCR runtime dependencies
RUN if [ "$BUILD_TYPE" = "ocr" ] || [ "$BUILD_TYPE" = "hardsubx" ]; then \
apt-get update && apt-get install -y --no-install-recommends \
tesseract-ocr \
liblept5 \
&& rm -rf /var/lib/apt/lists/*; \
fi
# HardSubX runtime dependencies
RUN if [ "$BUILD_TYPE" = "hardsubx" ]; then \
apt-get update && apt-get install -y --no-install-recommends \
libavcodec59 \
libavformat59 \
libavutil57 \
libswscale6 \
libswresample4 \
libavfilter8 \
libavdevice59 \
&& rm -rf /var/lib/apt/lists/*; \
fi
# Copy GPAC library from builder
COPY --from=builder /usr/local/lib/libgpac.so* /usr/local/lib/
# Update library cache
RUN ldconfig
# Copy CCExtractor binary
COPY --from=builder /ccextractor /ccextractor
ENTRYPOINT ["/ccextractor"]

View File

@@ -1,91 +1,61 @@
# CCExtractor Docker Image
# CCExtractor Docker image
This Dockerfile builds CCExtractor with support for multiple build variants.
This dockerfile prepares a minimalist Docker image with CCExtractor. It compiles CCExtractor from sources following instructions from the [Compilation Guide](https://github.com/CCExtractor/ccextractor/blob/master/docs/COMPILATION.MD).
## Build Variants
You can install the latest build of this image by running `docker pull CCExtractor/ccextractor`
| Variant | Description | Features |
|---------|-------------|----------|
| `minimal` | Basic CCExtractor | No OCR support |
| `ocr` | With OCR support (default) | Tesseract OCR for bitmap subtitles |
| `hardsubx` | With burned-in subtitle extraction | OCR + FFmpeg for hardcoded subtitles |
## Build
## Building
### Standalone Build (from Dockerfile only)
You can build CCExtractor using just the Dockerfile - it will clone the source from GitHub:
You can build the Docker image directly from the Dockerfile provided in [docker](https://github.com/CCExtractor/ccextractor/tree/master/docker) directory of CCExtractor source
```bash
# Default build (OCR enabled)
docker build -t ccextractor docker/
# Minimal build (no OCR)
docker build --build-arg BUILD_TYPE=minimal -t ccextractor docker/
# HardSubX build (OCR + FFmpeg for burned-in subtitles)
docker build --build-arg BUILD_TYPE=hardsubx -t ccextractor docker/
$ git clone https://github.com/CCExtractor/ccextractor.git && cd ccextractor
$ cd docker/
$ docker build -t ccextractor .
```
### Build from Cloned Repository (faster)
If you have already cloned the repository, you can use local source for faster builds:
```bash
git clone https://github.com/CCExtractor/ccextractor.git
cd ccextractor
# Default build (OCR enabled)
docker build --build-arg USE_LOCAL_SOURCE=1 -f docker/Dockerfile -t ccextractor .
# Minimal build
docker build --build-arg USE_LOCAL_SOURCE=1 --build-arg BUILD_TYPE=minimal -f docker/Dockerfile -t ccextractor .
# HardSubX build
docker build --build-arg USE_LOCAL_SOURCE=1 --build-arg BUILD_TYPE=hardsubx -f docker/Dockerfile -t ccextractor .
```
## Build Arguments
| Argument | Default | Description |
|----------|---------|-------------|
| `BUILD_TYPE` | `ocr` | Build variant: `minimal`, `ocr`, or `hardsubx` |
| `USE_LOCAL_SOURCE` | `0` | Set to `1` to use local source instead of cloning |
| `DEBIAN_VERSION` | `bookworm-slim` | Debian version to use as base |
## Usage
### Basic Usage
The CCExtractor Docker image can be used in several ways, depending on your needs.
```bash
# Show version
docker run --rm ccextractor --version
# Show help
docker run --rm ccextractor --help
# General usage
$ docker run ccextractor:latest <features>
```
### Processing Local Files
1. Process a local file & use `-o` flag
Mount your local directory to process files:
To process a local video file, mount a directory containing the input file inside the container:
```bash
# Process a video file with output file
docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor input.mp4 -o output.srt
# Use `-o` to specifying output file
$ docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor:latest input.mp4 -o output.srt
# Process using stdout
docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor input.mp4 --stdout > output.srt
# Alternatively use `--stdout` feature
$ docker run --rm -v $(pwd):$(pwd) -w $(pwd) ccextractor:latest input.mp4 --stdout > output.srt
```
### Interactive Mode
Run this command from where your input video file is present, and change `input.mp4` & `output.srt` with the actual name of files.
2. Enter an interactive environment
If you need to run CCExtractor with additional options or perform other tasks within the container, you can enter an interactive environment:
bash
```bash
docker run --rm -it --entrypoint=/bin/bash ccextractor
$ docker run --rm -it --entrypoint='sh' ccextractor:latest
```
## Image Size
This will start a Bash shell inside the container, allowing you to run CCExtractor commands manually or perform other operations.
The multi-stage build produces runtime images:
- `minimal`: ~130MB
- `ocr`: ~215MB (includes Tesseract)
- `hardsubx`: ~610MB (includes Tesseract + FFmpeg)
### Example
I run help command in image built from `dockerfile`
```bash
$ docker build -t ccextractor .
$ docker run --rm ccextractor:latest --help
```
This will show the `--help` message of CCExtractor tool
From there you can see all the features and flags which can be used.

46
docker/dockerfile Normal file
View File

@@ -0,0 +1,46 @@
FROM alpine:latest as base
FROM base as builder
RUN apk add --no-cache --update git curl gcc cmake glew glfw \
tesseract-ocr-dev leptonica-dev clang-dev llvm-dev make pkgconfig \
zlib-dev libpng-dev libjpeg-turbo-dev openssl-dev freetype-dev libxml2-dev bash cargo
WORKDIR /root
RUN git clone -b v2.4.0 https://github.com/gpac/gpac
WORKDIR /root/gpac/
RUN ./configure && make -j$(nproc) && make install-lib
WORKDIR /root
RUN rm -rf /root/gpac
RUN git clone https://github.com/CCExtractor/ccextractor.git
WORKDIR /root/ccextractor/linux
RUN ./pre-build.sh && ./build
RUN cp /root/ccextractor/linux/ccextractor /ccextractor && rm -rf ~/ccextractor
FROM base as final
COPY --from=builder /lib/ld-musl-x86_64.so.1 /lib/
COPY --from=builder /usr/lib/libtesseract.so.5 /usr/lib/
COPY --from=builder /usr/lib/libleptonica.so.6 /usr/lib/
COPY --from=builder /usr/local/lib/libgpac.so.12 /usr/local/lib/
COPY --from=builder /usr/lib/libstdc++.so.6 /usr/lib/
COPY --from=builder /usr/lib/libgcc_s.so.1 /usr/lib/
COPY --from=builder /usr/lib/libgomp.so.1 /usr/lib/
COPY --from=builder /usr/lib/libpng16.so.16 /usr/lib/
COPY --from=builder /usr/lib/libjpeg.so.8 /usr/lib/
COPY --from=builder /usr/lib/libgif.so.7 /usr/lib/
COPY --from=builder /usr/lib/libtiff.so.6 /usr/lib/
COPY --from=builder /usr/lib/libwebp.so.7 /usr/lib/
COPY --from=builder /usr/lib/libwebpmux.so.3 /usr/lib/
COPY --from=builder /usr/lib/libz.so.1 /lib/
COPY --from=builder /usr/lib/libssl.so.3 /lib/
COPY --from=builder /usr/lib/libcrypto.so.3 /lib/
COPY --from=builder /usr/lib/liblzma.so.5 /usr/lib/
COPY --from=builder /usr/lib/libzstd.so.1 /usr/lib/
COPY --from=builder /usr/lib/libsharpyuv.so.0 /usr/lib/
COPY --from=builder /ccextractor /
ENTRYPOINT [ "/ccextractor" ]

View File

@@ -1,157 +0,0 @@
# Building CCExtractor on macOS using System Libraries (-system-libs)
## Overview
This document explains how to build CCExtractor on macOS using system-installed libraries instead of bundled third-party libraries.
This build mode is required for Homebrew compatibility and is enabled via the `-system-libs` flag introduced in PR #1862.
## Why is -system-libs needed?
### Background
CCExtractor was removed from Homebrew (homebrew-core) because:
- Homebrew does not allow bundling third-party libraries
- The default CCExtractor build compiles libraries from `src/thirdparty/`
- This violates Homebrew packaging policies
### What -system-libs fixes
The `-system-libs` flag allows CCExtractor to:
- Use system-installed libraries via Homebrew
- Resolve headers and linker flags using `pkg-config`
- Skip compiling bundled copies of common libraries
This makes CCExtractor acceptable for Homebrew packaging.
## Build Modes Explained
### 1⃣ Default Build (Bundled Libraries)
**Command:**
```bash
./mac/build.command
```
**Behavior:**
- Compiles bundled libraries:
- `freetype`
- `libpng`
- `zlib`
- `utf8proc`
- Self-contained binary
- Larger size
- Suitable for standalone builds
### 2⃣ System Libraries Build (Homebrew-compatible)
**Command:**
```bash
./mac/build.command -system-libs
```
**Behavior:**
- Uses system libraries via `pkg-config`
- Does not compile bundled libraries
- Smaller binary
- Faster build
- Required for Homebrew
## Required Homebrew Dependencies
Install required dependencies:
```bash
brew install pkg-config autoconf automake libtool \
gpac freetype libpng protobuf-c utf8proc zlib
```
**Optional** (OCR / HARDSUBX support):
```bash
brew install tesseract leptonica ffmpeg
```
## How to Build
```bash
cd mac
./build.command -system-libs
```
**Verify:**
```bash
./ccextractor --version
```
## What Changes Internally with -system-libs
### Libraries NOT compiled (system-provided)
- **FreeType**
- **libpng**
- **zlib**
- **utf8proc**
### Libraries STILL bundled
- **lib_hash** (Custom SHA-256 implementation, no system equivalent)
## CI Coverage
A new CI job was added:
- `build_shell_system_libs`
**What it does:**
- Installs Homebrew dependencies
- Runs `./build.command -system-libs`
- Verifies the binary runs correctly
This ensures Homebrew-compatible builds stay working.
## Verification (Local)
You can confirm system libraries are used:
```bash
otool -L mac/ccextractor
```
**Expected output includes paths like:**
```
/opt/homebrew/opt/gpac/lib/libgpac.dylib
```
## Homebrew Formula Usage (Future)
Example formula snippet:
```ruby
def install
system "./mac/build.command", "-system-libs"
bin.install "mac/ccextractor"
end
```
## Summary
- `-system-libs` is opt-in
- Default build remains unchanged
- Enables CCExtractor to return to Homebrew
- Fully tested in CI and locally
## Related
- **PR #1862** — Add `-system-libs` flag
- **Issue #1580** — Homebrew compatibility
- **Issue #1534** — System library support

View File

@@ -1,98 +1,58 @@
0.96.6 (unreleased)
-------------------
- Fix: DVB EIT start time BCD decoding in XMLTV output causing invalid timestamps (#1835)
- New: Add Snap packaging support with Snapcraft configuration and GitHub Actions CI workflow.
- Fix: Clear status line output on Linux/WSL to prevent text artifacts (#2017)
- Fix: Prevent infinite loop on truncated MKV files
- Fix: Various memory safety and stability fixes in demuxers (MP4, PS, MKV, DVB)
- Fix: Delete empty output files instead of leaving 0-byte files (#1282)
- Fix: --mkvlang now supports BCP 47 language tags (e.g., en-US, zh-Hans-CN) and multiple codes
- Fix: segmentation fault when using --multiprogram
0.96.5 (2026-01-05)
-------------------
- New: CCExtractor is available again via Homebrew on macOS and Linux.
- New: Add support for raw CDP (Caption Distribution Packet) files (#1406)
- New: Add --scc-accurate-timing option for bandwidth-aware SCC output (#1120)
- Fix: MXF files containing CEA-708 captions not being detected/extracted (#1647)
- Docs: Add Windows WSL build instructions
- Fix: Security fixes (out-of-bounds read/write) in a few places in the legacy C code.
0.96.4 (2026-01-01)
-------------------
- New: Persistent CEA-708 decoder context - maintains state across multiple calls for proper subtitle continuity
- New: OCR character blacklist options (--ocr-blacklist, --ocr-blacklist-file) for improved accuracy
- New: OCR line-split option (--ocr-splitontimechange) for better subtitle segmentation
- Fix: 32-bit build failures on i686 and armv7l architectures
- Fix: Legacy command-line argument compatibility (-1, -2, -12, --sc, --svc)
- Fix: Prevent heap buffer overflow in Teletext processing (security fix)
- Fix: Prevent integer overflow leading to heap buffer overflow in Transport Stream handling (security fix)
- Fix: Lazy OCR initialization - only initialize when first DVB subtitle is encountered
- Build: Optimized Windows CI workflow for faster builds
- Fix: Updated GUI with version 0.7.1. A blind attempt to fix a hang on start on some Windows.
0.96.3 (2025-12-29)
-------------------
- New: VOBSUB subtitle extraction with OCR support for MP4 files
- New: VOBSUB subtitle extraction support for MKV/Matroska files
- New: Native SCC (Scenarist Closed Caption) input file support - CCExtractor can now read SCC files
- New: Configurable frame rate (--scc-framerate) and styled PAC codes for SCC output
- Fix: Apply --delay option to DVB/bitmap subtitles (previously only worked with text-based subtitles)
- Fix: 200ms timing offset in MOV/MP4 caption extraction
- Fix: utf8proc include path for system library builds
- Fix: Use fixed-width integer types in MP4 bswap functions for better portability
- Fix: Guard ocr_text access with ENABLE_OCR preprocessor check
- Fix: Preserve FFmpeg libs when building with -system-libs -hardsubx
- Build: Add vobsub_decoder to Windows and autoconf build systems
- Build: Add winget and Chocolatey packaging workflows for Windows distribution
- Docs: Add VOBSUB extraction documentation and subtile-ocr Dockerfile
0.96.2 (2025-12-26)
-------------------
- Fix: Resolve utf8proc header include path when building against system libraries on Linux.
- Rebundle Windows version to include required runtime files to process hardcoded subtitles
(hardcodex mode).
- New: Add optional -system-libs flag to Linux build script for package manager compatibility
0.96.1 (2025-12-25)
-------------------
- Rebundle Windows version to include an updated GUI. No changes in CCExtractor itself.
0.96 (2025-12-23)
1.0 (to be released)
-----------------
- New: Multi-page teletext extraction support (#665)
- Extract multiple teletext pages simultaneously with separate output files
- Use --tpage multiple times (e.g., --tpage 100 --tpage 200)
- Output files are named with page suffix (e.g., output_p100.srt, output_p200.srt)
- Fix: SPUPNG subtitle offset calculation to center based on actual image dimensions
- New: Added --list-tracks (-L) option to list all tracks in media files without processing
New: Chinese, Korean, Japanese support - proper encoding and OCR.
New: Correct McPoodle DVD raw format support
Fix: Timing is now frame perfect (using FFMpeg timing dump as reference) in all formats.
Fix: Solved garbling in all the pending issues we had on GitHub.
Fix: All causes of "premature end of file" messages due to bugs and not actual file cuts.
Fix: All memory leaks, double frees and usual C nastyness that valgrind could find.
- Fix Include ATSC VCT virtual channel numbers and call signs in XMLTV output
- Fix: Restore ATSC XMLTV generation with ETT parsing for extended descriptions, multi-segment handling, extended table ID's (EIT/VCT), corrected <programme> XMLTV formatting, buffer bounds fixes
- Fix: Add HEVC/H.265 stream type recognition to prevent crashes on ATSC 3.0 streams.
Fix: Tolerance to damaged streams - recover where possible instead of terminating.
Issues closed: Over 40! Too many to list here, but each of them was either a bug squashed or a feature implemented.
0.95 (2025-09-15 - never formally packaged)
-----------------
- New: Create a Docker image to simplify the CCExtractor usage without any environmental hustle (#1611)
- New: Add SCC support for CEA-708 decoder (#1595)
Refactor: Lots of code ported to Rust.
- Fix: ARM64/aarch64 build failure due to c_char type mismatch in nal.rs
- Fix: HardSubX OCR on Rust
- Removed the Share Module
- Fix: Regression failures on DVD files
- Fix: Segmentation faults on MP4 files with CEA-708 captions
- Refactor: Remove API structures from ccextractor
- New: Add Encoder Module to Rust
- Fix: Elementary stream regressions
- Fix: Segmentation faults on XDS files
- Fix: Clippy Errors Based on Rust 1.88
- IMPROVEMENT: Refactor and optimize Dockerfile
- Fix: Improved handling of IETF language tags in Matroska files (#1665)
- New: Create unit test for rust code (#1615)
- Breaking: Major argument flags revamp for CCExtractor (#1564 & #1619)
- New: Create a Docker image to simplify the CCExtractor usage without any environmental hustle (#1611)
- New: Add time units module in lib_ccxr (#1623)
- New: Add bits and levenshtein module in lib_ccxr (#1627)
- New: Add constants module in lib_ccxr (#1624)
- New: Add log module in lib_ccxr (#1622)
- New: Create `lib_ccxr` and `libccxr_exports` (#1621)
- Fix: Unexpected behavior of get_write_interval (#1609)
- Update: Bump rsmpeg to latest version for ffmpeg bindings (#1600)
- New: Add SCC support for CEA-708 decoder (#1595)
- Fix: respect `-stdout` even if multiple CC tracks are present in a Matroska input file (#1453)
- Fix: crash in Rust decoder on ATSC1.0 TS Files (#1407)
- Removed the --with-gui flag for linux/configure and mac/configure (use the Flutter GUI instead)
- Fix: segmentation fault in using hardsubx
- New: Add function (and command) that extracts closed caption subtitles as well as burnt-in subtitles from a file in a single pass. (As proposed in issue 726)
- Refactored: the `general_loop` function has some code moved to a new function
- Fix: WebVTT X-TIMESTAMP-MAP placement (#1463)
- Disable X-TIMESTAMP-MAP by default (changed option --no-timestamp-map to --timestamp-map)
- Fix: missing `#` in color attribute of font tag
- Fix: ffmpeg 5.0, tesseract 5.0 compatibility and remove deprecated methods
- Fix: tesseract 5.x traineddata location in ocr
- Fix: fix autoconf tesseract detection problem (#1503)
- Fix: add missing compile_info_real.h source to Autotools build
- Fix: add missing `-lavfilter` for hardsubx linking
- Fix: make webvtt-full work correctly with multi-byte utf-8 characters
- Fix: encoding of solid block in latin-1 and unicode
- Fix: McPoodle Broadcast Raw format for field 1
- Fix: Incorrect skipping of packets
- Fix: Repeated values for enums
- Cleanup: Remove the (unmaintained) Nuklear GUI code
- Cleanup: Reduce the amount of Windows build options in the project file
- Fix: infinite loop in MP4 file type detector.
- Improvement: Use Corrosion to build Rust code
- Improvement: Ignore MXF Caption Essence Container version byte to enhance SRT subtitle extraction compatibility
- New: Add tesseract page segmentation modes control with `--psm` flag
- Fix: Resolve compile-time error about implicit declarations (#1646)
- Fix: fatal out of memory error extracting from a VOB PS
- Fix: Unit Test Rust failing due to changes in Rust Version 1.86.0
- Fix: Support for MINGW-w64 cross compiling
- Fix: Build with ENABLE_FFMPEG to support ffmpeg 5
0.94 (2021-12-14)
-----------------

View File

@@ -1,16 +1,3 @@
# Installation
## Homebrew
The easiest way to install CCExtractor for Mac and Linux is through Homebrew:
```bash
brew install ccextractor
```
Note: If you don't have Homebrew installed, see [brew.sh](https://brew.sh/)
for installation instructions.
---
# Compiling CCExtractor
You may compile CCExtractor across all major platforms using `CMakeLists.txt` stored under `ccextractor/src/` directory. Autoconf and custom build scripts are also available. See platform specific instructions in the below sections.
@@ -23,16 +10,6 @@ Clone the latest repository from Github
git clone https://github.com/CCExtractor/ccextractor.git
```
### Hardsubx (Burned-in Subtitles) and FFmpeg Versions
CCExtractor's hardsubx feature extracts burned-in subtitles from videos using OCR. It requires FFmpeg libraries. The build system automatically selects appropriate FFmpeg versions for each platform:
- **Linux**: FFmpeg 6.x (default)
- **Windows**: FFmpeg 6.x (default)
- **macOS**: FFmpeg 8.x (default)
You can override the default by setting the `FFMPEG_VERSION` environment variable to `ffmpeg6`, `ffmpeg7`, or `ffmpeg8` before building. This flexibility ensures compatibility with different FFmpeg installations across platforms.
## Docker
You can now use docker image to build latest source of CCExtractor without any environmental hustle. Follow these [instructions](https://github.com/CCExtractor/ccextractor/tree/master/docker/README.md) for building docker image & usage of it.
@@ -56,10 +33,6 @@ Arch:
```bash
sudo paru -S glew glfw curl tesseract leptonica cmake gcc clang gpac
```
or
```bash
sudo pacman -S glew glfw curl tesseract leptonica cmake gcc clang gpac
```
Rust 1.54 or above is also required. [Install Rust](https://www.rust-lang.org/tools/install). Check specific compilation methods below, on how to compile without rust.
@@ -83,26 +56,21 @@ cd ccextractor/linux
# compile without debug flags
./build
# compile without rust
./build -without-rust
# compile with debug info
./build -debug # same as ./builddebug
# compile with hardsubx (burned-in subtitle extraction)
# Hardsubx requires FFmpeg libraries. Different FFmpeg versions are used by default:
# - Linux: FFmpeg 6.x (automatic)
# - Windows: FFmpeg 6.x (automatic)
# - macOS: FFmpeg 8.x (automatic)
# compile with hardsubx
[Optional] You need to set these environment variables correctly according to your machine,
FFMPEG_INCLUDE_DIR=/usr/include
FFMPEG_PKG_CONFIG_PATH=/usr/lib/pkgconfig
./build -hardsubx # uses platform-specific FFmpeg version
# To override the default FFmpeg version, set FFMPEG_VERSION:
FFMPEG_VERSION=ffmpeg8 ./build -hardsubx # force FFmpeg 8 on any platform
FFMPEG_VERSION=ffmpeg6 ./build -hardsubx # force FFmpeg 6 on any platform
FFMPEG_VERSION=ffmpeg7 ./build -hardsubx # force FFmpeg 7 on any platform
# [Optional] For custom FFmpeg installations, set these environment variables:
FFMPEG_INCLUDE_DIR=/usr/include
FFMPEG_PKG_CONFIG_PATH=/usr/lib/pkgconfig
./build -hardsubx # same as ./build_hardsubx
# compile in debug mode without rust
./build -debug -without-rust
# test your build
./ccextractor
@@ -114,7 +82,7 @@ FFMPEG_PKG_CONFIG_PATH=/usr/lib/pkgconfig
sudo apt-get install autoconf # dependency to generate configuration script
cd ccextractor/linux
./autogen.sh
./configure
./configure # OR ./configure --without-rust
make
# test your build
@@ -145,15 +113,9 @@ sudo make install
`cmake` also accepts the options:
`-DWITH_OCR=ON` to enable OCR
`-DWITH_HARDSUBX=ON` to enable burned-in subtitles (requires FFmpeg)
`-DWITH_HARDSUBX=ON` to enable burned-in subtitles
For hardsubx with specific FFmpeg versions:
Set `FFMPEG_VERSION=ffmpeg6` for FFmpeg 6.x (default on Linux and Windows)
Set `FFMPEG_VERSION=ffmpeg7` for FFmpeg 7.x
Set `FFMPEG_VERSION=ffmpeg8` for FFmpeg 8.x
(Defaults: Linux=FFmpeg 6, Windows=FFmpeg 6, macOS=FFmpeg 8)
([OPTIONAL] For custom FFmpeg installations, set these environment variables)
([OPTIONAL] For hardsubx, you also need to set these environment variables correctly according to your machine)
FFMPEG_INCLUDE_DIR=/usr/include
FFMPEG_PKG_CONFIG_PATH=/usr/lib/pkgconfig
@@ -174,8 +136,6 @@ brew install cmake gpac
# optional if you want OCR:
brew install tesseract
brew install leptonica
# optional if you want hardsubx (burned-in subtitle extraction):
brew install ffmpeg
```
If configuring OCR, use pkg-config to verify tesseract and leptonica dependencies, e.g.
@@ -191,12 +151,7 @@ pkg-config --exists --print-errors lept
```bash
cd ccextractor/mac
./build.command # basic build
./build.command -ocr # build with OCR support
./build.command -hardsubx # build with hardsubx (uses FFmpeg 8 by default on macOS)
# Override FFmpeg version if needed:
FFMPEG_VERSION=ffmpeg7 ./build.command -hardsubx
./build.command # OR ./build.command OCR
# test your build
./ccextractor
@@ -227,7 +182,7 @@ make
```bash
cd ccextractor/mac
./autogen.sh
./configure
./configure # OR ./configure --without-rust
make
# test your build
@@ -265,12 +220,6 @@ Other dependencies are required through vcpkg, so you can follow below steps:
```
vcpkg install ffmpeg leptonica tesseract --triplet x64-windows-static
```
Note: Windows builds use FFmpeg 6 by default. To override:
```
set FFMPEG_VERSION=ffmpeg8
msbuild ccextractor.sln /p:Configuration=Debug-Full /p:Platform=x64
```
otherwise if you have Debug, Release
```
vcpkg install libpng --triplet x64-windows-static

View File

@@ -54,32 +54,6 @@ To build the program with hardsubx support,
NOTE: The build has been tested with FFMpeg version 3.1.0, and Tesseract 3.04.
macOS
-----
Install the required dependencies using Homebrew:
brew install tesseract leptonica ffmpeg
To build the program with hardsubx support, use one of these methods:
== Using build.command (Recommended):
cd ccextractor/mac
./build.command -hardsubx
== Using autoconf:
cd ccextractor/mac
./autogen.sh
./configure --enable-hardsubx --enable-ocr
make
== Using cmake:
cd ccextractor
mkdir build && cd build
cmake -DWITH_OCR=ON -DWITH_HARDSUBX=ON ../src/
make
NOTE: The -hardsubx parameter uses a single dash (not --hardsubx).
Windows
-------

View File

@@ -26,14 +26,6 @@ Running ccextractor without parameters shows the help screen. Usage is
trivial - you just need to pass the input file and (optionally) some
details about the input and output files.
Example:
ccextractor input_video.ts
This command extracts subtitles from the input video file and generates a subtitle output file
(such as .srt) in the same directory.
## Languages
Usually English captions are transmitted in line 21 field 1 data,

View File

@@ -1,129 +0,0 @@
# VOBSUB Subtitle Extraction from MKV Files
CCExtractor supports extracting VOBSUB (S_VOBSUB) subtitles from Matroska (MKV) containers. VOBSUB is an image-based subtitle format originally from DVD video.
## Overview
VOBSUB subtitles consist of two files:
- `.idx` - Index file containing metadata, palette, and timestamp/position entries
- `.sub` - Binary file containing the actual subtitle bitmap data in MPEG Program Stream format
## Basic Usage
```bash
ccextractor movie.mkv
```
This will extract all VOBSUB tracks and create paired `.idx` and `.sub` files:
- `movie_eng.idx` + `movie_eng.sub` (first English track)
- `movie_eng_1.idx` + `movie_eng_1.sub` (second English track, if present)
- etc.
## Converting VOBSUB to SRT (Text)
Since VOBSUB subtitles are images, you need OCR (Optical Character Recognition) to convert them to text-based formats like SRT.
### Using subtile-ocr (Recommended)
[subtile-ocr](https://github.com/gwen-lg/subtile-ocr) is an actively maintained Rust tool that provides accurate OCR conversion.
#### Option 1: Docker (Easiest)
We provide a Dockerfile that builds subtile-ocr with all dependencies:
```bash
# Build the Docker image (one-time)
cd tools/vobsubocr
docker build -t subtile-ocr .
# Extract VOBSUB from MKV
ccextractor movie.mkv
# Convert to SRT using OCR
docker run --rm -v $(pwd):/data subtile-ocr -l eng -o /data/movie_eng.srt /data/movie_eng.idx
```
#### Option 2: Install subtile-ocr Natively
If you have Rust and Tesseract development libraries installed:
```bash
# Install dependencies (Ubuntu/Debian)
sudo apt-get install libleptonica-dev libtesseract-dev tesseract-ocr tesseract-ocr-eng
# Install subtile-ocr
cargo install --git https://github.com/gwen-lg/subtile-ocr
# Convert
subtile-ocr -l eng -o movie_eng.srt movie_eng.idx
```
### subtile-ocr Options
| Option | Description |
|--------|-------------|
| `-l, --lang <LANG>` | Tesseract language code (required). Examples: `eng`, `fra`, `deu`, `chi_sim` |
| `-o, --output <FILE>` | Output SRT file (stdout if not specified) |
| `-t, --threshold <0.0-1.0>` | Binarization threshold (default: 0.6) |
| `-d, --dpi <DPI>` | Image DPI for OCR (default: 150) |
| `--dump` | Save processed subtitle images as PNG files |
### Language Codes
Install additional Tesseract language packs as needed:
```bash
# Examples
sudo apt-get install tesseract-ocr-fra # French
sudo apt-get install tesseract-ocr-deu # German
sudo apt-get install tesseract-ocr-spa # Spanish
sudo apt-get install tesseract-ocr-chi-sim # Simplified Chinese
```
## Technical Details
### .idx File Format
The index file contains:
1. Header with metadata (size, palette, alignment settings)
2. Language identifier line
3. Timestamp entries with file positions
Example:
```
# VobSub index file, v7 (do not modify this line!)
size: 720x576
palette: 000000, 828282, ...
id: eng, index: 0
timestamp: 00:01:12:920, filepos: 000000000
timestamp: 00:01:18:640, filepos: 000000800
...
```
### .sub File Format
The binary file contains MPEG Program Stream packets:
- Each subtitle is wrapped in a PS Pack header (14 bytes) + PES header (15 bytes)
- Subtitles are aligned to 2048-byte boundaries
- Contains raw SPU (SubPicture Unit) bitmap data
## Troubleshooting
### Empty output files
- Ensure the MKV file actually contains VOBSUB tracks (check with `mediainfo` or `ffprobe`)
- CCExtractor will report "No VOBSUB subtitles to write" if the track is empty
### OCR quality issues
- Try adjusting the `-t` threshold parameter
- Ensure the correct language pack is installed
- Use `--dump` to inspect the processed images
### Docker permission issues
- The output files may be owned by root; use `sudo chown` to fix ownership
- Or run Docker with `--user $(id -u):$(id -g)`
## See Also
- [OCR.md](OCR.md) - General OCR support in CCExtractor
- [subtile-ocr GitHub](https://github.com/gwen-lg/subtile-ocr) - OCR tool documentation

View File

@@ -1,137 +0,0 @@
# Building CCExtractor on Windows using WSL
This guide explains how to build CCExtractor on Windows using WSL (Ubuntu).
It is based on a fresh setup and includes all required dependencies and
common build issues encountered during compilation.
---
## Prerequisites
- Windows 10 or Windows 11
- WSL enabled
- Ubuntu installed via Microsoft Store
---
## Install WSL and Ubuntu
From PowerShell (run as Administrator):
```powershell
wsl --install -d Ubuntu
```
Restart the system if prompted, then launch Ubuntu from the Start menu.
---
## Update system packages
```bash
sudo apt update
```
---
## Install basic build tools
```bash
sudo apt install -y build-essential git pkg-config
```
---
## Install Rust (required)
CCExtractor includes Rust components, so Rust and Cargo are required.
```bash
curl https://sh.rustup.rs -sSf | sh
source ~/.cargo/env
```
Verify installation:
```bash
cargo --version
rustc --version
```
---
## Install required libraries
```bash
sudo apt install -y \
libclang-dev clang \
libtesseract-dev tesseract-ocr \
libgpac-dev
```
---
## Clone the repository
```bash
git clone https://github.com/CCExtractor/ccextractor.git
cd ccextractor
```
---
## Build CCExtractor
```bash
cd linux
./build
```
After a successful build, verify by running:
```bash
./ccextractor
```
You should see the help/usage output.
---
## Common build issues
### cargo: command not found
```bash
source ~/.cargo/env
```
---
### Unable to find libclang
```bash
sudo apt install libclang-dev clang
```
---
### gpac/isomedia.h: No such file or directory
```bash
sudo apt install libgpac-dev
```
---
### please install tesseract development library
```bash
sudo apt install libtesseract-dev tesseract-ocr
```
---
## Notes
- Compiler warnings during the build process are expected and do not indicate failure.
- This guide was tested on Ubuntu (WSL) running on Windows 11.

View File

@@ -151,8 +151,6 @@ ccextractor_SOURCES = \
../src/lib_ccx/list.h \
../src/lib_ccx/matroska.c \
../src/lib_ccx/matroska.h \
../src/lib_ccx/vobsub_decoder.c \
../src/lib_ccx/vobsub_decoder.h \
../src/lib_ccx/mp4.c \
../src/lib_ccx/myth.c \
../src/lib_ccx/networking.c \
@@ -296,18 +294,12 @@ ccextractor_CPPFLAGS+= ${libavformat_CFLAGS}
ccextractor_CPPFLAGS+= ${libavfilter_CFLAGS}
ccextractor_CPPFLAGS+= ${libavutil_CFALGS}
ccextractor_CPPFLAGS+= ${libswscale_CFLAGS}
# HARDSUBX requires tesseract/leptonica for OCR (same as OCR feature)
ccextractor_CPPFLAGS+= ${tesseract_CFLAGS}
ccextractor_CPPFLAGS+= ${lept_CFLAGS}
AV_LIB = ${libavcodec_LIBS}
AV_LIB += ${libavformat_LIBS}
AV_LIB += ${libavfilter_LIBS}
AV_LIB += ${libavutil_LIBS}
AV_LIB += ${libswscale_LIBS}
ccextractor_LDADD += $(AV_LIB)
# HARDSUBX requires tesseract/leptonica libs for OCR
ccextractor_LDADD += ${tesseract_LIBS}
ccextractor_LDADD += ${lept_LIBS}
HARDSUBX_FEATURE_RUST += --features "hardsubx_ocr"
endif

View File

@@ -2,7 +2,6 @@
RUST_LIB="rust/release/libccx_rust.a"
RUST_PROFILE="--release"
USE_SYSTEM_LIBS=false
while [[ $# -gt 0 ]]; do
case $1 in
-debug)
@@ -14,20 +13,11 @@ while [[ $# -gt 0 ]]; do
;;
-hardsubx)
HARDSUBX=true
# Allow overriding FFmpeg version via environment variable
if [ -n "$FFMPEG_VERSION" ]; then
RUST_FEATURES="--features hardsubx_ocr,$FFMPEG_VERSION"
else
RUST_FEATURES="--features hardsubx_ocr"
fi
RUST_FEATURES="--features hardsubx_ocr"
BLD_FLAGS="$BLD_FLAGS -DENABLE_HARDSUBX"
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter -lxcb-shm -lxcb -lX11 -llzma -lswresample"
shift
;;
-system-libs)
USE_SYSTEM_LIBS=true
shift
;;
-*)
echo "Unknown option $1"
exit 1
@@ -35,42 +25,7 @@ while [[ $# -gt 0 ]]; do
esac
done
if [ "$USE_SYSTEM_LIBS" = true ]; then
command -v pkg-config >/dev/null || {
echo "Error: pkg-config is required for -system-libs mode"
exit 1
}
MISSING=""
for lib in libpng zlib freetype2 libutf8proc; do
if ! pkg-config --exists "$lib" 2>/dev/null; then
MISSING="$MISSING $lib"
fi
done
if [ -n "$MISSING" ]; then
echo "Error: Missing required system libraries:$MISSING"
echo ""
echo "On Debian/Ubuntu: sudo apt install libpng-dev zlib1g-dev libfreetype-dev libutf8proc-dev"
exit 1
fi
for hdr in leptonica/allheaders.h tesseract/capi.h; do
if ! echo "#include <$hdr>" | gcc -E - >/dev/null 2>&1; then
echo "Error: Missing headers for <$hdr>"
echo "On Debian/Ubuntu: sudo apt install libleptonica-dev libtesseract-dev"
exit 1
fi
done
PKG_CFLAGS="$(pkg-config --cflags libpng zlib freetype2 libutf8proc)"
PKG_LIBS="$(pkg-config --libs libpng zlib freetype2 libutf8proc)"
fi
BLD_FLAGS="$BLD_FLAGS -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
if [ "$USE_SYSTEM_LIBS" != true ]; then
BLD_FLAGS="$BLD_FLAGS -DFT2_BUILD_LIBRARY"
fi
BLD_FLAGS="$BLD_FLAGS -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -DENABLE_OCR -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
bit_os=$(getconf LONG_BIT)
if [ "$bit_os" == "64" ]
then
@@ -80,7 +35,7 @@ BLD_INCLUDE="-I../src -I /usr/include/leptonica/ -I /usr/include/tesseract/ -I..
SRC_LIBPNG="$(find ../src/thirdparty/libpng/ -name '*.c')"
SRC_ZLIB="$(find ../src/thirdparty/zlib/ -name '*.c')"
SRC_CCX="$(find ../src/lib_ccx/ -name '*.c')"
SRC_GPAC="$(find /usr/include/gpac/ -name '*.c' 2>/dev/null)"
SRC_GPAC="$(find /usr/include/gpac/ -name '*.c')"
SRC_HASH="$(find ../src/thirdparty/lib_hash/ -name '*.c')"
SRC_UTF8PROC="../src/thirdparty/utf8proc/utf8proc.c"
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c
@@ -127,24 +82,6 @@ SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_GPAC $SRC_ZLIB $SRC_LIBPNG $SRC_HASH $SRC_UTF8PROC $SRC_FREETYPE"
BLD_LINKER="$BLD_LINKER -lm -zmuldefs -l tesseract -l leptonica -lpthread -ldl -lgpac"
if [ "$USE_SYSTEM_LIBS" = true ]; then
LEPTONICA_CFLAGS="$(pkg-config --cflags --silence-errors lept)"
TESSERACT_CFLAGS="$(pkg-config --cflags --silence-errors tesseract)"
GPAC_CFLAGS="$(pkg-config --cflags --silence-errors gpac)"
BLD_INCLUDE="-I../src -I../src/lib_ccx -I../src/lib_ccx/zvbi -I../src/thirdparty/lib_hash \
$PKG_CFLAGS $LEPTONICA_CFLAGS $TESSERACT_CFLAGS $GPAC_CFLAGS"
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_HASH"
# Preserve FFmpeg libraries if -hardsubx was specified
FFMPEG_LIBS=""
if [ "$HARDSUBX" = true ]; then
FFMPEG_LIBS="-lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter -lxcb-shm -lxcb -lX11 -llzma -lswresample"
fi
BLD_LINKER="$PKG_LIBS -ltesseract -lleptonica -lgpac -lpthread -ldl -lm $FFMPEG_LIBS"
fi
echo "Running pre-build script..."
./pre-build.sh
echo "Trying to compile..."
@@ -159,7 +96,7 @@ fi
rustc_version="$(rustc --version)"
semver=( ${rustc_version//./ } )
version="${semver[1]}.${semver[2]}.${semver[3]}"
MSRV="1.87.0"
MSRV="1.54.0"
if [ "$(printf '%s\n' "$MSRV" "$version" | sort -V | head -n1)" = "$MSRV" ]; then
echo "rustc >= MSRV(${MSRV})"
else
@@ -207,7 +144,3 @@ if [[ "$out" != "" ]] ; then
else
echo "Compilation successful, no compiler messages."
fi
if [ -d ./utf8proc_compat ]; then
rm -rf ./utf8proc_compat
fi

View File

@@ -1,230 +1,63 @@
#!/bin/bash
#
# CCExtractor AppImage Build Script
#
# Build variants via BUILD_TYPE environment variable:
# - minimal: Basic CCExtractor without OCR (smallest size)
# - ocr: CCExtractor with OCR support (default)
# - hardsubx: CCExtractor with burned-in subtitle extraction (requires FFmpeg)
#
# Usage:
# ./build_appimage.sh # Builds 'ocr' variant (default)
# BUILD_TYPE=minimal ./build_appimage.sh
# BUILD_TYPE=hardsubx ./build_appimage.sh
#
# Requirements:
# - CMake, GCC, pkg-config, Rust toolchain
# - For OCR: tesseract-ocr, libtesseract-dev, libleptonica-dev
# - For HardSubX: libavcodec-dev, libavformat-dev, libswscale-dev, etc.
# - wget for downloading linuxdeploy
#
set -x
set -e
# Build type: minimal, ocr, hardsubx (default: ocr)
BUILD_TYPE="${BUILD_TYPE:-ocr}"
# store the path of where the script is
OLD_CWD=$(readlink -f .)
echo "=========================================="
echo "CCExtractor AppImage Builder"
echo "Build type: $BUILD_TYPE"
echo "=========================================="
# store repo root as variable
REPO_ROOT=$(dirname $OLD_CWD)
# Validate build type
case "$BUILD_TYPE" in
minimal|ocr|hardsubx)
;;
*)
echo "Error: Invalid BUILD_TYPE '$BUILD_TYPE'"
echo "Valid options: minimal, ocr, hardsubx"
exit 1
;;
esac
# Make a temp directory for building stuff which will be cleaned automatically
BUILD_DIR="$OLD_CWD/temp"
# Store paths
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(dirname "$SCRIPT_DIR")"
BUILD_DIR="$SCRIPT_DIR/appimage_build"
# Check if temp directory exist, and if so then remove contents from it
# if not then create temp directory
if [ -d "$BUILD_DIR" ]; then
rm -r "$BUILD_DIR/*" | true
else
mkdir -p "$BUILD_DIR"
fi
# Clean up function
# make sure to clean up build dir, even if errors occur
cleanup() {
if [ -d "$BUILD_DIR" ]; then
echo "Cleaning up build directory..."
rm -rf "$BUILD_DIR"
fi
if [ -d "$BUILD_DIR" ]; then
rm -rf "$BUILD_DIR"
fi
}
# Cleanup on exit (comment out for debugging)
# Automatically trigger Cleanup function
trap cleanup EXIT
# Create fresh build directory
rm -rf "$BUILD_DIR" 2>/dev/null || true
mkdir -p "$BUILD_DIR"
# switch to build dir
pushd "$BUILD_DIR"
cd "$BUILD_DIR"
# configure build files with CMake
# we need to explicitly set the install prefix, as CMake's default is /usr/local for some reason...
cmake "$REPO_ROOT/src"
# Determine CMake options based on build type
CMAKE_OPTIONS=""
case "$BUILD_TYPE" in
minimal)
CMAKE_OPTIONS=""
;;
ocr)
CMAKE_OPTIONS="-DWITH_OCR=ON"
;;
hardsubx)
CMAKE_OPTIONS="-DWITH_OCR=ON -DWITH_HARDSUBX=ON -DWITH_FFMPEG=ON"
;;
esac
# build project and install files into AppDir
make -j$(nproc) ENABLE_OCR=yes
echo "CMake options: $CMAKE_OPTIONS"
# download linuxdeploy tool
wget https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage
# Configure with CMake
echo "Configuring with CMake..."
cmake $CMAKE_OPTIONS "$REPO_ROOT/src"
# make them executable
chmod +x linuxdeploy*.AppImage
# Build
echo "Building CCExtractor..."
make -j$(nproc)
# Create AppDir
mkdir -p "$BUILD_DIR/AppDir"
# Verify binary was built
if [ ! -f "$BUILD_DIR/ccextractor" ]; then
echo "Error: ccextractor binary not found after build"
exit 1
fi
# Link of CCExtractor image of any of these resolution(8x8, 16x16, 20x20, 22x22, 24x24, 28x28, 32x32, 36x36, 42x42,
# 48x48, 64x64, 72x72, 96x96, 128x128, 160x160, 192x192, 256x256, 384x384, 480x480, 512x512) in png extension
PNG_LINK="https://ccextractor.org/images/ccextractor.png"
echo "Build successful!"
"$BUILD_DIR/ccextractor" --version
# Download the image and put it in AppDir
wget "$PNG_LINK" -P AppDir
# Download linuxdeploy
echo "Downloading linuxdeploy..."
LINUXDEPLOY_URL="https://github.com/linuxdeploy/linuxdeploy/releases/download/continuous/linuxdeploy-x86_64.AppImage"
wget -q --show-progress "$LINUXDEPLOY_URL" -O linuxdeploy-x86_64.AppImage
chmod +x linuxdeploy-x86_64.AppImage
# now, build AppImage using linuxdeploy
./linuxdeploy-x86_64.AppImage --appdir=AppDir -e ccextractor --create-desktop-file --output appimage -i AppDir/ccextractor.png
# Create AppDir structure
echo "Creating AppDir structure..."
mkdir -p AppDir/usr/bin
mkdir -p AppDir/usr/share/icons/hicolor/256x256/apps
mkdir -p AppDir/usr/share/applications
mkdir -p AppDir/usr/share/tessdata
# Copy binary
cp "$BUILD_DIR/ccextractor" AppDir/usr/bin/
# Download icon
echo "Downloading icon..."
PNG_URL="https://ccextractor.org/images/ccextractor.png"
if wget -q "$PNG_URL" -O AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.png 2>/dev/null; then
echo "Icon downloaded successfully"
else
# Create a simple placeholder icon if download fails
echo "Warning: Could not download icon, creating placeholder"
convert -size 256x256 xc:navy -fill white -gravity center -pointsize 40 -annotate 0 "CCX" \
AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.png 2>/dev/null || \
echo "P3 256 256 255" > AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.ppm
fi
# Create desktop file
cat > AppDir/usr/share/applications/ccextractor.desktop << 'EOF'
[Desktop Entry]
Type=Application
Name=CCExtractor
Comment=Extract closed captions and subtitles from video files
Exec=ccextractor
Icon=ccextractor
Categories=AudioVideo;Video;
Terminal=true
NoDisplay=true
EOF
# Copy desktop file to AppDir root (required by linuxdeploy)
cp AppDir/usr/share/applications/ccextractor.desktop AppDir/
# Copy icon to AppDir root
cp AppDir/usr/share/icons/hicolor/256x256/apps/ccextractor.png AppDir/ 2>/dev/null || true
# For OCR builds, bundle tessdata
if [ "$BUILD_TYPE" = "ocr" ] || [ "$BUILD_TYPE" = "hardsubx" ]; then
echo "Bundling tessdata for OCR support..."
# Try to find system tessdata
TESSDATA_PATHS=(
"/usr/share/tesseract-ocr/5/tessdata"
"/usr/share/tesseract-ocr/4.00/tessdata"
"/usr/share/tessdata"
"/usr/local/share/tessdata"
)
TESSDATA_SRC=""
for path in "${TESSDATA_PATHS[@]}"; do
if [ -d "$path" ] && [ -f "$path/eng.traineddata" ]; then
TESSDATA_SRC="$path"
break
fi
done
if [ -n "$TESSDATA_SRC" ]; then
echo "Found tessdata at: $TESSDATA_SRC"
# Copy English language data (most common)
cp "$TESSDATA_SRC/eng.traineddata" AppDir/usr/share/tessdata/ 2>/dev/null || true
# Copy OSD (orientation and script detection) if available
cp "$TESSDATA_SRC/osd.traineddata" AppDir/usr/share/tessdata/ 2>/dev/null || true
else
echo "Warning: tessdata not found, downloading English language data..."
wget -q "https://github.com/tesseract-ocr/tessdata/raw/main/eng.traineddata" \
-O AppDir/usr/share/tessdata/eng.traineddata || true
fi
# Create wrapper script that sets TESSDATA_PREFIX
mv AppDir/usr/bin/ccextractor AppDir/usr/bin/ccextractor.bin
cat > AppDir/usr/bin/ccextractor << 'WRAPPER'
#!/bin/bash
SELF_DIR="$(dirname "$(readlink -f "$0")")"
export TESSDATA_PREFIX="${SELF_DIR}/../share/tessdata"
exec "${SELF_DIR}/ccextractor.bin" "$@"
WRAPPER
chmod +x AppDir/usr/bin/ccextractor
fi
# Determine output name based on build type
ARCH="x86_64"
case "$BUILD_TYPE" in
minimal)
OUTPUT_NAME="ccextractor-minimal-${ARCH}.AppImage"
;;
ocr)
OUTPUT_NAME="ccextractor-${ARCH}.AppImage"
;;
hardsubx)
OUTPUT_NAME="ccextractor-hardsubx-${ARCH}.AppImage"
;;
esac
# Build AppImage
echo "Building AppImage..."
export OUTPUT="$OUTPUT_NAME"
# Determine which executable to pass to linuxdeploy
# For OCR builds, we have a wrapper script, so pass the actual binary (.bin)
if [ -f "AppDir/usr/bin/ccextractor.bin" ]; then
LINUXDEPLOY_EXEC="AppDir/usr/bin/ccextractor.bin"
else
LINUXDEPLOY_EXEC="AppDir/usr/bin/ccextractor"
fi
./linuxdeploy-x86_64.AppImage \
--appdir=AppDir \
--executable="$LINUXDEPLOY_EXEC" \
--desktop-file=AppDir/ccextractor.desktop \
--icon-file=AppDir/ccextractor.png \
--output=appimage
# Move to output directory
mv "$OUTPUT_NAME" "$SCRIPT_DIR/"
echo "=========================================="
echo "AppImage built successfully!"
echo "Output: $SCRIPT_DIR/$OUTPUT_NAME"
echo ""
echo "Test with: $SCRIPT_DIR/$OUTPUT_NAME --version"
echo "=========================================="
# Move resulted AppImage binary to base directory
mv ccextractor*.AppImage "$OLD_CWD"

View File

@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.71])
AC_INIT([CCExtractor], [0.96.5], [carlos@ccextractor.org])
AC_INIT([CCExtractor], [0.94], [carlos@ccextractor.org])
AC_CONFIG_AUX_DIR([build-conf])
AC_CONFIG_SRCDIR([../src/ccextractor.c])
AM_INIT_AUTOMAKE([foreign subdir-objects])
@@ -32,11 +32,6 @@ AC_CHECK_LIB([avformat], [avformat_version], [HAS_AVFORMAT=1 && PKG_CHECK_MODULE
AC_CHECK_LIB([avutil], [avutil_version], [HAS_AVUTIL=1 && PKG_CHECK_MODULES([libavutil], [libavutil])], [HAS_AVUTIL=0])
AC_CHECK_LIB([swscale], [swscale_version], [HAS_SWSCALE=1 && PKG_CHECK_MODULES([libswscale], [libswscale])], [HAS_SWSCALE=0])
# Check for GPAC library (required for MP4 support)
PKG_CHECK_MODULES([gpac], [gpac], [HAS_GPAC=1], [HAS_GPAC=0])
AS_IF([test $HAS_GPAC -eq 0],
[AC_MSG_ERROR([GPAC library not found. Install gpac-devel (Fedora/RHEL), libgpac-dev (Debian/Ubuntu), or gpac (Arch) before proceeding.])])
# Checks for header files.
AC_CHECK_HEADERS([arpa/inet.h fcntl.h float.h inttypes.h limits.h locale.h malloc.h netdb.h netinet/in.h stddef.h stdint.h stdlib.h string.h sys/socket.h sys/time.h sys/timeb.h termios.h unistd.h wchar.h])
@@ -109,7 +104,7 @@ if test "x$with_rust" = "xyes" ; then
AS_IF([test "$RUSTC" = "notfound"], [AC_MSG_ERROR([rustc is required])])
rustc_version=$(rustc --version)
MSRV="1.87.0"
MSRV="1.54.0"
AX_COMPARE_VERSION($rustc_version, [ge], [$MSRV],
[AC_MSG_RESULT(rustc >= $MSRV)],
[AC_MSG_ERROR([Minimum supported rust version(MSRV) is $MSRV, please upgrade rust])])
@@ -154,7 +149,7 @@ AS_IF([ (test x$ocr = xtrue || test x$hardsubx = xtrue) && test ! $HAS_LEPT -gt
AM_CONDITIONAL(HARDSUBX_IS_ENABLED, [ test x$hardsubx = xtrue ])
AM_CONDITIONAL(OCR_IS_ENABLED, [ test x$ocr = xtrue || test x$hardsubx = xtrue ])
AM_CONDITIONAL(FFMPEG_IS_ENABLED, [ test x$ffmpeg = xtrue ])
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z "$(pkg-config --libs-only-l --silence-errors tesseract)" ])
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z $(pkg-config --libs-only-l --silence-errors tesseract) ])
AM_CONDITIONAL(TESSERACT_PRESENT_RPI, [ test -d "/usr/include/tesseract" && test $(ls -A /usr/include/tesseract | wc -l) -gt 0 ])
AM_CONDITIONAL(SYS_IS_LINUX, [ test $(uname -s) = "Linux"])
AM_CONDITIONAL(SYS_IS_MAC, [ test $(uname -s) = "Darwin"])

View File

@@ -0,0 +1,13 @@
#!/usr/bin/env bash
builddate=`date --utc --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y-%m-%d`
echo "Storing variables in file"
echo "Date: $builddate"
# Only append if it does not exist yet in the file
already_in_there=$(grep "CCX_CCEXTRACTOR_COMPILE_REAL_TIME_H" ../src/lib_ccx/compile_info_real.h 2>/dev/null)
if [ ! -z "$already_in_there" ]; then
echo "#ifndef CCX_CCEXTRACTOR_COMPILE_REAL_TIME_H" > ../src/lib_ccx/compile_info_real.h
echo "#define CCX_CCEXTRACTOR_COMPILE_REAL_TIME_H" >> ../src/lib_ccx/compile_info_real.h
echo "#define COMPILE_DATE \"$builddate\"" >> ../src/lib_ccx/compile_info_real.h
echo "#endif" >> ../src/lib_ccx/compile_info_real.h
endif
echo "Done."

View File

@@ -0,0 +1,34 @@
#!/usr/bin/env bash
echo "Obtaining Git commit"
commit=(`git rev-parse HEAD 2>/dev/null`)
if [ -z "$commit" ]; then
echo "Git command not present, trying folder approach"
if [ -d "../.git" ]; then
echo "Git folder found, using HEAD file"
head="`cat ../.git/HEAD`"
ref_pos=(`expr match "$head" 'ref: '`)
if [ $ref_pos -eq 5 ]; then
echo "HEAD file contains a ref, following"
commit=(`cat "../.git/${head:5}"`)
echo "Extracted commit: $commit"
else
echo "HEAD contains a commit, using it"
commit="$head"
echo "Extracted commit: $commit"
fi
fi
fi
if [ -z "$commit" ]; then
commit="Unknown"
fi
echo "Storing variables in file"
echo "Commit: $commit"
# Only append if it does not exist yet in the file
already_in_there=$(grep "CCX_CCEXTRACTOR_COMPILE_REAL_COMMIT_H" ../src/lib_ccx/compile_info_real.h 2>/dev/null)
if [ ! -z "$already_in_there" ]; then
echo "#ifndef CCX_CCEXTRACTOR_COMPILE_REAL_COMMIT_H" >> ../src/lib_ccx/compile_info_real.h
echo "#define CCX_CCEXTRACTOR_COMPILE_REAL_COMMIT_H" >> ../src/lib_ccx/compile_info_real.h
echo "#define GIT_COMMIT \"$commit\"" >> ../src/lib_ccx/compile_info_real.h
echo "#endif" >> ../src/lib_ccx/compile_info_real.h
endif
echo "Done."

View File

@@ -1,34 +1,3 @@
#!/usr/bin/env bash
echo "Obtaining Git commit"
commit=(`git rev-parse HEAD 2>/dev/null`)
if [ -z "$commit" ]; then
echo "Git command not present, trying folder approach"
if [ -d "../.git" ]; then
echo "Git folder found, using HEAD file"
head="`cat ../.git/HEAD`"
ref_pos=(`expr match "$head" 'ref: '`)
if [ $ref_pos -eq 5 ]; then
echo "HEAD file contains a ref, following"
commit=(`cat "../.git/${head:5}"`)
echo "Extracted commit: $commit"
else
echo "HEAD contains a commit, using it"
commit="$head"
echo "Extracted commit: $commit"
fi
fi
fi
if [ -z "$commit" ]; then
commit="Unknown"
fi
builddate=`date --utc --date="@${SOURCE_DATE_EPOCH:-$(date +%s)}" +%Y-%m-%d`
echo "Storing variables in file"
echo "Commit: $commit"
echo "Date: $builddate"
echo "#ifndef CCX_CCEXTRACTOR_COMPILE_REAL_H" > ../src/lib_ccx/compile_info_real.h
echo "#define CCX_CCEXTRACTOR_COMPILE_REAL_H" >> ../src/lib_ccx/compile_info_real.h
echo "#define GIT_COMMIT \"$commit\"" >> ../src/lib_ccx/compile_info_real.h
echo "#define COMPILE_DATE \"$builddate\"" >> ../src/lib_ccx/compile_info_real.h
echo "#endif" >> ../src/lib_ccx/compile_info_real.h
echo "Stored all in compile_info_real.h"
echo "Done."
./generate_version_info.sh
./generate_date_info.sh

View File

@@ -123,8 +123,6 @@ ccextractor_SOURCES = \
../src/lib_ccx/list.h \
../src/lib_ccx/matroska.c \
../src/lib_ccx/matroska.h \
../src/lib_ccx/vobsub_decoder.c \
../src/lib_ccx/vobsub_decoder.h \
../src/lib_ccx/mp4.c \
../src/lib_ccx/myth.c \
../src/lib_ccx/networking.c \

View File

@@ -20,19 +20,7 @@ while [[ $# -gt 0 ]]; do
;;
-hardsubx)
HARDSUBX=true
ENABLE_OCR=true
# Allow overriding FFmpeg version via environment variable
if [ -n "$FFMPEG_VERSION" ]; then
RUST_FEATURES="--features hardsubx_ocr,$FFMPEG_VERSION"
else
RUST_FEATURES="--features hardsubx_ocr"
fi
shift
;;
-system-libs)
# Use system-installed libraries via pkg-config instead of bundled ones
# This is required for Homebrew formula compatibility
USE_SYSTEM_LIBS=true
RUST_FEATURES="--features hardsubx_ocr"
shift
;;
-*)
@@ -42,21 +30,7 @@ while [[ $# -gt 0 ]]; do
esac
done
# Determine architecture based on cargo (to ensure consistency with Rust part)
CARGO_ARCH=$(file $(which cargo) | grep -o 'x86_64\|arm64')
if [[ "$CARGO_ARCH" == "x86_64" ]]; then
echo "Detected Intel (x86_64) Cargo. Forcing x86_64 build to match Rust and libraries..."
BLD_ARCH="-arch x86_64"
else
BLD_ARCH="-arch arm64"
fi
BLD_FLAGS="$BLD_ARCH -std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek"
# Add flags for bundled libraries (not needed when using system libs)
if [[ "$USE_SYSTEM_LIBS" != "true" ]]; then
BLD_FLAGS="$BLD_FLAGS -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
fi
BLD_FLAGS="-std=gnu99 -Wno-write-strings -Wno-pointer-sign -D_FILE_OFFSET_BITS=64 -DVERSION_FILE_PRESENT -Dfopen64=fopen -Dopen64=open -Dlseek64=lseek -DFT2_BUILD_LIBRARY -DGPAC_DISABLE_VTT -DGPAC_DISABLE_OD_DUMP -DGPAC_DISABLE_REMOTERY -DNO_GZIP"
# Add debug flags if needed
if [[ "$DEBUG" == "true" ]]; then
@@ -73,68 +47,7 @@ if [[ "$HARDSUBX" == "true" ]]; then
BLD_FLAGS="$BLD_FLAGS -DENABLE_HARDSUBX"
fi
# Set up include paths based on whether we're using system libs or bundled
if [[ "$USE_SYSTEM_LIBS" == "true" ]]; then
# Use system libraries via pkg-config (for Homebrew compatibility)
# Note: -I../src/thirdparty/lib_hash is needed so that "../lib_hash/sha2.h" resolves correctly
# (the .. goes up from lib_hash to thirdparty, then lib_hash/sha2.h finds the file)
BLD_INCLUDE="-I../src/ -I../src/lib_ccx -I../src/thirdparty/lib_hash -I../src/thirdparty"
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors freetype2)"
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors gpac)"
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors libpng)"
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors libprotobuf-c)"
BLD_INCLUDE="$BLD_INCLUDE $(pkg-config --cflags --silence-errors libutf8proc)"
else
# Use bundled libraries (default for standalone builds)
BLD_INCLUDE="-I../src/ -I../src/lib_ccx -I../src/thirdparty/lib_hash -I../src/thirdparty/libpng -I../src/thirdparty -I../src/thirdparty/zlib -I../src/thirdparty/freetype/include $(pkg-config --cflags --silence-errors gpac)"
fi
# Add FFmpeg include path for Mac
if [[ -d "/opt/homebrew/Cellar/ffmpeg" ]]; then
FFMPEG_VERSION=$(ls -1 /opt/homebrew/Cellar/ffmpeg | head -1)
if [[ -n "$FFMPEG_VERSION" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/Cellar/ffmpeg/$FFMPEG_VERSION/include"
fi
elif [[ -d "/usr/local/Cellar/ffmpeg" ]]; then
FFMPEG_VERSION=$(ls -1 /usr/local/Cellar/ffmpeg | head -1)
if [[ -n "$FFMPEG_VERSION" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/Cellar/ffmpeg/$FFMPEG_VERSION/include"
fi
fi
# Add Leptonica include path for Mac
if [[ -d "/opt/homebrew/Cellar/leptonica" ]]; then
LEPT_VERSION=$(ls -1 /opt/homebrew/Cellar/leptonica | head -1)
if [[ -n "$LEPT_VERSION" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/Cellar/leptonica/$LEPT_VERSION/include"
fi
elif [[ -d "/usr/local/Cellar/leptonica" ]]; then
LEPT_VERSION=$(ls -1 /usr/local/Cellar/leptonica | head -1)
if [[ -n "$LEPT_VERSION" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/Cellar/leptonica/$LEPT_VERSION/include"
fi
elif [[ -d "/opt/homebrew/include/leptonica" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/include"
elif [[ -d "/usr/local/include/leptonica" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/include"
fi
# Add Tesseract include path for Mac
if [[ -d "/opt/homebrew/Cellar/tesseract" ]]; then
TESS_VERSION=$(ls -1 /opt/homebrew/Cellar/tesseract | head -1)
if [[ -n "$TESS_VERSION" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/Cellar/tesseract/$TESS_VERSION/include"
fi
elif [[ -d "/usr/local/Cellar/tesseract" ]]; then
TESS_VERSION=$(ls -1 /usr/local/Cellar/tesseract | head -1)
if [[ -n "$TESS_VERSION" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/Cellar/tesseract/$TESS_VERSION/include"
fi
elif [[ -d "/opt/homebrew/include/tesseract" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/opt/homebrew/include"
elif [[ -d "/usr/local/include/tesseract" ]]; then
BLD_INCLUDE="$BLD_INCLUDE -I/usr/local/include"
fi
BLD_INCLUDE="-I../src/ -I../src/lib_ccx -I../src/lib_hash -I../src/thirdparty/libpng -I../src/thirdparty -I../src/thirdparty/zlib -I../src/thirdparty/freetype/include `pkg-config --cflags --silence-errors gpac`"
if [[ "$ENABLE_OCR" == "true" ]]; then
BLD_INCLUDE="$BLD_INCLUDE `pkg-config --cflags --silence-errors tesseract`"
@@ -142,111 +55,61 @@ fi
SRC_CCX="$(find ../src/lib_ccx -name '*.c')"
SRC_LIB_HASH="$(find ../src/thirdparty/lib_hash -name '*.c')"
SRC_LIBPNG="$(find ../src/thirdparty/libpng -name '*.c')"
SRC_UTF8="../src/thirdparty/utf8proc/utf8proc.c"
SRC_ZLIB="$(find ../src/thirdparty/zlib -name '*.c')"
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \
../src/thirdparty/freetype/base/ftbase.c \
../src/thirdparty/freetype/base/ftbbox.c \
../src/thirdparty/freetype/base/ftbdf.c \
../src/thirdparty/freetype/base/ftbitmap.c \
../src/thirdparty/freetype/base/ftcid.c \
../src/thirdparty/freetype/base/ftfntfmt.c \
../src/thirdparty/freetype/base/ftfstype.c \
../src/thirdparty/freetype/base/ftgasp.c \
../src/thirdparty/freetype/base/ftglyph.c \
../src/thirdparty/freetype/base/ftgxval.c \
../src/thirdparty/freetype/base/ftinit.c \
../src/thirdparty/freetype/base/ftlcdfil.c \
../src/thirdparty/freetype/base/ftmm.c \
../src/thirdparty/freetype/base/ftotval.c \
../src/thirdparty/freetype/base/ftpatent.c \
../src/thirdparty/freetype/base/ftpfr.c \
../src/thirdparty/freetype/base/ftstroke.c \
../src/thirdparty/freetype/base/ftsynth.c \
../src/thirdparty/freetype/base/ftsystem.c \
../src/thirdparty/freetype/base/fttype1.c \
../src/thirdparty/freetype/base/ftwinfnt.c \
../src/thirdparty/freetype/bdf/bdf.c \
../src/thirdparty/freetype/bzip2/ftbzip2.c \
../src/thirdparty/freetype/cache/ftcache.c \
../src/thirdparty/freetype/cff/cff.c \
../src/thirdparty/freetype/cid/type1cid.c \
../src/thirdparty/freetype/gzip/ftgzip.c \
../src/thirdparty/freetype/lzw/ftlzw.c \
../src/thirdparty/freetype/pcf/pcf.c \
../src/thirdparty/freetype/pfr/pfr.c \
../src/thirdparty/freetype/psaux/psaux.c \
../src/thirdparty/freetype/pshinter/pshinter.c \
../src/thirdparty/freetype/psnames/psnames.c \
../src/thirdparty/freetype/raster/raster.c \
../src/thirdparty/freetype/sfnt/sfnt.c \
../src/thirdparty/freetype/smooth/smooth.c \
../src/thirdparty/freetype/truetype/truetype.c \
../src/thirdparty/freetype/type1/type1.c \
../src/thirdparty/freetype/type42/type42.c \
../src/thirdparty/freetype/winfonts/winfnt.c"
# Set up sources and linker based on whether we're using system libs or bundled
if [[ "$USE_SYSTEM_LIBS" == "true" ]]; then
# Use system libraries - don't compile bundled sources
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_LIB_HASH"
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_LIB_HASH $SRC_LIBPNG $SRC_UTF8 $SRC_ZLIB $SRC_FREETYPE"
BLD_LINKER="-lm -liconv -lpthread -ldl"
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors freetype2)"
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors gpac)"
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors libpng)"
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors libprotobuf-c)"
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors libutf8proc)"
BLD_LINKER="$BLD_LINKER $(pkg-config --libs --silence-errors zlib)"
else
# Use bundled libraries (default)
SRC_LIBPNG="$(find ../src/thirdparty/libpng -name '*.c')"
SRC_UTF8="../src/thirdparty/utf8proc/utf8proc.c"
SRC_ZLIB="$(find ../src/thirdparty/zlib -name '*.c')"
SRC_FREETYPE="../src/thirdparty/freetype/autofit/autofit.c \
../src/thirdparty/freetype/base/ftbase.c \
../src/thirdparty/freetype/base/ftbbox.c \
../src/thirdparty/freetype/base/ftbdf.c \
../src/thirdparty/freetype/base/ftbitmap.c \
../src/thirdparty/freetype/base/ftcid.c \
../src/thirdparty/freetype/base/ftfntfmt.c \
../src/thirdparty/freetype/base/ftfstype.c \
../src/thirdparty/freetype/base/ftgasp.c \
../src/thirdparty/freetype/base/ftglyph.c \
../src/thirdparty/freetype/base/ftgxval.c \
../src/thirdparty/freetype/base/ftinit.c \
../src/thirdparty/freetype/base/ftlcdfil.c \
../src/thirdparty/freetype/base/ftmm.c \
../src/thirdparty/freetype/base/ftotval.c \
../src/thirdparty/freetype/base/ftpatent.c \
../src/thirdparty/freetype/base/ftpfr.c \
../src/thirdparty/freetype/base/ftstroke.c \
../src/thirdparty/freetype/base/ftsynth.c \
../src/thirdparty/freetype/base/ftsystem.c \
../src/thirdparty/freetype/base/fttype1.c \
../src/thirdparty/freetype/base/ftwinfnt.c \
../src/thirdparty/freetype/bdf/bdf.c \
../src/thirdparty/freetype/bzip2/ftbzip2.c \
../src/thirdparty/freetype/cache/ftcache.c \
../src/thirdparty/freetype/cff/cff.c \
../src/thirdparty/freetype/cid/type1cid.c \
../src/thirdparty/freetype/gzip/ftgzip.c \
../src/thirdparty/freetype/lzw/ftlzw.c \
../src/thirdparty/freetype/pcf/pcf.c \
../src/thirdparty/freetype/pfr/pfr.c \
../src/thirdparty/freetype/psaux/psaux.c \
../src/thirdparty/freetype/pshinter/pshinter.c \
../src/thirdparty/freetype/psnames/psnames.c \
../src/thirdparty/freetype/raster/raster.c \
../src/thirdparty/freetype/sfnt/sfnt.c \
../src/thirdparty/freetype/smooth/smooth.c \
../src/thirdparty/freetype/truetype/truetype.c \
../src/thirdparty/freetype/type1/type1.c \
../src/thirdparty/freetype/type42/type42.c \
../src/thirdparty/freetype/winfonts/winfnt.c"
BLD_SOURCES="../src/ccextractor.c $SRC_CCX $SRC_LIB_HASH $SRC_LIBPNG $SRC_UTF8 $SRC_ZLIB $SRC_FREETYPE"
BLD_LINKER="-lm -liconv -lpthread -ldl $(pkg-config --libs --silence-errors gpac)"
fi
BLD_LINKER="-lm -liconv -lpthread -ldl `pkg-config --libs --silence-errors gpac`"
if [[ "$ENABLE_OCR" == "true" ]]; then
BLD_LINKER="$BLD_LINKER `pkg-config --libs --silence-errors tesseract` `pkg-config --libs --silence-errors lept`"
fi
if [[ "$HARDSUBX" == "true" ]]; then
# Add FFmpeg library path for Mac
if [[ -d "/opt/homebrew/Cellar/ffmpeg" ]]; then
FFMPEG_VERSION=$(ls -1 /opt/homebrew/Cellar/ffmpeg | head -1)
if [[ -n "$FFMPEG_VERSION" ]]; then
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/Cellar/ffmpeg/$FFMPEG_VERSION/lib"
fi
elif [[ -d "/usr/local/Cellar/ffmpeg" ]]; then
FFMPEG_VERSION=$(ls -1 /usr/local/Cellar/ffmpeg | head -1)
if [[ -n "$FFMPEG_VERSION" ]]; then
BLD_LINKER="$BLD_LINKER -L/usr/local/Cellar/ffmpeg/$FFMPEG_VERSION/lib"
fi
fi
# Add library paths for Leptonica and Tesseract from Cellar
if [[ -d "/opt/homebrew/Cellar/leptonica" ]]; then
LEPT_VERSION=$(ls -1 /opt/homebrew/Cellar/leptonica | head -1)
if [[ -n "$LEPT_VERSION" ]]; then
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/Cellar/leptonica/$LEPT_VERSION/lib"
fi
fi
if [[ -d "/opt/homebrew/Cellar/tesseract" ]]; then
TESS_VERSION=$(ls -1 /opt/homebrew/Cellar/tesseract | head -1)
if [[ -n "$TESS_VERSION" ]]; then
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/Cellar/tesseract/$TESS_VERSION/lib"
fi
fi
# Also add homebrew lib path as fallback
if [[ -d "/opt/homebrew/lib" ]]; then
BLD_LINKER="$BLD_LINKER -L/opt/homebrew/lib"
elif [[ -d "/usr/local/lib" ]]; then
BLD_LINKER="$BLD_LINKER -L/usr/local/lib"
fi
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter -lleptonica -ltesseract"
BLD_LINKER="$BLD_LINKER -lswscale -lavutil -pthread -lavformat -lavcodec -lavfilter"
fi
echo "Running pre-build script..."
@@ -264,7 +127,7 @@ fi
rustc_version="$(rustc --version)"
semver=( ${rustc_version//./ } )
version="${semver[1]}.${semver[2]}.${semver[3]}"
MSRV="1.87.0"
MSRV="1.54.0"
if [ "$(printf '%s\n' "$MSRV" "$version" | sort -V | head -n1)" = "$MSRV" ]; then
echo "rustc >= MSRV(${MSRV})"
else
@@ -317,4 +180,4 @@ if [[ "$out" != "" ]]; then
echo "Compilation successful, compiler message shown in previous lines"
else
echo "Compilation successful, no compiler messages."
fi
fi

View File

@@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ([2.71])
AC_INIT([CCExtractor],[0.96.5],[carlos@ccextractor.org])
AC_INIT([CCExtractor],[0.94],[carlos@ccextractor.org])
AC_CONFIG_AUX_DIR([build-conf])
AC_CONFIG_SRCDIR([../src/ccextractor.c])
AM_INIT_AUTOMAKE([foreign subdir-objects])
@@ -25,7 +25,7 @@ fi
# Checks for libraries.
AC_CHECK_LIB([m], [sin], [], [AC_MSG_ERROR(Math library not installed. Install it before proceeding.)])
AC_CHECK_LIB([leptonica], [getLeptonicaVersion], [HAS_LEPT=1 && PKG_CHECK_MODULES([lept], [lept])], [HAS_LEPT=0])
AC_CHECK_LIB([lept], [getLeptonicaVersion], [HAS_LEPT=1 && PKG_CHECK_MODULES([lept], [lept])], [HAS_LEPT=0])
AC_CHECK_LIB([tesseract], [TessVersion], [HAS_TESSERACT=1 && PKG_CHECK_MODULES([tesseract], [tesseract])], [HAS_TESSERACT=0])
AC_CHECK_LIB([avcodec], [avcodec_version], [HAS_AVCODEC=1 && PKG_CHECK_MODULES([libavcodec], [libavcodec])], [HAS_AVCODEC=0])
AC_CHECK_LIB([avformat], [avformat_version], [HAS_AVFORMAT=1 && PKG_CHECK_MODULES([libavformat], [libavformat])], [HAS_AVFORMAT=0])
@@ -104,7 +104,7 @@ if test "x$with_rust" = "xyes" ; then
AS_IF([test "$RUSTC" = "notfound"], [AC_MSG_ERROR([rustc is required])])
rustc_version=$(rustc --version)
MSRV="1.87.0"
MSRV="1.54.0"
AX_COMPARE_VERSION($rustc_version, [ge], [$MSRV],
[AC_MSG_RESULT(rustc >= $MSRV)],
[AC_MSG_ERROR([Minimum supported rust version(MSRV) is $MSRV, please upgrade rust])])
@@ -148,7 +148,7 @@ AS_IF([ (test x$ocr = xtrue || test x$hardsubx = xtrue) && test ! $HAS_LEPT -gt
AM_CONDITIONAL(HARDSUBX_IS_ENABLED, [ test x$hardsubx = xtrue ])
AM_CONDITIONAL(OCR_IS_ENABLED, [ test x$ocr = xtrue || test x$hardsubx = xtrue ])
AM_CONDITIONAL(FFMPEG_IS_ENABLED, [ test x$ffmpeg = xtrue ])
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z "$(pkg-config --libs-only-l --silence-errors tesseract)" ])
AM_CONDITIONAL(TESSERACT_PRESENT, [ test ! -z $(pkg-config --libs-only-l --silence-errors tesseract) ])
AM_CONDITIONAL(TESSERACT_PRESENT_RPI, [ test -d "/usr/include/tesseract" && test $(ls -A /usr/include/tesseract | wc -l) -gt 0 ])
AM_CONDITIONAL(SYS_IS_LINUX, [ test $(uname -s) = "Linux"])
AM_CONDITIONAL(SYS_IS_MAC, [ test $(uname -s) = "Darwin"])

View File

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

View File

@@ -1,5 +1,5 @@
Name: ccextractor
Version: 0.96.5
Version: 0.94
Release: 1
Summary: A closed captions and teletext subtitles extractor for video streams.
Group: Applications/Internet

View File

@@ -1,7 +1,7 @@
#!/bin/bash
TYPE="debian" # can be one of 'slackware', 'debian', 'rpm'
PROGRAM_NAME="ccextractor"
VERSION="0.96.5"
VERSION="0.94"
RELEASE="1"
LICENSE="GPL-2.0"
MAINTAINER="carlos@ccextractor.org"

View File

@@ -1,96 +0,0 @@
# CCExtractor Packaging
This directory contains packaging configurations for Windows package managers.
## Windows Package Manager (winget)
### Initial Setup (One-time)
1. **Calculate MSI hash** for the current release:
```powershell
certutil -hashfile CCExtractor.0.96.1.msi SHA256
```
2. **Update the manifest files** in `winget/` with the SHA256 hash
3. **Fork microsoft/winget-pkgs** to the CCExtractor organization:
- Go to https://github.com/microsoft/winget-pkgs
- Fork to https://github.com/CCExtractor/winget-pkgs
4. **Submit initial manifest** via PR:
- Clone your fork
- Create directory: `manifests/c/CCExtractor/CCExtractor/0.96.1/`
- Copy the three YAML files from `winget/`
- Submit PR to microsoft/winget-pkgs
5. **Create GitHub token** for automation:
- Go to GitHub Settings > Developer settings > Personal access tokens > Tokens (classic)
- Create token with `public_repo` scope
- Add as secret `WINGET_TOKEN` in CCExtractor/ccextractor repository
### Automated Updates
After the initial submission is merged, the `publish_winget.yml` workflow will automatically submit PRs for new releases.
## Chocolatey
### Initial Setup (One-time)
1. **Create Chocolatey account**:
- Register at https://community.chocolatey.org/account/Register
2. **Get API key**:
- Go to https://community.chocolatey.org/account
- Copy your API key
3. **Add secret**:
- Add `CHOCOLATEY_API_KEY` secret to CCExtractor/ccextractor repository
### Package Structure
```
chocolatey/
├── ccextractor.nuspec # Package metadata
└── tools/
├── chocolateyInstall.ps1 # Installation script
└── chocolateyUninstall.ps1 # Uninstallation script
```
### Manual Testing
```powershell
cd packaging/chocolatey
# Update version and checksum in files first, then:
choco pack ccextractor.nuspec
# Test locally
choco install ccextractor --source="'.'" --yes --force
# Verify
ccextractor --version
```
### Automated Updates
The `publish_chocolatey.yml` workflow automatically:
1. Downloads the MSI from the release
2. Calculates the SHA256 checksum
3. Updates the nuspec and install script
4. Builds and tests the package
5. Pushes to Chocolatey
Note: Chocolatey packages go through moderation before being publicly available.
## Workflow Triggers
Both workflows trigger on:
- **Release published**: Automatic publishing when a new release is created
- **Manual dispatch**: Can be triggered manually with a specific tag
## Secrets Required
| Secret | Purpose |
|--------|---------|
| `WINGET_TOKEN` | GitHub PAT with `public_repo` scope for winget PRs |
| `CHOCOLATEY_API_KEY` | Chocolatey API key for package uploads |

View File

@@ -1,43 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<package xmlns="http://schemas.microsoft.com/packaging/2015/06/nuspec.xsd">
<metadata>
<id>ccextractor</id>
<version>0.96.5</version>
<title>CCExtractor</title>
<authors>CCExtractor Development Team</authors>
<owners>CCExtractor</owners>
<licenseUrl>https://github.com/CCExtractor/ccextractor/blob/master/LICENSE.txt</licenseUrl>
<projectUrl>https://ccextractor.org</projectUrl>
<iconUrl>https://raw.githubusercontent.com/CCExtractor/ccextractor/master/windows/CCX.ico</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>CCExtractor is a tool that analyzes video files and produces independent subtitle files from the closed captions data.
### Features
- Extracts closed captions from various video formats (MPEG, H.264, MKV, MP4, etc.)
- Supports multiple input sources including DVDs, DVRs, and live TV captures
- Outputs to multiple formats (SRT, WebVTT, SAMI, transcript, etc.)
- OCR support for bitmap-based subtitles (DVB, teletext)
- Includes a graphical user interface
### Usage
After installation, run `ccextractor` from the command line or use the GUI.
```
ccextractor video.ts -o output.srt
```
For more options: `ccextractor --help`
</description>
<summary>Extract closed captions and subtitles from video files</summary>
<releaseNotes>https://github.com/CCExtractor/ccextractor/releases</releaseNotes>
<copyright>Copyright (c) CCExtractor Development</copyright>
<tags>subtitles closed-captions video extraction accessibility srt dvb teletext ocr media cli</tags>
<projectSourceUrl>https://github.com/CCExtractor/ccextractor</projectSourceUrl>
<packageSourceUrl>https://github.com/CCExtractor/ccextractor/tree/master/packaging/chocolatey</packageSourceUrl>
<docsUrl>https://github.com/CCExtractor/ccextractor/wiki</docsUrl>
<bugTrackerUrl>https://github.com/CCExtractor/ccextractor/issues</bugTrackerUrl>
</metadata>
<files>
<file src="tools\**" target="tools" />
</files>
</package>

View File

@@ -1,24 +0,0 @@
$ErrorActionPreference = 'Stop'
$packageName = 'ccextractor'
$toolsDir = "$(Split-Path -parent $MyInvocation.MyCommand.Definition)"
# Package parameters
$packageArgs = @{
packageName = $packageName
fileType = 'MSI'
url64bit = 'https://github.com/CCExtractor/ccextractor/releases/download/v0.96.5/CCExtractor.0.96.5.msi'
checksum64 = 'FFCAB0D766180AFC2832277397CDEC885D15270DECE33A9A51947B790F1F095B'
checksumType64 = 'sha256'
silentArgs = '/quiet /norestart'
validExitCodes = @(0, 3010, 1641)
}
Install-ChocolateyPackage @packageArgs
# Add to PATH if not already there
$installPath = Join-Path $env:ProgramFiles 'CCExtractor'
if (Test-Path $installPath) {
Install-ChocolateyPath -PathToInstall $installPath -PathType 'Machine'
Write-Host "CCExtractor installed to: $installPath"
}

View File

@@ -1,23 +0,0 @@
$ErrorActionPreference = 'Stop'
$packageName = 'ccextractor'
# Get the uninstall registry key
$regKey = Get-UninstallRegistryKey -SoftwareName 'CCExtractor*'
if ($regKey) {
$silentArgs = '/quiet /norestart'
$file = $regKey.UninstallString -replace 'msiexec.exe','msiexec.exe ' -replace '/I','/X'
$packageArgs = @{
packageName = $packageName
fileType = 'MSI'
silentArgs = "$($regKey.PSChildName) $silentArgs"
file = ''
validExitCodes = @(0, 3010, 1605, 1614, 1641)
}
Uninstall-ChocolateyPackage @packageArgs
} else {
Write-Warning "CCExtractor was not found in the registry. It may have been uninstalled already."
}

View File

@@ -1,21 +0,0 @@
# yaml-language-server: $schema=https://aka.ms/winget-manifest.installer.1.9.0.schema.json
PackageIdentifier: CCExtractor.CCExtractor
PackageVersion: 0.96.5
Platform:
- Windows.Desktop
MinimumOSVersion: 10.0.0.0
InstallModes:
- interactive
- silent
- silentWithProgress
InstallerSwitches:
Silent: /quiet
SilentWithProgress: /passive
UpgradeBehavior: install
Installers:
- Architecture: x64
InstallerType: msi
InstallerUrl: https://github.com/CCExtractor/ccextractor/releases/download/v0.96.5/CCExtractor.0.96.5.msi
InstallerSha256: FFCAB0D766180AFC2832277397CDEC885D15270DECE33A9A51947B790F1F095B
ManifestType: installer
ManifestVersion: 1.9.0

View File

@@ -1,39 +0,0 @@
# yaml-language-server: $schema=https://aka.ms/winget-manifest.defaultLocale.1.9.0.schema.json
PackageIdentifier: CCExtractor.CCExtractor
PackageVersion: 0.96.5
PackageLocale: en-US
Publisher: CCExtractor Development
PublisherUrl: https://ccextractor.org
PublisherSupportUrl: https://github.com/CCExtractor/ccextractor/issues
Author: CCExtractor Development Team
PackageName: CCExtractor
PackageUrl: https://ccextractor.org
License: GPL-2.0
LicenseUrl: https://github.com/CCExtractor/ccextractor/blob/master/LICENSE.txt
Copyright: Copyright (c) CCExtractor Development
ShortDescription: A tool to extract subtitles from video files
Description: |-
CCExtractor is a tool that analyzes video files and produces independent subtitle files from the closed captions data.
Key features:
- Extracts closed captions from various video formats (MPEG, H.264, MKV, MP4, etc.)
- Supports multiple input sources including DVDs, DVRs, and live TV captures
- Outputs to multiple formats (SRT, WebVTT, SAMI, transcript, etc.)
- OCR support for bitmap-based subtitles (DVB, teletext)
- Cross-platform (Windows, Linux, macOS)
- Includes a GUI for easy operation
Moniker: ccextractor
Tags:
- subtitles
- closed-captions
- video
- extraction
- accessibility
- srt
- dvb
- teletext
- ocr
- media
ReleaseNotesUrl: https://github.com/CCExtractor/ccextractor/releases
ManifestType: defaultLocale
ManifestVersion: 1.9.0

View File

@@ -1,6 +0,0 @@
# yaml-language-server: $schema=https://aka.ms/winget-manifest.version.1.9.0.schema.json
PackageIdentifier: CCExtractor.CCExtractor
PackageVersion: 0.96.5
DefaultLocale: en-US
ManifestType: version
ManifestVersion: 1.9.0

View File

@@ -1,19 +0,0 @@
#!/bin/sh
set -e
# Default fallback
LIB_TRIPLET="x86_64-linux-gnu"
# Detect multiarch directory if present
for d in "$SNAP/usr/lib/"*-linux-gnu; do
if [ -d "$d" ]; then
LIB_TRIPLET=$(basename "$d")
break
fi
done
export LD_LIBRARY_PATH="$SNAP/usr/lib:\
$SNAP/usr/lib/$LIB_TRIPLET:\
$SNAP/usr/lib/$LIB_TRIPLET/blas:\
$SNAP/usr/lib/$LIB_TRIPLET/lapack:\
$SNAP/usr/lib/$LIB_TRIPLET/pulseaudio:\
${LD_LIBRARY_PATH:+:$LD_LIBRARY_PATH}"
shift
exec "$SNAP/usr/local/bin/ccextractor" "$@"

View File

@@ -1,104 +0,0 @@
name: ccextractor
base: core22
version: '0.96.5'
summary: Closed Caption Extractor
description: |
CCExtractor is a tool for extracting closed captions from video files.
website: https://www.ccextractor.org
source-code: https://github.com/CCExtractor/ccextractor
confinement: classic
apps:
ccextractor:
command: usr/local/bin/ccextractor
command-chain:
- local/run-ccextractor.sh
plugs:
- home
parts:
gpac:
plugin: make
source: https://github.com/gpac/gpac.git
source-tag: abi-16.4
build-packages:
- build-essential
- pkg-config
- zlib1g-dev
- libssl-dev
- libfreetype6-dev
- libjpeg-dev
- libpng-dev
override-build: |
set -eux
./configure --prefix=/usr
make -j$(nproc)
make DESTDIR=$SNAPCRAFT_PART_INSTALL install-lib
sed -i "s|^prefix=.*|prefix=$SNAPCRAFT_STAGE/usr|" $SNAPCRAFT_PART_INSTALL/usr/lib/pkgconfig/gpac.pc
stage:
- usr/lib/libgpac*
- usr/lib/pkgconfig/gpac.pc
- usr/include/gpac
ccextractor:
after: [gpac]
plugin: cmake
source: .
source-subdir: src
build-environment:
- PKG_CONFIG_PATH: "$SNAPCRAFT_STAGE/usr/lib/pkgconfig:$PKG_CONFIG_PATH"
build-snaps:
- cmake/latest/stable
- rustup/latest/stable
build-packages:
- build-essential
- pkg-config
- clang
- llvm-dev
- libclang-dev
- libzvbi-dev
- libtesseract-dev
- libavcodec-dev
- libavformat-dev
- libavdevice-dev
- libavfilter-dev
- libswscale-dev
- libx11-dev
- libxcb1-dev
- libxcb-shm0-dev
- libpng-dev
- zlib1g-dev
- libblas3
- liblapack3
stage-packages:
- libzvbi0
- libfreetype6
- libpng16-16
- libprotobuf-c1
- libutf8proc2
- libgl1
- libglu1-mesa
- libavcodec58
- libavformat58
- libavutil56
- libavdevice58
- libavfilter7
- libswscale5
- libjpeg-turbo8
- libvorbis0a
- libtheora0
- libxvidcore4
- libfaad2
- libmad0
- liba52-0.7.4
- libpulse0
- pulseaudio-utils
override-build: |
set -eux
rustup toolchain install stable
rustup default stable
export PATH="$HOME/.cargo/bin:$PATH"
snapcraftctl build
install -D -m 0755 \
$SNAPCRAFT_PROJECT_DIR/snap/local/run-ccextractor.sh \
$SNAPCRAFT_PART_INSTALL/local/run-ccextractor.sh

View File

@@ -9,7 +9,7 @@ option (WITH_HARDSUBX "Build with support for burned-in subtitles" OFF)
# Version number
set (CCEXTRACTOR_VERSION_MAJOR 0)
set (CCEXTRACTOR_VERSION_MINOR 96)
set (CCEXTRACTOR_VERSION_MINOR 89)
# Get project directory
get_filename_component(BASE_PROJ_DIR ../ ABSOLUTE)
@@ -230,14 +230,6 @@ if (PKG_CONFIG_FOUND AND WITH_HARDSUBX)
set (EXTRA_INCLUDES ${EXTRA_INCLUDES} ${SWSCALE_INCLUDE_DIRS})
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DENABLE_HARDSUBX")
pkg_check_modules (TESSERACT REQUIRED tesseract)
pkg_check_modules (LEPTONICA REQUIRED lept)
set (EXTRA_LIBS ${EXTRA_LIBS} ${TESSERACT_LIBRARIES})
set (EXTRA_LIBS ${EXTRA_LIBS} ${LEPTONICA_LIBRARIES})
set (EXTRA_INCLUDES ${EXTRA_INCLUDES} ${TESSERACT_INCLUDE_DIRS})
set (EXTRA_INCLUDES ${EXTRA_INCLUDES} ${LEPTONICA_INCLUDE_DIRS})
endif (PKG_CONFIG_FOUND AND WITH_HARDSUBX)
add_executable (ccextractor ${SOURCEFILE} ${FREETYPE_SOURCE} ${UTF8PROC_SOURCE})
@@ -255,13 +247,4 @@ endif (PKG_CONFIG_FOUND)
target_link_libraries (ccextractor ${EXTRA_LIBS})
target_include_directories (ccextractor PUBLIC ${EXTRA_INCLUDES})
# ccx_rust (Rust) calls C functions from ccx (like decode_vbi).
# Force the linker to pull these symbols from ccx before processing ccx_rust.
if (NOT WIN32 AND NOT APPLE)
target_link_options (ccextractor PRIVATE
-Wl,--undefined=decode_vbi
-Wl,--undefined=do_cb
-Wl,--undefined=store_hdcc)
endif()
install (TARGETS ccextractor DESTINATION bin)

View File

@@ -2,8 +2,6 @@
/* CCExtractor, originally by carlos at ccextractor.org, now a lot of people.
Credits: See AUTHORS.TXT
License: GPL 2.0
CI verification run: 2025-12-19T08:30 - Testing merged fixes from PRs #1847 and #1848
*/
#include "ccextractor.h"
#include <stdio.h>
@@ -186,11 +184,6 @@ int start_ccx()
ccx_options.use_gop_as_pts = 0;
if (ccx_options.ignore_pts_jumps)
ccx_common_timing_settings.disable_sync_check = 1;
// When using GOP timing (--goptime), disable sync check because
// GOP time (wall-clock) and PES PTS (stream-relative) are in
// different time bases and will always appear as huge jumps.
if (ccx_options.use_gop_as_pts == 1)
ccx_common_timing_settings.disable_sync_check = 1;
mprint("\rAnalyzing data in general mode\n");
tmp = general_loop(ctx);
if (!ret)
@@ -202,12 +195,6 @@ int start_ccx()
if (!ret)
ret = tmp;
break;
case CCX_SM_SCC:
mprint("\rAnalyzing data in SCC (Scenarist Closed Caption) mode\n");
tmp = raw_loop(ctx);
if (!ret)
ret = tmp;
break;
case CCX_SM_RCWT:
mprint("\rAnalyzing data in CCExtractor's binary format\n");
tmp = rcwt_loop(ctx);
@@ -435,9 +422,6 @@ int main(int argc, char *argv[])
int compile_ret = ccxr_parse_parameters(argc, argv);
// Update the Rust logger target after parsing so --quiet is respected
ccxr_update_logger_target();
if (compile_ret == EXIT_NO_INPUT_FILES)
{
print_usage();

View File

@@ -29,16 +29,17 @@ CURLcode res;
extern struct ccx_s_options ccx_options;
extern struct lib_ccx_ctx *signal_ctx;
// volatile int terminate_asap = 0;
//volatile int terminate_asap = 0;
struct ccx_s_options *api_init_options();
struct ccx_s_options* api_init_options();
int api_start(struct ccx_s_options api_options);
void sigterm_handler(int sig);
void sigint_handler(int sig);
void print_end_msg(void);
int main(int argc, char *argv[]);
#endif // CCEXTRACTOR_H
#endif //CCEXTRACTOR_H

View File

@@ -1,9 +1,9 @@
cmake_policy (SET CMP0037 NEW)
if(MSVC)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -W3 /wd4005 /wd4996")
set (CMAKE_C_FLAGS "-W3 /wd4005 /wd4996")
else (MSVC)
set (CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -Wno-pointer-sign -g -std=gnu99")
set (CMAKE_C_FLAGS "-Wall -Wno-pointer-sign -g -std=gnu99")
endif(MSVC)
if(WIN32)

View File

@@ -2,16 +2,16 @@
#define ACTIVITY_H
extern unsigned long net_activity_gui;
void activity_header(void);
void activity_progress(int percentaje, int cur_min, int cur_sec);
void activity_report_version(void);
void activity_input_file_closed(void);
void activity_input_file_open(const char *filename);
void activity_message(const char *fmt, ...);
void activity_video_info(int hor_size, int vert_size,
const char *aspect_ratio, const char *framerate);
void activity_program_number(unsigned program_number);
void activity_header (void);
void activity_progress (int percentaje, int cur_min, int cur_sec);
void activity_report_version (void);
void activity_input_file_closed (void);
void activity_input_file_open (const char *filename);
void activity_message (const char *fmt, ...);
void activity_video_info (int hor_size,int vert_size,
const char *aspect_ratio, const char *framerate);
void activity_program_number (unsigned program_number);
void activity_library_process(enum ccx_common_logging_gui message_type, ...);
void activity_report_data_read(void);
void activity_report_data_read (void);
#endif

View File

@@ -30,30 +30,28 @@
// 10.13 - Undocumented DVR-MS properties
#define DVRMS_PTS "\x2A\xC0\x3C\xFD\xDB\x06\xFA\x4C\x80\x1C\x72\x12\xD3\x87\x45\xE4"
typedef struct
{
typedef struct {
int VideoStreamNumber;
int AudioStreamNumber;
int CaptionStreamNumber;
int CaptionStreamStyle; // 1 = NTSC, 2 = ATSC
int DecodeStreamNumber; // The stream that is chosen to be decoded
int DecodeStreamPTS; // This will be used for the next returned block
int CaptionStreamStyle; // 1 = NTSC, 2 = ATSC
int DecodeStreamNumber; // The stream that is chosen to be decoded
int DecodeStreamPTS; // This will be used for the next returned block
int currDecodeStreamPTS; // Time of the data returned by the function
int prevDecodeStreamPTS; // Previous time
int VideoStreamMS; // See ableve, just for video
int VideoStreamMS; // See ableve, just for video
int currVideoStreamMS;
int prevVideoStreamMS;
int VideoJump; // Remember a jump in the video timeline
int VideoJump; // Remember a jump in the video timeline
} asf_data_stream_properties;
#define STREAMNUM 10
#define STREAMNUM 10
#define PAYEXTNUM 10
typedef struct
{
typedef struct {
// Generic buffer to hold data
unsigned char *parsebuf;
int64_t parsebufsize;
long parsebufsize;
// Header Object variables
int64_t HeaderObjectSize;
int64_t FileSize;
@@ -74,23 +72,23 @@ typedef struct
uint32_t TotalDataPackets;
int VideoClosedCaptioningFlag;
// Payload data
int PayloadLType; // ASF - Payload Length Type. <>0 for multiple payloads
uint32_t PayloadLength; // ASF - Payload Length
int NumberOfPayloads; // ASF - Number of payloads.
int payloadcur; // local
int PayloadLType; // ASF - Payload Length Type. <>0 for multiple payloads
uint32_t PayloadLength; // ASF - Payload Length
int NumberOfPayloads; // ASF - Number of payloads.
int payloadcur; // local
int PayloadStreamNumber; // ASF
int KeyFrame; // ASF
int KeyFrame; // ASF
uint32_t PayloadMediaNumber; // ASF
// Data Object Loop
uint32_t datapacketcur; // Current packet number
int64_t dobjectread; // Bytes read in Data Object
uint32_t datapacketcur; // Current packet number
int64_t dobjectread; // Bytes read in Data Object
// Payload parsing information
int MultiplePayloads; // ASF
int PacketLType; // ASF
int ReplicatedLType; // ASF
int OffsetMediaLType; // ASF
int MediaNumberLType; // ASF
int StreamNumberLType; // ASF
int MultiplePayloads; // ASF
int PacketLType; // ASF
int ReplicatedLType; // ASF
int OffsetMediaLType; // ASF
int MediaNumberLType; // ASF
int StreamNumberLType; // ASF
uint32_t PacketLength;
uint32_t PaddingLength;
} asf_data;

View File

@@ -42,14 +42,14 @@ char *gui_data_string(void *val)
{
static char sbuf[40];
snprintf(sbuf, sizeof(sbuf), "%08lX-%04X-%04X-",
(long)*((uint32_t *)((char *)val + 0)),
(int)*((uint16_t *)((char *)val + 4)),
(int)*((uint16_t *)((char *)val + 6)));
sprintf(sbuf, "%08lX-%04X-%04X-",
(long)*((uint32_t *)((char *)val + 0)),
(int)*((uint16_t *)((char *)val + 4)),
(int)*((uint16_t *)((char *)val + 6)));
for (int ii = 0; ii < 2; ii++)
snprintf(sbuf + 19 + ii * 2, sizeof(sbuf) - 19 - ii * 2, "%02X-", *((unsigned char *)val + 8 + ii));
sprintf(sbuf + 19 + ii * 2, "%02X-", *((unsigned char *)val + 8 + ii));
for (int ii = 0; ii < 6; ii++)
snprintf(sbuf + 24 + ii * 2, sizeof(sbuf) - 24 - ii * 2, "%02X", *((unsigned char *)val + 10 + ii));
sprintf(sbuf + 24 + ii * 2, "%02X", *((unsigned char *)val + 10 + ii));
return sbuf;
}
@@ -150,10 +150,6 @@ int asf_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
.StreamNumberLType = 0,
.PacketLength = 0,
.PaddingLength = 0};
// Check for allocation failure
if (!asf_data_container.parsebuf)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In asf_getmoredata: Out of memory allocating initial parse buffer.");
// Initialize the Payload Extension System
for (int stream = 0; stream < STREAMNUM; stream++)
{
@@ -189,13 +185,9 @@ int asf_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
if (asf_data_container.HeaderObjectSize > asf_data_container.parsebufsize)
{
unsigned char *tmp = (unsigned char *)realloc(asf_data_container.parsebuf, (size_t)asf_data_container.HeaderObjectSize);
if (!tmp)
{
free(asf_data_container.parsebuf);
asf_data_container.parsebuf = (unsigned char *)realloc(asf_data_container.parsebuf, (size_t)asf_data_container.HeaderObjectSize);
if (!asf_data_container.parsebuf)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In asf_getmoredata: Out of memory requesting buffer for data container.");
}
asf_data_container.parsebuf = tmp;
asf_data_container.parsebufsize = (long)asf_data_container.HeaderObjectSize;
}
@@ -759,13 +751,9 @@ int asf_get_more_data(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
if ((long)replicated_length > asf_data_container.parsebufsize)
{
unsigned char *tmp = (unsigned char *)realloc(asf_data_container.parsebuf, replicated_length);
if (!tmp)
{
free(asf_data_container.parsebuf);
asf_data_container.parsebuf = (unsigned char *)realloc(asf_data_container.parsebuf, replicated_length);
if (!asf_data_container.parsebuf)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In asf_getmoredata: Not enough memory for buffer, unable to continue.\n");
}
asf_data_container.parsebuf = tmp;
asf_data_container.parsebufsize = replicated_length;
}
result = buffered_read(ctx->demux_ctx, asf_data_container.parsebuf, (long)replicated_length);

View File

@@ -48,7 +48,6 @@ struct avc_ctx *init_avc(void)
ctx->cc_databufsize = 1024;
ctx->cc_buffer_saved = CCX_TRUE; // Was the CC buffer saved after it was last updated?
ctx->is_hevc = 0;
ctx->got_seq_para = 0;
ctx->nal_ref_idc = 0;
ctx->seq_parameter_set_id = 0;
@@ -88,43 +87,16 @@ struct avc_ctx *init_avc(void)
return ctx;
}
// HEVC NAL unit types for SEI messages
#define HEVC_NAL_PREFIX_SEI 39
#define HEVC_NAL_SUFFIX_SEI 40
#define HEVC_NAL_VPS 32
#define HEVC_NAL_SPS 33
#define HEVC_NAL_PPS 34
void do_NAL(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, unsigned char *NAL_start, LLONG NAL_length, struct cc_subtitle *sub)
{
unsigned char *NAL_stop;
int nal_unit_type;
int nal_header_size;
unsigned char *payload_start;
// Determine if this is HEVC or H.264 based on NAL header
// H.264 NAL header: 1 byte, type in bits [4:0]
// HEVC NAL header: 2 bytes, type in bits [6:1] of first byte
if (dec_ctx->avc_ctx->is_hevc)
{
// HEVC: NAL type is in bits [6:1] of byte 0
nal_unit_type = (NAL_start[0] >> 1) & 0x3F;
nal_header_size = 2;
}
else
{
// H.264: NAL type is in bits [4:0] of byte 0
nal_unit_type = NAL_start[0] & 0x1F;
nal_header_size = 1;
}
enum ccx_avc_nal_types nal_unit_type = *NAL_start & 0x1F;
NAL_stop = NAL_length + NAL_start;
NAL_stop = remove_03emu(NAL_start + nal_header_size, NAL_stop);
payload_start = NAL_start + nal_header_size;
NAL_stop = remove_03emu(NAL_start + 1, NAL_stop); // Add +1 to NAL_stop for TS, without it for MP4. Still don't know why
dvprint("BEGIN NAL unit type: %d length %d ref_idc: %d - Buffered captions before: %d (HEVC: %d)\n",
nal_unit_type, NAL_stop - NAL_start - nal_header_size, dec_ctx->avc_ctx->nal_ref_idc,
!dec_ctx->avc_ctx->cc_buffer_saved, dec_ctx->avc_ctx->is_hevc);
dvprint("BEGIN NAL unit type: %d length %d ref_idc: %d - Buffered captions before: %d\n",
nal_unit_type, NAL_stop - NAL_start - 1, dec_ctx->avc_ctx->nal_ref_idc, !dec_ctx->avc_ctx->cc_buffer_saved);
if (NAL_stop == NULL) // remove_03emu failed.
{
@@ -132,64 +104,45 @@ void do_NAL(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, unsigned
return;
}
if (dec_ctx->avc_ctx->is_hevc)
if (nal_unit_type == CCX_NAL_TYPE_ACCESS_UNIT_DELIMITER_9)
{
// HEVC NAL unit processing
if (nal_unit_type == HEVC_NAL_VPS || nal_unit_type == HEVC_NAL_SPS || nal_unit_type == HEVC_NAL_PPS)
{
// Found HEVC parameter set - mark as having sequence params
// We don't parse HEVC SPS fully, but we need to enable SEI processing
dec_ctx->avc_ctx->got_seq_para = 1;
}
else if (nal_unit_type == HEVC_NAL_PREFIX_SEI || nal_unit_type == HEVC_NAL_SUFFIX_SEI)
{
// Found HEVC SEI (used for subtitles)
// SEI payload format is similar to H.264
sei_rbsp(dec_ctx->avc_ctx, payload_start, NAL_stop);
}
// Found Access Unit Delimiter
}
else
else if (nal_unit_type == CCX_NAL_TYPE_SEQUENCE_PARAMETER_SET_7)
{
// H.264 NAL unit processing (original code)
if (nal_unit_type == CCX_NAL_TYPE_ACCESS_UNIT_DELIMITER_9)
{
// Found Access Unit Delimiter
}
else if (nal_unit_type == CCX_NAL_TYPE_SEQUENCE_PARAMETER_SET_7)
{
// Found sequence parameter set
// We need this to parse NAL type 1 (CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1)
dec_ctx->avc_ctx->num_nal_unit_type_7++;
seq_parameter_set_rbsp(dec_ctx->avc_ctx, payload_start, NAL_stop);
dec_ctx->avc_ctx->got_seq_para = 1;
}
else if (dec_ctx->avc_ctx->got_seq_para && (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1 ||
nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE))
{
// Found coded slice of a non-IDR picture
// We only need the slice header data
slice_header(enc_ctx, dec_ctx, payload_start, NAL_stop, nal_unit_type, sub);
}
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_SEI)
{
// Found SEI (used for subtitles)
sei_rbsp(dec_ctx->avc_ctx, payload_start, NAL_stop);
}
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_PICTURE_PARAMETER_SET)
{
// Found Picture parameter set
}
// Found sequence parameter set
// We need this to parse NAL type 1 (CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1)
dec_ctx->avc_ctx->num_nal_unit_type_7++;
seq_parameter_set_rbsp(dec_ctx->avc_ctx, NAL_start + 1, NAL_stop);
dec_ctx->avc_ctx->got_seq_para = 1;
}
else if (dec_ctx->avc_ctx->got_seq_para && (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_NON_IDR_PICTURE_1 ||
nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE)) // Only if nal_unit_type=1
{
// Found coded slice of a non-IDR picture
// We only need the slice header data, no need to implement
// slice_layer_without_partitioning_rbsp( );
slice_header(enc_ctx, dec_ctx, NAL_start + 1, NAL_stop, nal_unit_type, sub);
}
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_SEI)
{
// Found SEI (used for subtitles)
// set_fts(ctx->timing); // FIXME - check this!!!
sei_rbsp(dec_ctx->avc_ctx, NAL_start + 1, NAL_stop);
}
else if (dec_ctx->avc_ctx->got_seq_para && nal_unit_type == CCX_NAL_TYPE_PICTURE_PARAMETER_SET)
{
// Found Picture parameter set
}
if (temp_debug)
{
int len = NAL_stop - payload_start;
int len = NAL_stop - (NAL_start + 1);
dbg_print(CCX_DMT_VIDES, "\n After decoding, the actual thing was (length =%d)\n", len);
dump(CCX_DMT_VIDES, payload_start, len > 160 ? 160 : len, 0, 0);
dump(CCX_DMT_VIDES, NAL_start + 1, len > 160 ? 160 : len, 0, 0);
}
dvprint("END NAL unit type: %d length %d ref_idc: %d - Buffered captions after: %d\n",
nal_unit_type, NAL_stop - NAL_start - nal_header_size, dec_ctx->avc_ctx->nal_ref_idc, !dec_ctx->avc_ctx->cc_buffer_saved);
nal_unit_type, NAL_stop - NAL_start - 1, dec_ctx->avc_ctx->nal_ref_idc, !dec_ctx->avc_ctx->cc_buffer_saved);
}
// Process inbuf bytes in buffer holding and AVC (H.264) video stream.
@@ -379,10 +332,11 @@ void sei_rbsp(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend)
}
else
{
// Unexpected SEI length - common with malformed streams, don't spam output
dbg_print(CCX_DMT_VERBOSE, "WARNING: Unexpected SEI unit length (parsed to %p, expected %p)...trying to continue.\n",
(void *)tbuf, (void *)(seiend - 1));
dump(CCX_DMT_VERBOSE, (unsigned char *)seibuf, seiend - seibuf, 0, 0);
// TODO: This really really looks bad
mprint("WARNING: Unexpected SEI unit length...trying to continue.");
temp_debug = 1;
mprint("\n Failed block (at sei_rbsp) was:\n");
dump(CCX_DMT_GENERIC_NOTICES, (unsigned char *)seibuf, seiend - seibuf, 0, 0);
ctx->num_unexpected_sei_length++;
}
@@ -392,24 +346,20 @@ void sei_rbsp(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend)
unsigned char *sei_message(struct avc_ctx *ctx, unsigned char *seibuf, unsigned char *seiend)
{
int payload_type = 0;
while (seibuf < seiend && *seibuf == 0xff)
while (*seibuf == 0xff)
{
payload_type += 255;
seibuf++;
}
if (seibuf >= seiend)
return NULL;
payload_type += *seibuf;
seibuf++;
int payload_size = 0;
while (seibuf < seiend && *seibuf == 0xff)
while (*seibuf == 0xff)
{
payload_size += 255;
seibuf++;
}
if (seibuf >= seiend)
return NULL;
payload_size += *seibuf;
seibuf++;
@@ -560,13 +510,9 @@ void user_data_registered_itu_t_t35(struct avc_ctx *ctx, unsigned char *userbuf,
// Save the data and process once we know the sequence number
if (((ctx->cc_count + local_cc_count) * 3) + 1 > ctx->cc_databufsize)
{
unsigned char *tmp = (unsigned char *)realloc(ctx->cc_data, (size_t)((ctx->cc_count + local_cc_count) * 6) + 1);
if (!tmp)
{
free(ctx->cc_data);
ctx->cc_data = (unsigned char *)realloc(ctx->cc_data, (size_t)((ctx->cc_count + local_cc_count) * 6) + 1);
if (!ctx->cc_data)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In user_data_registered_itu_t_t35: Out of memory to allocate buffer for CC data.");
}
ctx->cc_data = tmp;
ctx->cc_databufsize = (long)((ctx->cc_count + local_cc_count) * 6) + 1;
}
// Copy new cc data into cc_data
@@ -635,13 +581,9 @@ void user_data_registered_itu_t_t35(struct avc_ctx *ctx, unsigned char *userbuf,
// Save the data and process once we know the sequence number
if ((((local_cc_count + ctx->cc_count) * 3) + 1) > ctx->cc_databufsize)
{
unsigned char *tmp = (unsigned char *)realloc(ctx->cc_data, (size_t)(((local_cc_count + ctx->cc_count) * 6) + 1));
if (!tmp)
{
free(ctx->cc_data);
ctx->cc_data = (unsigned char *)realloc(ctx->cc_data, (size_t)(((local_cc_count + ctx->cc_count) * 6) + 1));
if (!ctx->cc_data)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In user_data_registered_itu_t_t35: Not enough memory trying to allocate buffer for CC data.");
}
ctx->cc_data = tmp;
ctx->cc_databufsize = (long)(((local_cc_count + ctx->cc_count) * 6) + 1);
}
// Copy new cc data into cc_data - replace command below.
@@ -907,10 +849,10 @@ void seq_parameter_set_rbsp(struct avc_ctx *ctx, unsigned char *seqbuf, unsigned
dvprint("vcl_hrd_parameters_present_flag= %llX\n", tmp1);
if (tmp)
{
// VCL HRD parameters are for video buffering compliance, not needed for caption extraction.
// Just skip and continue - this doesn't affect our ability to extract captions.
mprint("Skipping VCL HRD parameters (not needed for caption extraction)\n");
// TODO.
mprint("vcl_hrd. Not implemented for now. Hopefully not needed. Skipping rest of NAL\n");
ctx->num_vcl_hrd++;
// exit(1);
}
if (tmp || tmp1)
{
@@ -957,15 +899,6 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
dvprint("first_mb_in_slice= % 4lld (%#llX)\n", tmp, tmp);
slice_type = read_exp_golomb_unsigned(&q1);
dvprint("slice_type= % 4llX\n", slice_type);
// Validate slice_type to prevent buffer overflow in slice_types[] array
// Valid H.264 slice_type values are 0-9 (H.264 spec Table 7-6)
if (slice_type >= 10)
{
mprint("Invalid slice_type %lld in slice header, skipping.\n", slice_type);
return;
}
tmp = read_exp_golomb_unsigned(&q1);
dvprint("pic_parameter_set_id= % 4lld (%#llX)\n", tmp, tmp);
@@ -996,9 +929,9 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
if (nal_unit_type == 5)
{
// idr_pic_id: Read to advance bitstream position; value not needed for caption extraction
tmp = read_exp_golomb_unsigned(&q1);
dvprint("idr_pic_id= % 4lld (%#llX)\n", tmp, tmp);
// TODO
}
if (dec_ctx->avc_ctx->pic_order_cnt_type == 0)
{
@@ -1105,12 +1038,7 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
}
// if slices are buffered - flush
// For I/P-only streams (like HDHomeRun recordings), flushing on every
// reference frame defeats reordering since all frames are reference frames.
// Only flush and reset on IDR frames (nal_unit_type==5), not P-frames.
// This allows P-frames to accumulate in the buffer and be sorted by PTS.
int is_idr = (nal_unit_type == CCX_NAL_TYPE_CODED_SLICE_IDR_PICTURE);
if (isref && is_idr)
if (isref)
{
dvprint("\nReference pic! [%s]\n", slice_types[slice_type]);
dbg_print(CCX_DMT_TIME, "\nReference pic! [%s] maxrefcnt: %3d\n",
@@ -1205,32 +1133,8 @@ void slice_header(struct encoder_ctx *enc_ctx, struct lib_cc_decode *dec_ctx, un
if (abs(current_index) >= MAXBFRAMES)
{
// Large PTS gap detected. This can happen with certain encoders
// (like HDHomeRun) that produce streams where PTS jumps are common.
// Instead of just resetting current_index to 0 (which causes captions
// to pile up at the same buffer slot and become garbled), we need to:
// 1. Flush any buffered captions
// 2. Reset the reference PTS to the current PTS
// 3. Set current_index to 0 for a fresh start
// This ensures subsequent frames use the new reference point.
dbg_print(CCX_DMT_VERBOSE, "\nLarge PTS gap(%d) detected, flushing buffer and resetting reference.\n", current_index);
// Flush any buffered captions before resetting
if (dec_ctx->has_ccdata_buffered)
{
process_hdcc(enc_ctx, dec_ctx, sub);
}
// Reset the reference point to current PTS
dec_ctx->avc_ctx->currefpts = dec_ctx->timing->current_pts;
// Reset tracking variables for the new reference
dec_ctx->avc_ctx->lastmaxidx = -1;
dec_ctx->avc_ctx->maxidx = 0;
dec_ctx->avc_ctx->lastminidx = 10000;
dec_ctx->avc_ctx->minidx = 10000;
// Start with index 0 relative to the new reference
// Probably a jump in the timeline. Warn and handle gracefully.
mprint("\nFound large gap(%d) in PTS! Trying to recover ...\n", current_index);
current_index = 0;
}

View File

@@ -1,15 +1,15 @@
#ifndef AVC_FUNCTION_H
#define AVC_FUNCTION_H
struct avc_ctx
{
unsigned char cc_count;
// buffer to hold cc data
unsigned char *cc_data;
int64_t cc_databufsize;
long cc_databufsize;
int cc_buffer_saved; // Was the CC buffer saved after it was last updated?
int is_hevc; // Flag to indicate HEVC (H.265) mode vs H.264
int got_seq_para;
unsigned nal_ref_idc;
LLONG seq_parameter_set_id;
@@ -19,11 +19,11 @@ struct avc_ctx
int frame_mbs_only_flag;
// Use and throw stats for debug, remove this ugliness soon
int64_t num_nal_unit_type_7;
int64_t num_vcl_hrd;
int64_t num_nal_hrd;
int64_t num_jump_in_frames;
int64_t num_unexpected_sei_length;
long num_nal_unit_type_7;
long num_vcl_hrd;
long num_nal_hrd;
long num_jump_in_frames;
long num_unexpected_sei_length;
int ccblocks_in_avc_total;
int ccblocks_in_avc_lost;
@@ -50,6 +50,6 @@ struct avc_ctx
struct avc_ctx *init_avc(void);
void dinit_avc(struct avc_ctx **ctx);
void do_NAL(struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *NAL_start, LLONG NAL_length, struct cc_subtitle *sub);
void do_NAL (struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *NAL_start, LLONG NAL_length, struct cc_subtitle *sub);
size_t process_avc(struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *avcbuf, size_t avcbuflen, struct cc_subtitle *sub);
#endif

View File

@@ -26,25 +26,26 @@ struct bitstream
int _i_bpos;
};
#define read_u8(bstream) (uint8_t)bitstream_get_num(bstream, 1, 1)
#define read_u16(bstream) (uint16_t)bitstream_get_num(bstream, 2, 1)
#define read_u32(bstream) (uint32_t)bitstream_get_num(bstream, 4, 1)
#define read_u64(bstream) (uint64_t)bitstream_get_num(bstream, 8, 1)
#define read_i8(bstream) (int8_t)bitstream_get_num(bstream, 1, 1)
#define read_i16(bstream) (int16_t)bitstream_get_num(bstream, 2, 1)
#define read_i32(bstream) (int32_t)bitstream_get_num(bstream, 4, 1)
#define read_i64(bstream) (int64_t)bitstream_get_num(bstream, 8, 1)
#define read_u8(bstream) (uint8_t)bitstream_get_num(bstream,1,1)
#define read_u16(bstream) (uint16_t)bitstream_get_num(bstream,2,1)
#define read_u32(bstream) (uint32_t)bitstream_get_num(bstream,4,1)
#define read_u64(bstream) (uint64_t)bitstream_get_num(bstream,8,1)
#define read_i8(bstream) (int8_t)bitstream_get_num(bstream,1,1)
#define read_i16(bstream) (int16_t)bitstream_get_num(bstream,2,1)
#define read_i32(bstream) (int32_t)bitstream_get_num(bstream,4,1)
#define read_i64(bstream) (int64_t)bitstream_get_num(bstream,8,1)
#define skip_u32(bstream) (void)bitstream_get_num(bstream, 4, 1)
#define skip_u32(bstream) (void)bitstream_get_num(bstream,4,1)
#define next_u8(bstream) (uint8_t)bitstream_get_num(bstream,1,0)
#define next_u16(bstream) (uint16_t)bitstream_get_num(bstream,2,0)
#define next_u32(bstream) (uint32_t)bitstream_get_num(bstream,4,0)
#define next_u64(bstream) (uint64_t)bitstream_get_num(bstream,8,0)
#define next_i8(bstream) (int8_t)bitstream_get_num(bstream,1,0)
#define next_i16(bstream) (int16_t)bitstream_get_num(bstream,2,0)
#define next_i32(bstream) (int32_t)bitstream_get_num(bstream,4,0)
#define next_i64(bstream) (int64_t)bitstream_get_num(bstream,8,0)
#define next_u8(bstream) (uint8_t)bitstream_get_num(bstream, 1, 0)
#define next_u16(bstream) (uint16_t)bitstream_get_num(bstream, 2, 0)
#define next_u32(bstream) (uint32_t)bitstream_get_num(bstream, 4, 0)
#define next_u64(bstream) (uint64_t)bitstream_get_num(bstream, 8, 0)
#define next_i8(bstream) (int8_t)bitstream_get_num(bstream, 1, 0)
#define next_i16(bstream) (int16_t)bitstream_get_num(bstream, 2, 0)
#define next_i32(bstream) (int32_t)bitstream_get_num(bstream, 4, 0)
#define next_i64(bstream) (int64_t)bitstream_get_num(bstream, 8, 0)
int init_bitstream(struct bitstream *bstr, unsigned char *start, unsigned char *end);
uint64_t next_bits(struct bitstream *bstr, unsigned bnum);

View File

@@ -10,47 +10,47 @@
<100 means display whatever was output to stderr as a warning
>=100 means display whatever was output to stdout as an error
*/
#define EXIT_OK 0
#define EXIT_NO_INPUT_FILES 2
#define EXIT_TOO_MANY_INPUT_FILES 3
#define EXIT_INCOMPATIBLE_PARAMETERS 4
#define EXIT_UNABLE_TO_DETERMINE_FILE_SIZE 6
#define EXIT_MALFORMED_PARAMETER 7
#define EXIT_READ_ERROR 8
#define EXIT_NO_CAPTIONS 10
#define EXIT_WITH_HELP 11
#define EXIT_NOT_CLASSIFIED 300
#define EXIT_ERROR_IN_CAPITALIZATION_FILE 501
#define EXIT_BUFFER_FULL 502
#define EXIT_MISSING_ASF_HEADER 1001
#define EXIT_MISSING_RCWT_HEADER 1002
#define EXIT_OK 0
#define EXIT_NO_INPUT_FILES 2
#define EXIT_TOO_MANY_INPUT_FILES 3
#define EXIT_INCOMPATIBLE_PARAMETERS 4
#define EXIT_UNABLE_TO_DETERMINE_FILE_SIZE 6
#define EXIT_MALFORMED_PARAMETER 7
#define EXIT_READ_ERROR 8
#define EXIT_NO_CAPTIONS 10
#define EXIT_WITH_HELP 11
#define EXIT_NOT_CLASSIFIED 300
#define EXIT_ERROR_IN_CAPITALIZATION_FILE 501
#define EXIT_BUFFER_FULL 502
#define EXIT_MISSING_ASF_HEADER 1001
#define EXIT_MISSING_RCWT_HEADER 1002
#define CCX_COMMON_EXIT_FILE_CREATION_FAILED 5
#define CCX_COMMON_EXIT_UNSUPPORTED 9
#define EXIT_NOT_ENOUGH_MEMORY 500
#define CCX_COMMON_EXIT_BUG_BUG 1000
#define CCX_COMMON_EXIT_FILE_CREATION_FAILED 5
#define CCX_COMMON_EXIT_UNSUPPORTED 9
#define EXIT_NOT_ENOUGH_MEMORY 500
#define CCX_COMMON_EXIT_BUG_BUG 1000
#define CCX_OK 0
#define CCX_FALSE 0
#define CCX_TRUE 1
#define CCX_EAGAIN -100
#define CCX_EOF -101
#define CCX_EINVAL -102
#define CCX_OK 0
#define CCX_FALSE 0
#define CCX_TRUE 1
#define CCX_EAGAIN -100
#define CCX_EOF -101
#define CCX_EINVAL -102
#define CCX_ENOSUPP -103
#define CCX_ENOMEM -104
#define CCX_ENOMEM -104
// Declarations
int cc608_parity(unsigned int byte);
int fdprintf(int fd, const char *fmt, ...);
void millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes, unsigned *seconds, unsigned *ms);
void millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds, unsigned *ms);
extern void ccxr_millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes, unsigned *seconds, unsigned *ms);
extern void ccxr_millis_to_time(LLONG milli, unsigned *hours, unsigned *minutes,unsigned *seconds, unsigned *ms);
void freep(void *arg);
void dbg_print(LLONG mask, const char *fmt, ...);
unsigned char *debug_608_to_ASC(unsigned char *ccdata, int channel);
int add_cc_sub_text(struct cc_subtitle *sub, char *str, LLONG start_time,
LLONG end_time, char *info, char *mode, enum ccx_encoding_type);
LLONG end_time, char *info, char *mode, enum ccx_encoding_type);
extern int cc608_parity_table[256]; // From myth
#endif

View File

@@ -17,8 +17,8 @@ const unsigned char UTF8_BOM[] = {0xef, 0xbb, 0xbf};
const unsigned char DVD_HEADER[8] = {0x00, 0x00, 0x01, 0xb2, 0x43, 0x43, 0x01, 0xf8};
const unsigned char lc1[1] = {0x8a};
const unsigned char lc2[1] = {0x8f};
const unsigned char lc3[1] = {0x16}; // McPoodle uses single-byte loop markers
const unsigned char lc4[1] = {0x1e};
const unsigned char lc3[2] = {0x16, 0xfe};
const unsigned char lc4[2] = {0x1e, 0xfe};
const unsigned char lc5[1] = {0xff};
const unsigned char lc6[1] = {0xfe};

View File

@@ -22,42 +22,42 @@ extern const unsigned char UTF8_BOM[3];
extern const unsigned char DVD_HEADER[8];
extern const unsigned char lc1[1];
extern const unsigned char lc2[1];
extern const unsigned char lc3[1];
extern const unsigned char lc4[1];
extern const unsigned char lc3[2];
extern const unsigned char lc4[2];
extern const unsigned char lc5[1];
extern const unsigned char lc6[1];
extern unsigned char rcwt_header[11];
#define ONEPASS 120 /* Bytes we can always look ahead without going out of limits */
#define BUFSIZE (2048 * 1024 + ONEPASS) /* 2 Mb plus the safety pass */
#define ONEPASS 120 /* Bytes we can always look ahead without going out of limits */
#define BUFSIZE (2048*1024+ONEPASS) /* 2 Mb plus the safety pass */
#define MAX_CLOSED_CAPTION_DATA_PER_PICTURE 32
#define EIA_708_BUFFER_LENGTH 2048 // TODO: Find out what the real limit is
#define TS_PACKET_PAYLOAD_LENGTH 184 // From specs
#define SUBLINESIZE 2048 // Max. length of a .srt line - TODO: Get rid of this
#define STARTBYTESLENGTH (1024 * 1024)
#define EIA_708_BUFFER_LENGTH 2048 // TODO: Find out what the real limit is
#define TS_PACKET_PAYLOAD_LENGTH 184 // From specs
#define SUBLINESIZE 2048 // Max. length of a .srt line - TODO: Get rid of this
#define STARTBYTESLENGTH (1024*1024)
#define UTF8_MAX_BYTES 6
#define XMLRPC_CHUNK_SIZE (64 * 1024) // 64 Kb per chunk, to avoid too many realloc()
#define XMLRPC_CHUNK_SIZE (64*1024) // 64 Kb per chunk, to avoid too many realloc()
enum ccx_debug_message_types
{
/* Each debug message now belongs to one of these types. Use bitmaps in case
we want one message to belong to more than one type. */
CCX_DMT_PARSE = 1, // Show information related to parsing the container
CCX_DMT_VIDES = 2, // Show video stream related information
CCX_DMT_TIME = 4, // Show GOP and PTS timing information
CCX_DMT_VERBOSE = 8, // Show lots of debugging output
CCX_DMT_DECODER_608 = 0x10, // Show CC-608 decoder debug?
CCX_DMT_708 = 0x20, // Show CC-708 decoder debug?
CCX_DMT_DECODER_XDS = 0x40, // Show XDS decoder debug?
CCX_DMT_CBRAW = 0x80, // Caption blocks with FTS timing
CCX_DMT_PARSE = 1, // Show information related to parsing the container
CCX_DMT_VIDES = 2, // Show video stream related information
CCX_DMT_TIME = 4, // Show GOP and PTS timing information
CCX_DMT_VERBOSE = 8, // Show lots of debugging output
CCX_DMT_DECODER_608 = 0x10, // Show CC-608 decoder debug?
CCX_DMT_708 = 0x20, // Show CC-708 decoder debug?
CCX_DMT_DECODER_XDS = 0x40, // Show XDS decoder debug?
CCX_DMT_CBRAW = 0x80, // Caption blocks with FTS timing
CCX_DMT_GENERIC_NOTICES = 0x100, // Generic, always displayed even if no debug is selected
CCX_DMT_TELETEXT = 0x200, // Show teletext debug?
CCX_DMT_PAT = 0x400, // Program Allocation Table dump
CCX_DMT_PMT = 0x800, // Program Map Table dump
CCX_DMT_LEVENSHTEIN = 0x1000, // Levenshtein distance calculations
CCX_DMT_DVB = 0x2000, // DVB
CCX_DMT_DUMPDEF = 0x4000 // Dump defective TS packets
CCX_DMT_TELETEXT = 0x200, // Show teletext debug?
CCX_DMT_PAT = 0x400, // Program Allocation Table dump
CCX_DMT_PMT = 0x800, // Program Map Table dump
CCX_DMT_LEVENSHTEIN = 0x1000, // Levenshtein distance calculations
CCX_DMT_DVB = 0x2000, // DVB
CCX_DMT_DUMPDEF = 0x4000 // Dump defective TS packets
};
// AVC NAL types
@@ -101,95 +101,95 @@ enum ccx_avc_nal_types
enum ccx_stream_type
{
CCX_STREAM_TYPE_UNKNOWNSTREAM = 0,
/*
/*
The later constants are defined by MPEG-TS standard
Explore at: https://exiftool.org/TagNames/M2TS.html
Explore at: https://exiftool.org/TagNames/M2TS.html
*/
CCX_STREAM_TYPE_VIDEO_MPEG1 = 0x01,
CCX_STREAM_TYPE_VIDEO_MPEG2 = 0x02,
CCX_STREAM_TYPE_AUDIO_MPEG1 = 0x03,
CCX_STREAM_TYPE_AUDIO_MPEG2 = 0x04,
CCX_STREAM_TYPE_PRIVATE_TABLE_MPEG2 = 0x05,
CCX_STREAM_TYPE_PRIVATE_MPEG2 = 0x06,
CCX_STREAM_TYPE_MHEG_PACKETS = 0x07,
CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC = 0x08,
CCX_STREAM_TYPE_ITU_T_H222_1 = 0x09,
CCX_STREAM_TYPE_VIDEO_MPEG1 = 0x01,
CCX_STREAM_TYPE_VIDEO_MPEG2 = 0x02,
CCX_STREAM_TYPE_AUDIO_MPEG1 = 0x03,
CCX_STREAM_TYPE_AUDIO_MPEG2 = 0x04,
CCX_STREAM_TYPE_PRIVATE_TABLE_MPEG2 = 0x05,
CCX_STREAM_TYPE_PRIVATE_MPEG2 = 0x06,
CCX_STREAM_TYPE_MHEG_PACKETS = 0x07,
CCX_STREAM_TYPE_MPEG2_ANNEX_A_DSM_CC = 0x08,
CCX_STREAM_TYPE_ITU_T_H222_1 = 0x09,
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_A = 0x0A,
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_B = 0x0B,
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_C = 0x0C,
CCX_STREAM_TYPE_ISO_IEC_13818_6_TYPE_D = 0x0D,
CCX_STREAM_TYPE_AUDIO_AAC = 0x0f,
CCX_STREAM_TYPE_VIDEO_MPEG4 = 0x10,
CCX_STREAM_TYPE_VIDEO_H264 = 0x1b,
CCX_STREAM_TYPE_VIDEO_HEVC = 0x24,
CCX_STREAM_TYPE_PRIVATE_USER_MPEG2 = 0x80,
CCX_STREAM_TYPE_AUDIO_AC3 = 0x81,
CCX_STREAM_TYPE_AUDIO_HDMV_DTS = 0x82,
CCX_STREAM_TYPE_AUDIO_DTS = 0x8a
CCX_STREAM_TYPE_AUDIO_AAC = 0x0f,
CCX_STREAM_TYPE_VIDEO_MPEG4 = 0x10,
CCX_STREAM_TYPE_VIDEO_H264 = 0x1b,
CCX_STREAM_TYPE_PRIVATE_USER_MPEG2 = 0x80,
CCX_STREAM_TYPE_AUDIO_AC3 = 0x81,
CCX_STREAM_TYPE_AUDIO_HDMV_DTS = 0x82,
CCX_STREAM_TYPE_AUDIO_DTS = 0x8a
};
enum ccx_mpeg_descriptor
{
/*
/*
The later constants are defined by ETSI EN 300 468 standard
Explore at: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.11.01_60/en_300468v011101p.pdf
Explore at: https://www.etsi.org/deliver/etsi_en/300400_300499/300468/01.11.01_60/en_300468v011101p.pdf
*/
CCX_MPEG_DSC_REGISTRATION = 0x05,
CCX_MPEG_DSC_DATA_STREAM_ALIGNMENT = 0x06,
CCX_MPEG_DSC_ISO639_LANGUAGE = 0x0A,
CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR = 0x45,
CCX_MPEG_DSC_REGISTRATION = 0x05,
CCX_MPEG_DSC_DATA_STREAM_ALIGNMENT = 0x06,
CCX_MPEG_DSC_ISO639_LANGUAGE = 0x0A,
CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR = 0x45,
CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR = 0x46,
CCX_MPEG_DSC_TELETEXT_DESCRIPTOR = 0x56,
CCX_MPEG_DSC_DVB_SUBTITLE = 0x59,
CCX_MPEG_DSC_TELETEXT_DESCRIPTOR = 0x56,
CCX_MPEG_DSC_DVB_SUBTITLE = 0x59,
/* User defined */
CCX_MPEG_DSC_CAPTION_SERVICE = 0x86,
CCX_MPEG_DESC_DATA_COMP = 0xfd // Consider to change DESC to DSC
CCX_MPEG_DSC_CAPTION_SERVICE = 0x86,
CCX_MPEG_DESC_DATA_COMP = 0xfd // Consider to change DESC to DSC
};
enum
{
CCX_MESSAGES_QUIET = 0,
CCX_MESSAGES_QUIET = 0,
CCX_MESSAGES_STDOUT = 1,
CCX_MESSAGES_STDERR = 2
};
enum ccx_datasource
{
CCX_DS_FILE = 0,
CCX_DS_STDIN = 1,
CCX_DS_FILE = 0,
CCX_DS_STDIN = 1,
CCX_DS_NETWORK = 2,
CCX_DS_TCP = 3
CCX_DS_TCP = 3
};
enum ccx_output_format
{
CCX_OF_RAW = 0,
CCX_OF_SRT = 1,
CCX_OF_SAMI = 2,
CCX_OF_RAW = 0,
CCX_OF_SRT = 1,
CCX_OF_SAMI = 2,
CCX_OF_TRANSCRIPT = 3,
CCX_OF_RCWT = 4,
CCX_OF_NULL = 5,
CCX_OF_SMPTETT = 6,
CCX_OF_SPUPNG = 7,
CCX_OF_DVDRAW = 8, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract
CCX_OF_WEBVTT = 9,
CCX_OF_RCWT = 4,
CCX_OF_NULL = 5,
CCX_OF_SMPTETT = 6,
CCX_OF_SPUPNG = 7,
CCX_OF_DVDRAW = 8, // See -d at http://www.theneitherworld.com/mcpoodle/SCC_TOOLS/DOCS/SCC_TOOLS.HTML#CCExtract
CCX_OF_WEBVTT = 9,
CCX_OF_SIMPLE_XML = 10,
CCX_OF_G608 = 11,
CCX_OF_CURL = 12,
CCX_OF_SSA = 13,
CCX_OF_MCC = 14,
CCX_OF_SCC = 15,
CCX_OF_CCD = 16,
CCX_OF_G608 = 11,
CCX_OF_CURL = 12,
CCX_OF_SSA = 13,
CCX_OF_MCC = 14,
CCX_OF_SCC = 15,
CCX_OF_CCD = 16,
};
enum ccx_output_date_format
{
ODF_NONE = 0,
ODF_HHMMSS = 1,
ODF_SECONDS = 2,
ODF_DATE = 3,
ODF_HHMMSSMS = 4 // HH:MM:SS,MILIS (.srt style)
ODF_NONE = 0,
ODF_HHMMSS = 1,
ODF_SECONDS = 2,
ODF_DATE = 3,
ODF_HHMMSSMS = 4 // HH:MM:SS,MILIS (.srt style)
};
enum ccx_stream_mode_enum
@@ -199,9 +199,9 @@ enum ccx_stream_mode_enum
CCX_SM_PROGRAM = 2,
CCX_SM_ASF = 3,
CCX_SM_MCPOODLESRAW = 4,
CCX_SM_RCWT = 5, // Raw Captions With Time, not used yet.
CCX_SM_MYTH = 6, // Use the myth loop
CCX_SM_MP4 = 7, // MP4, ISO-
CCX_SM_RCWT = 5, // Raw Captions With Time, not used yet.
CCX_SM_MYTH = 6, // Use the myth loop
CCX_SM_MP4 = 7, // MP4, ISO-
#ifdef WTV_DEBUG
CCX_SM_HEX_DUMP = 8, // Hexadecimal dump generated by wtvccdump
#endif
@@ -212,7 +212,6 @@ enum ccx_stream_mode_enum
CCX_SM_GXF = 11,
CCX_SM_MKV = 12,
CCX_SM_MXF = 13,
CCX_SM_SCC = 14, // Scenarist Closed Caption input
CCX_SM_AUTODETECT = 16
};
@@ -221,8 +220,8 @@ enum ccx_encoding_type
{
CCX_ENC_UNICODE = 0,
CCX_ENC_LATIN_1 = 1,
CCX_ENC_UTF_8 = 2,
CCX_ENC_ASCII = 3
CCX_ENC_UTF_8 = 2,
CCX_ENC_ASCII = 3
};
enum ccx_bufferdata_type
@@ -238,8 +237,7 @@ enum ccx_bufferdata_type
CCX_ISDB_SUBTITLE = 8,
/* BUffer where cc data contain 3 byte cc_valid ccdata 1 ccdata 2 */
CCX_RAW_TYPE = 9,
CCX_DVD_SUBTITLE = 10,
CCX_HEVC = 11
CCX_DVD_SUBTITLE = 10
};
enum ccx_frame_type
@@ -251,33 +249,32 @@ enum ccx_frame_type
CCX_FRAME_TYPE_D_FRAME = 4
};
typedef enum
{
NO = 0,
typedef enum {
NO = 0,
YES = 1,
UNDEFINED = 0xff
} bool_t;
enum ccx_code_type
{
CCX_CODEC_ANY = 0,
CCX_CODEC_ANY = 0,
CCX_CODEC_TELETEXT = 1,
CCX_CODEC_DVB = 2,
CCX_CODEC_ISDB_CC = 3,
CCX_CODEC_ATSC_CC = 4,
CCX_CODEC_NONE = 5
CCX_CODEC_DVB = 2,
CCX_CODEC_ISDB_CC = 3,
CCX_CODEC_ATSC_CC = 4,
CCX_CODEC_NONE = 5
};
/* Caption Distribution Packet */
enum cdp_section_type
{
/*
/*
The later constants are defined by SMPTE ST 334
Purchase for 80$ at: https://ieeexplore.ieee.org/document/8255806
*/
CDP_SECTION_DATA = 0x72,
CDP_SECTION_DATA = 0x72,
CDP_SECTION_SVC_INFO = 0x73,
CDP_SECTION_FOOTER = 0x74
CDP_SECTION_FOOTER = 0x74
};
/*
@@ -290,9 +287,9 @@ enum cdp_section_type
*
*/
#define IS_VALID_TELETEXT_DESC(desc) (((desc) == CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR) || \
((desc) == CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR) || \
((desc) == CCX_MPEG_DSC_TELETEXT_DESCRIPTOR))
#define IS_VALID_TELETEXT_DESC(desc) ( ((desc) == CCX_MPEG_DSC_VBI_DATA_DESCRIPTOR )|| \
( (desc) == CCX_MPEG_DSC_VBI_TELETEXT_DESCRIPTOR ) || \
( (desc) == CCX_MPEG_DSC_TELETEXT_DESCRIPTOR ) )
/*
* This macro to be used when you want to find out whether you
@@ -311,19 +308,19 @@ enum cdp_section_type
* @param f_sel pass the codec name whom you are testing to be feasible
* to parse.
*/
#define IS_FEASIBLE(u_sel, u_nsel, f_sel) (((u_sel) == CCX_CODEC_ANY && (u_nsel) != (f_sel)) || (u_sel) == (f_sel))
#define CCX_TXT_FORBIDDEN 0 // Ignore teletext packets
#define CCX_TXT_AUTO_NOT_YET_FOUND 1
#define CCX_TXT_IN_USE 2 // Positive auto-detected, or forced, etc
#define IS_FEASIBLE(u_sel,u_nsel,f_sel) ( ( (u_sel) == CCX_CODEC_ANY && (u_nsel) != (f_sel) ) || (u_sel) == (f_sel) )
#define CCX_TXT_FORBIDDEN 0 // Ignore teletext packets
#define CCX_TXT_AUTO_NOT_YET_FOUND 1
#define CCX_TXT_IN_USE 2 // Positive auto-detected, or forced, etc
#define NB_LANGUAGE 100
extern const char *language[NB_LANGUAGE];
#define DEF_VAL_STARTCREDITSNOTBEFORE "0"
#define DEF_VAL_STARTCREDITSNOTBEFORE "0"
// To catch the theme after the teaser in TV shows
#define DEF_VAL_STARTCREDITSNOTAFTER "5:00"
#define DEF_VAL_STARTCREDITSFORATLEAST "2"
#define DEF_VAL_STARTCREDITSFORATMOST "5"
#define DEF_VAL_ENDCREDITSFORATLEAST "2"
#define DEF_VAL_ENDCREDITSFORATMOST "5"
#define DEF_VAL_STARTCREDITSNOTAFTER "5:00"
#define DEF_VAL_STARTCREDITSFORATLEAST "2"
#define DEF_VAL_STARTCREDITSFORATMOST "5"
#define DEF_VAL_ENDCREDITSFORATLEAST "2"
#define DEF_VAL_ENDCREDITSFORATMOST "5"
#endif

View File

@@ -73,9 +73,7 @@ void init_options(struct ccx_s_options *options)
options->ocrlang = NULL; // By default, autodetect .traineddata file
options->ocr_oem = -1; // By default, OEM mode depends on the tesseract version
options->psm = 3; // Default PSM mode (3 is the default tesseract as well)
options->ocr_quantmode = 0; // No quantization (better OCR accuracy for DVB subtitles)
options->ocr_line_split = 0; // By default, don't split images into lines (pending testing)
options->ocr_blacklist = 1; // By default, use character blacklist to prevent common OCR errors (| vs I, etc.)
options->ocr_quantmode = 1; // CCExtractor's internal
options->mkvlang = NULL; // By default, all the languages are extracted
options->ignore_pts_jumps = 1;
options->analyze_video_stream = 0;
@@ -141,9 +139,7 @@ void init_options(struct ccx_s_options *options)
options->enc_cfg.services_charsets = NULL;
options->enc_cfg.all_services_charset = NULL;
options->enc_cfg.with_semaphore = 0;
options->enc_cfg.force_dropframe = 0; // Assume No Drop Frame for MCC Encode.
options->enc_cfg.scc_framerate = 0; // Default: 29.97fps for SCC output
options->enc_cfg.scc_accurate_timing = 0; // Default: off for backwards compatibility (issue #1120)
options->enc_cfg.force_dropframe = 0; // Assume No Drop Frame for MCC Encode.
options->enc_cfg.extract_only_708 = 0;
options->settings_dtvcc.enabled = 0;
@@ -151,13 +147,10 @@ void init_options(struct ccx_s_options *options)
options->settings_dtvcc.print_file_reports = 1;
options->settings_dtvcc.no_rollup = 0;
options->settings_dtvcc.report = NULL;
options->settings_dtvcc.timing = NULL;
memset(
options->settings_dtvcc.services_enabled, 0,
CCX_DTVCC_MAX_SERVICES * sizeof(options->settings_dtvcc.services_enabled[0]));
options->scc_framerate = 0; // Default: 29.97fps
#ifdef WITH_LIBCURL
options->curlposturl = NULL;
#endif

View File

@@ -15,38 +15,38 @@ struct demuxer_cfg
enum ccx_code_type codec;
enum ccx_code_type nocodec;
unsigned ts_autoprogram; // Try to find a stream with captions automatically (no -pn needed)
unsigned ts_autoprogram; // Try to find a stream with captions automatically (no -pn needed)
unsigned ts_allprogram;
unsigned ts_cappids[128]; // PID for stream that holds caption information
unsigned ts_cappids[128]; // PID for stream that holds caption information
int nb_ts_cappid;
unsigned ts_forced_cappid; // If 1, never mess with the selected PID
int ts_forced_program; // Specific program to process in TS files, if ts_forced_program_selected==1
unsigned ts_forced_cappid ; // If 1, never mess with the selected PID
int ts_forced_program; // Specific program to process in TS files, if ts_forced_program_selected==1
unsigned ts_forced_program_selected;
int ts_datastreamtype; // User WANTED stream type (i.e. use the stream that has this type)
int ts_datastreamtype ; // User WANTED stream type (i.e. use the stream that has this type)
unsigned ts_forced_streamtype; // User selected (forced) stream type
};
struct encoder_cfg
{
int extract; // Extract 1st, 2nd or both fields
int dtvcc_extract; // 1 or 0
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
int extract; // Extract 1st, 2nd or both fields
int dtvcc_extract; // 1 or 0
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
char *output_filename;
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
int keep_output_closed;
int force_flush; // Force flush on content write
int append_mode; // Append mode for output files
int ucla; // 1 if -UCLA used, 0 if not
int force_flush; // Force flush on content write
int append_mode; // Append mode for output files
int ucla; // 1 if -UCLA used, 0 if not
enum ccx_encoding_type encoding;
enum ccx_output_date_format date_format;
char millis_separator;
int autodash; // Add dashes (-) before each speaker automatically?
int trim_subs; // " Remove spaces at sides? "
int autodash; // Add dashes (-) before each speaker automatically?
int trim_subs; // " Remove spaces at sides? "
int sentence_cap; // FIX CASE? = Fix case?
int splitbysentence; // Split text into complete sentences and prorate time?
#ifdef WITH_LIBCURL
char *curlposturl; // If out=curl, where do we send the data to?
char *curlposturl; // If out=curl, where do we send the data to?
#endif
int filter_profanity; // Censors profane words from subtitles
@@ -54,49 +54,45 @@ struct encoder_cfg
/* Credit stuff */
char *start_credits_text;
char *end_credits_text;
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
struct ccx_boundary_time startcreditsforatleast, startcreditsforatmost; // How long to display them?
struct ccx_boundary_time endcreditsforatleast, endcreditsforatmost;
ccx_encoders_transcript_format transcript_settings; // Keeps the settings for generating transcript output files.
unsigned int send_to_srv;
int no_bom; // Set to 1 when no BOM (Byte Order Mark) should be used for files. Note, this might make files unreadable in windows!
int no_bom; // Set to 1 when no BOM (Byte Order Mark) should be used for files. Note, this might make files unreadable in windows!
char *first_input_file;
int multiple_files;
int no_font_color;
int no_type_setting;
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
int line_terminator_lf; // 0 = CRLF, 1=LF
LLONG subs_delay; // ms to delay (or advance) subs
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
int line_terminator_lf; // 0 = CRLF, 1=LF
LLONG subs_delay; // ms to delay (or advance) subs
int program_number;
unsigned char in_format;
int nospupngocr; // 1 if we don't want to OCR bitmaps to add the text as comments in the XML file in spupng
int nospupngocr; // 1 if we don't want to OCR bitmaps to add the text as comments in the XML file in spupng
// MCC File
int force_dropframe; // 1 if dropframe frame count should be used. defaults to no drop frame.
// SCC output framerate
int scc_framerate; // SCC output framerate: 0=29.97 (default), 1=24, 2=25, 3=30
int scc_accurate_timing; // If 1, use bandwidth-aware timing for broadcast compliance (issue #1120)
int force_dropframe; // 1 if dropframe frame count should be used. defaults to no drop frame.
// text -> png (text render)
char *render_font; // The font used to render text if needed (e.g. teletext->spupng)
char *render_font; // The font used to render text if needed (e.g. teletext->spupng)
char *render_font_italics;
// CEA-708
//CEA-708
int services_enabled[CCX_DTVCC_MAX_SERVICES];
char **services_charsets;
char *all_services_charset;
int extract_only_708; // 1 if only 708 subs extraction is enabled
char** services_charsets;
char* all_services_charset;
int extract_only_708; // 1 if only 708 subs extraction is enabled
};
struct ccx_s_options // Options from user parameters
{
int extract; // Extract 1st, 2nd or both fields
int no_rollup; // Disable roll-up emulation (no duplicate output in generated file)
int extract; // Extract 1st, 2nd or both fields
int no_rollup; // Disable roll-up emulation (no duplicate output in generated file)
int noscte20;
int webvtt_create_css;
int cc_channel; // Channel we want to dump in srt mode
int cc_channel; // Channel we want to dump in srt mode
int buffer_input;
int nofontcolor;
int nohtmlescape;
@@ -104,59 +100,57 @@ struct ccx_s_options // Options from user parameters
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
int print_file_reports;
ccx_decoder_608_settings settings_608; // Contains the settings for the 608 decoder.
ccx_decoder_dtvcc_settings settings_dtvcc; // Same for 708 decoder
int is_608_enabled; // Is 608 enabled by explicitly using flags(-1,-2,-12)
int is_708_enabled; // Is 708 enabled by explicitly using flags(-svc)
ccx_decoder_608_settings settings_608; // Contains the settings for the 608 decoder.
ccx_decoder_dtvcc_settings settings_dtvcc; // Same for 708 decoder
int is_608_enabled; // Is 608 enabled by explicitly using flags(-1,-2,-12)
int is_708_enabled; // Is 708 enabled by explicitly using flags(-svc)
char millis_separator;
int binary_concat; // Disabled by -ve or --videoedited
int use_gop_as_pts; // Use GOP instead of PTS timing (0=do as needed, 1=always, -1=never)
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
int no_progress_bar; // If 1, suppress the output of the progress to stdout
char *sentence_cap_file; // Extra capitalization word file
int live_stream; /* -1 -> Not a complete file but a live stream, without timeout
0 -> A regular file
>0 -> Live stream with a timeout of this value in seconds */
char *filter_profanity_file; // Extra profanity word file
int messages_target; // 0 = nowhere (quiet), 1=stdout, 2=stderr
int timestamp_map; // If 1, add WebVTT X-TIMESTAMP-MAP header
int binary_concat; // Disabled by -ve or --videoedited
int use_gop_as_pts; // Use GOP instead of PTS timing (0=do as needed, 1=always, -1=never)
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
int no_progress_bar; // If 1, suppress the output of the progress to stdout
char *sentence_cap_file; // Extra capitalization word file
int live_stream; /* -1 -> Not a complete file but a live stream, without timeout
0 -> A regular file
>0 -> Live stream with a timeout of this value in seconds */
char *filter_profanity_file; // Extra profanity word file
int messages_target; // 0 = nowhere (quiet), 1=stdout, 2=stderr
int timestamp_map; // If 1, add WebVTT X-TIMESTAMP-MAP header
/* Levenshtein's parameters, for string comparison */
int dolevdist; // 0 => don't attempt to correct typos with this algorithm
int dolevdist; // 0 => don't attempt to correct typos with this algorithm
int levdistmincnt, levdistmaxpct; // Means 2 fails or less is "the same", 10% or less is also "the same"
int investigate_packets; // Look for captions in all packets when everything else fails
int fullbin; // Disable pruning of padding cc blocks
int nosync; // Disable syncing
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
int wtvconvertfix; // Fix broken Windows 7 conversion
int investigate_packets; // Look for captions in all packets when everything else fails
int fullbin; // Disable pruning of padding cc blocks
int nosync; // Disable syncing
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
int wtvconvertfix; // Fix broken Windows 7 conversion
int wtvmpeg2;
int auto_myth; // Use myth-tv mpeg code? 0=no, 1=yes, 2=auto
int auto_myth; // Use myth-tv mpeg code? 0=no, 1=yes, 2=auto
/* MP4 related stuff */
unsigned mp4vidtrack; // Process the video track even if a CC dedicated track exists.
int extract_chapters; // If 1, extracts chapters (if present), from MP4 files.
unsigned mp4vidtrack; // Process the video track even if a CC dedicated track exists.
int extract_chapters; // If 1, extracts chapters (if present), from MP4 files.
/* General settings */
int usepicorder; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams
int xmltv; // 1 = full output. 2 = live output. 3 = both
int xmltvliveinterval; // interval in seconds between writing xmltv output files in live mode
int xmltvoutputinterval; // interval in seconds between writing xmltv full file output
int xmltvonlycurrent; // 0 off 1 on
int usepicorder; // Force the use of pic_order_cnt_lsb in AVC/H.264 data streams
int xmltv; // 1 = full output. 2 = live output. 3 = both
int xmltvliveinterval; // interval in seconds between writing xmltv output files in live mode
int xmltvoutputinterval; // interval in seconds between writing xmltv full file output
int xmltvonlycurrent; // 0 off 1 on
int keep_output_closed;
int force_flush; // Force flush on content write
int append_mode; // Append mode for output files
int ucla; // 1 if UCLA used, 0 if not
int tickertext; // 1 if ticker text style burned in subs, 0 if not
int hardsubx; // 1 if burned-in subtitles to be extracted
int hardsubx_and_common; // 1 if both burned-in and not burned in need to be extracted
char *dvblang; // The name of the language stream for DVB
const char *ocrlang; // The name of the .traineddata file to be loaded with tesseract
int ocr_oem; // The Tesseract OEM mode, could be 0 (default), 1 or 2
int psm; // The Tesseract PSM mode, could be between 0 and 13. 3 is tesseract default
int ocr_quantmode; // How to quantize the bitmap before passing to to tesseract (0=no quantization at all, 1=CCExtractor's internal)
int ocr_line_split; // If 1, split images into lines before OCR (uses PSM 7 for better accuracy)
int ocr_blacklist; // If 1, use character blacklist to prevent common OCR errors (default: enabled)
char *mkvlang; // The name of the language stream for MKV
int analyze_video_stream; // If 1, the video stream will be processed even if we're using a different one for subtitles.
int force_flush; // Force flush on content write
int append_mode; // Append mode for output files
int ucla; // 1 if UCLA used, 0 if not
int tickertext; // 1 if ticker text style burned in subs, 0 if not
int hardsubx; // 1 if burned-in subtitles to be extracted
int hardsubx_and_common; // 1 if both burned-in and not burned in need to be extracted
char *dvblang; // The name of the language stream for DVB
const char *ocrlang; // The name of the .traineddata file to be loaded with tesseract
int ocr_oem; // The Tesseract OEM mode, could be 0 (default), 1 or 2
int psm; // The Tesseract PSM mode, could be between 0 and 13. 3 is tesseract default
int ocr_quantmode; // How to quantize the bitmap before passing to to tesseract (0=no quantization at all, 1=CCExtractor's internal)
char *mkvlang; // The name of the language stream for MKV
int analyze_video_stream; // If 1, the video stream will be processed even if we're using a different one for subtitles.
/*HardsubX related stuff*/
int hardsubx_ocr_mode;
@@ -170,43 +164,42 @@ struct ccx_s_options // Options from user parameters
ccx_encoders_transcript_format transcript_settings; // Keeps the settings for generating transcript output files.
enum ccx_output_date_format date_format;
unsigned send_to_srv;
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
int write_format_rewritten;
int use_ass_instead_of_ssa;
int use_webvtt_styling;
LLONG debug_mask; // dbg_print will use this mask to print or ignore different types
LLONG debug_mask_on_debug; // If we're using temp_debug to enable/disable debug "live", this is the mask when temp_debug=1
LLONG debug_mask; // dbg_print will use this mask to print or ignore different types
LLONG debug_mask_on_debug; // If we're using temp_debug to enable/disable debug "live", this is the mask when temp_debug=1
/* Networking */
char *udpsrc;
char *udpaddr;
unsigned udpport; // Non-zero => Listen for UDP packets on this port, no files.
unsigned udpport; // Non-zero => Listen for UDP packets on this port, no files.
char *tcpport;
char *tcp_password;
char *tcp_desc;
char *srv_addr;
char *srv_port;
int noautotimeref; // Do NOT set time automatically?
enum ccx_datasource input_source; // Files, stdin or network
int noautotimeref; // Do NOT set time automatically?
enum ccx_datasource input_source; // Files, stdin or network
char *output_filename;
char **inputfile; // List of files to process
int num_input_files; // How many?
char **inputfile; // List of files to process
int num_input_files; // How many?
struct demuxer_cfg demux_cfg;
struct encoder_cfg enc_cfg;
LLONG subs_delay; // ms to delay (or advance) subs
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
int pes_header_to_stdout; // If this is set to 1, the PES Header will be printed to console (debugging purposes)
int ignore_pts_jumps; // If 1, the program will ignore PTS jumps. Sometimes this parameter is required for DVB subs with > 30s pause time
LLONG subs_delay; // ms to delay (or advance) subs
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
int pes_header_to_stdout; // If this is set to 1, the PES Header will be printed to console (debugging purposes)
int ignore_pts_jumps; // If 1, the program will ignore PTS jumps. Sometimes this parameter is required for DVB subs with > 30s pause time
int multiprogram;
int out_interval;
int segment_on_key_frames_only;
int scc_framerate; // SCC input framerate: 0=29.97 (default), 1=24, 2=25, 3=30
#ifdef WITH_LIBCURL
char *curlposturl;
#endif
};
extern struct ccx_s_options ccx_options;
void init_options(struct ccx_s_options *options);
void init_options (struct ccx_s_options *options);
#endif

View File

@@ -1,122 +1,123 @@
#ifndef CCX_PLATFORM_H
#define CCX_PLATFORM_H
#define CCX_PLATFORM_H
// Default includes (cross-platform)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <stdarg.h>
#include <errno.h>
// Default includes (cross-platform)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <time.h>
#include <fcntl.h>
#include <stdarg.h>
#include <errno.h>
#define __STDC_FORMAT_MACROS
#define __STDC_FORMAT_MACROS
#ifdef _WIN32
#define inline _inline
#define typeof decltype
#include <io.h>
#include <ws2tcpip.h>
#include <windows.h>
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#include "inttypes.h"
#undef UINT64_MAX
#define UINT64_MAX _UI64_MAX
typedef int socklen_t;
#if !defined(__MINGW64__) && !defined(__MINGW32__)
typedef int ssize_t;
#endif
typedef uint32_t in_addr_t;
#ifndef IN_CLASSD
#define IN_CLASSD(i) (((INT32)(i) & 0xf0000000) == 0xe0000000)
#define IN_MULTICAST(i) IN_CLASSD(i)
#endif
#include <direct.h>
#define mkdir(path, mode) _mkdir(path)
#ifndef snprintf
// Added ifndef because VS2013 warns for macro redefinition.
#define snprintf(buf, len, fmt, ...) _snprintf(buf, len, fmt, __VA_ARGS__)
#endif
#define sleep(sec) Sleep((sec) * 1000)
#ifdef _WIN32
#define inline _inline
#define typeof decltype
#include <io.h>
#include <ws2tcpip.h>
#include <windows.h>
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
#include "inttypes.h"
#undef UINT64_MAX
#define UINT64_MAX _UI64_MAX
typedef int socklen_t;
#if !defined(__MINGW64__) && !defined(__MINGW32__)
typedef int ssize_t;
#endif
typedef uint32_t in_addr_t;
#ifndef IN_CLASSD
#define IN_CLASSD(i) (((INT32)(i) & 0xf0000000) == 0xe0000000)
#define IN_MULTICAST(i) IN_CLASSD(i)
#endif
#include <direct.h>
#define mkdir(path, mode) _mkdir(path)
#ifndef snprintf
// Added ifndef because VS2013 warns for macro redefinition.
#define snprintf(buf, len, fmt, ...) _snprintf(buf, len, fmt, __VA_ARGS__)
#endif
#define sleep(sec) Sleep((sec) * 1000)
#include <fcntl.h>
#else // _WIN32
#include <unistd.h>
#define __STDC_LIMIT_MACROS
#include <inttypes.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif // _WIN32
#include <fcntl.h>
#else // _WIN32
#include <unistd.h>
#define __STDC_LIMIT_MACROS
#include <inttypes.h>
#include <stdint.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <sys/stat.h>
#include <sys/types.h>
#endif // _WIN32
// #include "disable_warnings.h"
//#include "disable_warnings.h"
#if defined(_MSC_VER) && !defined(__clang__)
#include "stdintmsc.h"
// Don't bug me with strcpy() deprecation warnings
#pragma warning(disable : 4996)
#else
#include <stdint.h>
#endif
#if defined(_MSC_VER) && !defined(__clang__)
#include "stdintmsc.h"
// Don't bug me with strcpy() deprecation warnings
#pragma warning(disable : 4996)
#else
#include <stdint.h>
#endif
#ifdef __OpenBSD__
#define FOPEN64 fopen
#define OPEN open
#define FSEEK fseek
#define FTELL ftell
#define LSEEK lseek
#define FSTAT fstat
#else
#ifdef _WIN32
#define OPEN _open
// 64 bit file functions
#if defined(_MSC_VER)
#define FSEEK _fseeki64
#define FTELL _ftelli64
#else
// For MinGW
#define FSEEK fseeko64
#define FTELL ftello64
#endif
#define TELL _telli64
#define LSEEK _lseeki64
typedef struct _stati64 FSTATSTRUCT;
#else
// Linux internally maps these functions to 64bit usage,
// if _FILE_OFFSET_BITS macro is set to 64
#define FOPEN64 fopen
#define OPEN open
#define LSEEK lseek
#define FSEEK fseek
#define FTELL ftell
#define FSTAT fstat
#define TELL tell
#include <stdint.h>
#endif
#endif
#ifdef __OpenBSD__
#define FOPEN64 fopen
#define OPEN open
#define FSEEK fseek
#define FTELL ftell
#define LSEEK lseek
#define FSTAT fstat
#else
#ifdef _WIN32
#define OPEN _open
// 64 bit file functions
#if defined(_MSC_VER)
#define FSEEK _fseeki64
#define FTELL _ftelli64
#else
// For MinGW
#define FSEEK fseeko64
#define FTELL ftello64
#endif
#define TELL _telli64
#define LSEEK _lseeki64
typedef struct _stati64 FSTATSTRUCT;
#else
// Linux internally maps these functions to 64bit usage,
// if _FILE_OFFSET_BITS macro is set to 64
#define FOPEN64 fopen
#define OPEN open
#define LSEEK lseek
#define FSEEK fseek
#define FTELL ftell
#define FSTAT fstat
#define TELL tell
#include <stdint.h>
#endif
#endif
#ifndef int64_t_C
#define int64_t_C(c) (c##LL)
#define uint64_t_C(c) (c##ULL)
#endif
#ifndef int64_t_C
#define int64_t_C(c) (c ## LL)
#define uint64_t_C(c) (c ## ULL)
#endif
#ifndef O_BINARY
#define O_BINARY 0 // Not present in Linux because it's always binary
#endif
#ifndef O_BINARY
#define O_BINARY 0 // Not present in Linux because it's always binary
#endif
#ifndef max
#define max(a, b) (((a) > (b)) ? (a) : (b))
#endif
#ifndef max
#define max(a,b) (((a) > (b)) ? (a) : (b))
#endif
typedef int64_t LLONG;
typedef uint64_t ULLONG;
typedef uint8_t UBYTE;
typedef int64_t LLONG;
typedef uint64_t ULLONG;
typedef uint8_t UBYTE;
#endif // CCX_PLATFORM_H

View File

@@ -3,28 +3,26 @@
#include "ccx_common_constants.h"
enum ccx_common_logging_gui
{
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME, // Called with xds_program_name
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR, // Called with current_xds_min, current_xds_hour, current_xds_date, current_xds_month
enum ccx_common_logging_gui {
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_NAME, // Called with xds_program_name
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_ID_NR, // Called with current_xds_min, current_xds_hour, current_xds_date, current_xds_month
CCX_COMMON_LOGGING_GUI_XDS_PROGRAM_DESCRIPTION, // Called with line_num, xds_desc
CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS // Called with current_xds_call_letters
CCX_COMMON_LOGGING_GUI_XDS_CALL_LETTERS // Called with current_xds_call_letters
};
struct ccx_common_logging_t
{
LLONG debug_mask; // The debug mask that is used to determine if things should be printed or not.
void (*fatal_ftn)(int exit_code, const char *fmt, ...); // Used when an unrecoverable error happens. This should log output/save the error and then exit the program.
void (*debug_ftn)(LLONG mask, const char *fmt, ...); // Used to process debug output. Mask can be ignored (custom set by debug_mask).
void (*log_ftn)(const char *fmt, ...); // Used to print things. Replacement of standard printf, to allow more control.
void (*gui_ftn)(enum ccx_common_logging_gui message_type, ...); // Used to display things in a gui (if appropriate). Is called with the message_type and appropriate variables (described in enum)
struct ccx_common_logging_t {
LLONG debug_mask; // The debug mask that is used to determine if things should be printed or not.
void(*fatal_ftn) (int exit_code, const char *fmt, ...); // Used when an unrecoverable error happens. This should log output/save the error and then exit the program.
void(*debug_ftn) (LLONG mask, const char *fmt, ...); // Used to process debug output. Mask can be ignored (custom set by debug_mask).
void(*log_ftn)(const char *fmt, ...); // Used to print things. Replacement of standard printf, to allow more control.
void(*gui_ftn)(enum ccx_common_logging_gui message_type, ...); // Used to display things in a gui (if appropriate). Is called with the message_type and appropriate variables (described in enum)
};
extern struct ccx_common_logging_t ccx_common_logging;
enum subdatatype
{
CC_DATATYPE_GENERIC = 0,
CC_DATATYPE_DVB = 1
CC_DATATYPE_GENERIC=0,
CC_DATATYPE_DVB=1
};
enum subtype
@@ -36,18 +34,18 @@ enum subtype
};
/**
* Raw Subtitle struct used as output of decoder (cc608)
* and input for encoder (sami, srt, transcript or smptett etc)
*
* if subtype CC_BITMAP then data contain nb_data numbers of rectangle
* which have to be displayed at same time.
*/
* Raw Subtitle struct used as output of decoder (cc608)
* and input for encoder (sami, srt, transcript or smptett etc)
*
* if subtype CC_BITMAP then data contain nb_data numbers of rectangle
* which have to be displayed at same time.
*/
struct cc_subtitle
{
/**
* A generic data which contain data according to decoder
* @warn decoder cant output multiple types of data
*/
* A generic data which contain data according to decoder
* @warn decoder cant output multiple types of data
*/
void *data;
enum subdatatype datatype;
@@ -58,11 +56,11 @@ struct cc_subtitle
enum subtype type;
/** Encoding type of Text, must be ignored in case of subtype as bitmap or cc_screen*/
enum ccx_encoding_type enc_type;
enum ccx_encoding_type enc_type;
/* set only when all the data is to be displayed at same time
* Unit is of time is ms
*/
* Unit is of time is ms
*/
LLONG start_time;
LLONG end_time;
@@ -74,19 +72,13 @@ struct cc_subtitle
/** flag to tell that decoder has given output */
int got_output;
char mode[5];
char info[4];
/** Used for DVB end time in ms */
int time_out;
/** Raw PTS value when this subtitle started (for DVB timing) */
LLONG start_pts;
/** Teletext page number (for multi-page extraction, issue #665) */
uint16_t teletext_page;
struct cc_subtitle *next;
struct cc_subtitle *prev;
};

View File

@@ -35,8 +35,6 @@ void ccxr_set_current_pts(struct ccx_common_timing_ctx *ctx, LLONG pts);
int ccxr_set_fts(struct ccx_common_timing_ctx *ctx);
LLONG ccxr_get_fts(struct ccx_common_timing_ctx *ctx, int current_field);
LLONG ccxr_get_fts_max(struct ccx_common_timing_ctx *ctx);
LLONG ccxr_get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field);
LLONG ccxr_get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field);
char *ccxr_print_mstime_static(LLONG mstime, char *buf);
void ccxr_print_debug_timing(struct ccx_common_timing_ctx *ctx);
void ccxr_calculate_ms_gop_time(struct gop_time_code *g);
@@ -65,9 +63,6 @@ struct ccx_common_timing_ctx *init_timing_ctx(struct ccx_common_timing_settings_
ctx->current_pts = 0;
ctx->current_picture_coding_type = CCX_FRAME_TYPE_RESET_OR_UNKNOWN;
ctx->min_pts_adjusted = 0;
ctx->seen_known_frame_type = 0;
ctx->pending_min_pts = 0x01FFFFFFFFLL;
ctx->unknown_frame_count = 0;
ctx->min_pts = 0x01FFFFFFFFLL; // 33 bit
ctx->max_pts = 0;
ctx->sync_pts = 0;
@@ -113,18 +108,15 @@ LLONG get_fts_max(struct ccx_common_timing_ctx *ctx)
/**
* SCC Time formatting
* Note: buf must have at least 32 bytes available from the write position
*/
size_t print_scc_time(struct ccx_boundary_time time, char *buf)
{
char *fmt = "%02u:%02u:%02u;%02u";
double frame;
// Format produces "HH:MM:SS;FF" = 11 chars + null, use 32 for safety
const size_t max_time_len = 32;
frame = ((double)(time.time_in_ms - 1000 * (time.ss + 60 * (time.mm + 60 * time.hh))) * 29.97 / 1000);
return (size_t)snprintf(buf + time.set, max_time_len, fmt, time.hh, time.mm, time.ss, (unsigned)frame);
return (size_t)sprintf(buf + time.set, fmt, time.hh, time.mm, time.ss, (unsigned)frame);
}
struct ccx_boundary_time get_time(LLONG time)
@@ -145,14 +137,11 @@ struct ccx_boundary_time get_time(LLONG time)
/**
* Fill buffer with a time string using specified format
* @param fmt has to contain 4 format specifiers for h, m, s and ms respectively
* Note: buf must have at least 32 bytes available from the write position
*/
size_t print_mstime_buff(LLONG mstime, char *fmt, char *buf)
{
unsigned hh, mm, ss, ms;
int signoffset = (mstime < 0 ? 1 : 0);
// Typical format produces "HH:MM:SS:MSS" = 12 chars + null, use 32 for safety
const size_t max_time_len = 32;
if (mstime < 0) // Avoid loss of data warning with abs()
mstime = -mstime;
@@ -164,7 +153,7 @@ size_t print_mstime_buff(LLONG mstime, char *fmt, char *buf)
buf[0] = '-';
return (size_t)snprintf(buf + signoffset, max_time_len, fmt, hh, mm, ss, ms);
return (size_t)sprintf(buf + signoffset, fmt, hh, mm, ss, ms);
}
/* Fill a static buffer with a time string (hh:mm:ss:ms) corresponding

View File

@@ -17,43 +17,40 @@ struct gop_time_code
struct ccx_common_timing_settings_t
{
int disable_sync_check; // If 1, timeline jumps will be ignored. This is important in several input formats that are assumed to have correct timing, no matter what.
int no_sync; // If 1, there will be no sync at all. Mostly useful for debugging.
int disable_sync_check; // If 1, timeline jumps will be ignored. This is important in several input formats that are assumed to have correct timing, no matter what.
int no_sync; // If 1, there will be no sync at all. Mostly useful for debugging.
int is_elementary_stream; // Needs to be set, as it's used in set_fts.
LLONG *file_position; // The position of the file
LLONG *file_position; // The position of the file
};
extern struct ccx_common_timing_settings_t ccx_common_timing_settings;
struct ccx_boundary_time
{
int hh, mm, ss;
int hh,mm,ss;
LLONG time_in_ms;
int set;
};
struct ccx_common_timing_ctx
{
int pts_set; // 0 = No, 1 = received, 2 = min_pts set
int min_pts_adjusted; // 0 = No, 1=Yes (don't adjust again)
int seen_known_frame_type; // 0 = No, 1 = Yes. Tracks if we've seen a frame with known type
LLONG pending_min_pts; // Minimum PTS seen while waiting for frame type determination
unsigned int unknown_frame_count; // Count of set_fts calls with unknown frame type
int pts_set; // 0 = No, 1 = received, 2 = min_pts set
int min_pts_adjusted; // 0 = No, 1=Yes (don't adjust again)
LLONG current_pts;
enum ccx_frame_type current_picture_coding_type;
int current_tref; // Store temporal reference of current frame
int current_tref; // Store temporal reference of current frame
LLONG min_pts;
LLONG max_pts;
LLONG sync_pts;
LLONG minimum_fts; // No screen should start before this FTS
LLONG fts_now; // Time stamp of current file (w/ fts_offset, w/o fts_global)
LLONG fts_now; // Time stamp of current file (w/ fts_offset, w/o fts_global)
LLONG fts_offset; // Time before first sync_pts
LLONG fts_fc_offset; // Time before first GOP
LLONG fts_max; // Remember the maximum fts that we saw in current file
LLONG fts_max; // Remember the maximum fts that we saw in current file
LLONG fts_global; // Duration of previous files (-ve mode)
int sync_pts2fts_set; // 0 = No, 1 = Yes
LLONG sync_pts2fts_fts;
LLONG sync_pts2fts_pts;
int pts_reset; // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev
int pts_reset; // 0 = No, 1 = Yes. PTS resets when current_pts is lower than prev
};
// Count 608 (per field) and 708 blocks since last set_fts() call
extern int cb_field1, cb_field2, cb_708;
@@ -63,6 +60,7 @@ extern int MPEG_CLOCK_FREQ; // This is part of the standard
extern int max_dif;
extern unsigned pts_big_change;
extern enum ccx_frame_type current_picture_coding_type;
extern double current_fps;
extern int frames_since_ref_time;
@@ -87,7 +85,7 @@ LLONG get_fts_max(struct ccx_common_timing_ctx *ctx);
char *print_mstime_static(LLONG mstime);
size_t print_mstime_buff(LLONG mstime, char *fmt, char *buf);
void print_debug_timing(struct ccx_common_timing_ctx *ctx);
int gop_accepted(struct gop_time_code *g);
int gop_accepted(struct gop_time_code* g);
void calculate_ms_gop_time(struct gop_time_code *g);
#endif

View File

@@ -127,8 +127,6 @@ ccx_decoder_608_context *ccx_decoder_608_init_library(struct ccx_decoder_608_set
ccx_decoder_608_context *data = NULL;
data = malloc(sizeof(ccx_decoder_608_context));
if (!data)
return NULL;
data->cursor_column = 0;
data->cursor_row = 0;
@@ -149,11 +147,10 @@ ccx_decoder_608_context *ccx_decoder_608_init_library(struct ccx_decoder_608_set
data->my_field = field;
data->my_channel = channel;
data->have_cursor_position = 0;
data->rollup_from_popon = 0;
data->output_format = output_format;
data->cc_to_stdout = cc_to_stdout;
data->textprinted = 0;
// Note: ts_start_of_current_line already set to -1 above
data->ts_start_of_current_line = 0;
data->halt = halt;
@@ -201,9 +198,6 @@ void delete_to_end_of_row(ccx_decoder_608_context *context)
{
if (context->mode != MODE_TEXT)
{
if (context->cursor_row >= CCX_DECODER_608_SCREEN_ROWS)
return;
struct eia608_screen *use_buffer = get_writing_buffer(context);
for (int i = context->cursor_column; i <= CCX_DECODER_608_SCREEN_WIDTH - 1; i++)
{
@@ -224,10 +218,6 @@ void write_char(const unsigned char c, ccx_decoder_608_context *context)
/* printf ("\rWriting char [%c] at %s:%d:%d\n",c,
use_buffer == &wb->data608->buffer1?"B1":"B2",
wb->data608->cursor_row,wb->data608->cursor_column); */
if (context->cursor_row >= CCX_DECODER_608_SCREEN_ROWS || context->cursor_column >= CCX_DECODER_608_SCREEN_WIDTH)
return;
use_buffer->characters[context->cursor_row][context->cursor_column] = c;
use_buffer->colors[context->cursor_row][context->cursor_column] = context->current_color;
use_buffer->fonts[context->cursor_row][context->cursor_column] = context->font;
@@ -235,9 +225,7 @@ void write_char(const unsigned char c, ccx_decoder_608_context *context)
if (use_buffer->empty)
{
// Don't set start time if we're in a transition from pop-on to roll-up
// In this case, start time will be set when CR causes scrolling
if (MODE_POPON != context->mode && !context->rollup_from_popon)
if (MODE_POPON != context->mode)
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
}
use_buffer->empty = 0;
@@ -323,23 +311,12 @@ int write_cc_buffer(ccx_decoder_608_context *context, struct cc_subtitle *sub)
if (!data->empty && context->output_format != CCX_OF_NULL)
{
size_t new_size;
if (sub->nb_data + 1 > SIZE_MAX / sizeof(struct eia608_screen))
sub->data = (struct eia608_screen *)realloc(sub->data, (sub->nb_data + 1) * sizeof(*data));
if (!sub->data)
{
ccx_common_logging.log_ftn("Too many screens, cannot allocate more memory.\n");
ccx_common_logging.log_ftn("No Memory left");
return 0;
}
new_size = (sub->nb_data + 1) * sizeof(struct eia608_screen);
struct eia608_screen *new_data = (struct eia608_screen *)realloc(sub->data, new_size);
if (!new_data)
{
ccx_common_logging.log_ftn("Out of memory while reallocating screen buffer\n");
return 0;
}
sub->data = new_data;
sub->datatype = CC_DATATYPE_GENERIC;
memcpy(((struct eia608_screen *)sub->data) + sub->nb_data, data, sizeof(*data));
sub->nb_data++;
@@ -403,23 +380,12 @@ int write_cc_line(ccx_decoder_608_context *context, struct cc_subtitle *sub)
if (!data->empty)
{
size_t new_size;
if (sub->nb_data + 1 > SIZE_MAX / sizeof(struct eia608_screen))
sub->data = (struct eia608_screen *)realloc(sub->data, (sub->nb_data + 1) * sizeof(*data));
if (!sub->data)
{
ccx_common_logging.log_ftn("Too many screens, cannot allocate more memory.\n");
ccx_common_logging.log_ftn("No Memory left");
return 0;
}
new_size = (sub->nb_data + 1) * sizeof(struct eia608_screen);
struct eia608_screen *new_data = (struct eia608_screen *)realloc(sub->data, new_size);
if (!new_data)
{
ccx_common_logging.log_ftn("Out of memory while reallocating screen buffer\n");
return 0;
}
sub->data = new_data;
memcpy(((struct eia608_screen *)sub->data) + sub->nb_data, data, sizeof(*data));
data = (struct eia608_screen *)sub->data + sub->nb_data;
sub->datatype = CC_DATATYPE_GENERIC;
@@ -754,10 +720,6 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
if (write_cc_buffer(context, sub))
context->screenfuls_counter++;
erase_memory(context, true);
// Track transition from pop-on/paint-on to roll-up for timing adjustment
// Start time will be set when CR causes scrolling (matching FFmpeg behavior)
context->rollup_from_popon = 1;
context->ts_start_of_current_line = -1;
}
erase_memory(context, false);
@@ -810,15 +772,6 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
changes = check_roll_up(context);
if (changes)
{
// Handle pop-on to roll-up transition timing
// Use ts_start_of_current_line (when current line started) as the start time
// This matches FFmpeg's behavior of timestamping when the display changed
if (context->rollup_from_popon && context->ts_start_of_current_line > 0)
{
context->current_visible_start_ms = context->ts_start_of_current_line;
context->rollup_from_popon = 0;
}
// Only if the roll up would actually cause a line to disappear we write the buffer
if (context->output_format != CCX_OF_TRANSCRIPT)
{
@@ -828,18 +781,8 @@ void handle_command(unsigned char c1, const unsigned char c2, ccx_decoder_608_co
erase_memory(context, true); // Make sure the lines we just wrote aren't written again
}
}
roll_up(context); // The roll must be done anyway of course.
// When in pop-on to roll-up transition with changes=0 (first CR, only 1 line),
// preserve the CR time so the next caption uses the display state change time,
// not the character typing time. This matches FFmpeg's timing behavior.
if (context->rollup_from_popon && !changes)
{
context->ts_start_of_current_line = get_fts(context->timing, context->my_field);
}
else
{
context->ts_start_of_current_line = -1; // Unknown.
}
roll_up(context); // The roll must be done anyway of course.
context->ts_start_of_current_line = -1; // Unknown.
if (changes)
context->current_visible_start_ms = get_visible_start(context->timing, context->my_field);
context->cursor_column = 0;

View File

@@ -1,5 +1,5 @@
#ifndef CCX_DECODER_608_H
#define CCX_DECODER_608_H
#define CCX_DECODER_608_H
#include "ccx_common_platform.h"
#include "ccx_common_structs.h"
#include "ccx_decoders_structs.h"
@@ -19,11 +19,11 @@ struct ccx_decoder_608_report
typedef struct ccx_decoder_608_settings
{
int direct_rollup; // Write roll-up captions directly instead of line by line?
int force_rollup; // 0=Disabled, 1, 2 or 3=max lines in roll-up mode
int no_rollup; // If 1, write one line at a time
int direct_rollup; // Write roll-up captions directly instead of line by line?
int force_rollup; // 0=Disabled, 1, 2 or 3=max lines in roll-up mode
int no_rollup; // If 1, write one line at a time
enum ccx_decoder_608_color_code default_color; // Default color to use.
int screens_to_process; // How many screenfuls we want? Use -1 for unlimited
int screens_to_process; // How many screenfuls we want? Use -1 for unlimited
struct ccx_decoder_608_report *report;
} ccx_decoder_608_settings;
@@ -34,33 +34,33 @@ typedef struct ccx_decoder_608_context
struct eia608_screen buffer2;
int cursor_row, cursor_column;
int visible_buffer;
int screenfuls_counter; // Number of meaningful screenfuls written
int screenfuls_counter; // Number of meaningful screenfuls written
LLONG current_visible_start_ms; // At what time did the current visible buffer became so?
enum cc_modes mode;
unsigned char last_c1, last_c2;
int channel; // Currently selected channel
enum ccx_decoder_608_color_code current_color; // Color we are currently using to write
enum font_bits font; // Font we are currently using to write
int channel; // Currently selected channel
enum ccx_decoder_608_color_code current_color; // Color we are currently using to write
enum font_bits font; // Font we are currently using to write
int rollup_base_row;
LLONG ts_start_of_current_line; /* Time at which the first character for current line was received, =-1 no character received yet */
LLONG ts_last_char_received; /* Time at which the last written character was received, =-1 no character received yet */
int new_channel; // The new channel after a channel change
int my_field; // Used for sanity checks
int my_channel; // Used for sanity checks
int rollup_from_popon; // Track transition from pop-on/paint-on to roll-up mode
int64_t bytes_processed_608; // To be written ONLY by process_608
LLONG ts_last_char_received; /* Time at which the last written character was received, =-1 no character received yet */
int new_channel; // The new channel after a channel change
int my_field; // Used for sanity checks
int my_channel; // Used for sanity checks
long bytes_processed_608; // To be written ONLY by process_608
int have_cursor_position;
int *halt; // Can be used to halt the feeding of caption data. Set to 1 if screens_to_progress != -1 && screenfuls_counter >= screens_to_process
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
int *halt; // Can be used to halt the feeding of caption data. Set to 1 if screens_to_progress != -1 && screenfuls_counter >= screens_to_process
int cc_to_stdout; // If this is set to 1, the stdout will be flushed when data was written to the screen during a process_608 call.
struct ccx_decoder_608_report *report;
LLONG subs_delay; // ms to delay (or advance) subs
LLONG subs_delay; // ms to delay (or advance) subs
enum ccx_output_format output_format; // What kind of output format should be used?
int textprinted;
struct ccx_common_timing_ctx *timing;
} ccx_decoder_608_context;
#define MAX_COLOR 10
extern const char *color_text[MAX_COLOR][2];
@@ -80,7 +80,7 @@ enum command_code
COM_ERASENONDISPLAYEDMEMORY = 11,
COM_BACKSPACE = 12,
COM_RESUMETEXTDISPLAY = 13,
COM_ALARMOFF = 14,
COM_ALARMOFF =14,
COM_ALARMON = 15,
COM_DELETETOENDOFROW = 16,
COM_RESUMEDIRECTCAPTIONING = 17,
@@ -89,14 +89,15 @@ enum command_code
COM_FAKE_RULLUP1 = 18
};
void ccx_decoder_608_dinit_library(void **ctx);
/*
*
*/
ccx_decoder_608_context *ccx_decoder_608_init_library(struct ccx_decoder_608_settings *settings, int channel,
int field, int *halt,
int cc_to_stdout,
enum ccx_output_format output_format, struct ccx_common_timing_ctx *timing);
ccx_decoder_608_context* ccx_decoder_608_init_library(struct ccx_decoder_608_settings *settings, int channel,
int field, int *halt,
int cc_to_stdout,
enum ccx_output_format output_format, struct ccx_common_timing_ctx *timing);
/**
* @param data raw cc608 data to be processed

View File

@@ -998,14 +998,6 @@ void dtvcc_handle_DFx_DefineWindow(dtvcc_service_decoder *decoder, int window_id
int row_count = (data[4] & 0xf) + 1; // according to CEA-708-D
int anchor_point = data[4] >> 4;
int col_count = (data[5] & 0x3f) + 1; // according to CEA-708-D
if (row_count > CCX_DTVCC_MAX_ROWS || col_count > CCX_DTVCC_MAX_COLUMNS)
{
ccx_common_logging.log_ftn("[CEA-708] Invalid window size %dx%d (max %dx%d), rejecting window definition\n",
row_count, col_count, CCX_DTVCC_MAX_ROWS, CCX_DTVCC_MAX_COLUMNS);
return;
}
int pen_style = data[6] & 0x7;
int win_style = (data[6] >> 3) & 0x7;
@@ -1033,18 +1025,6 @@ void dtvcc_handle_DFx_DefineWindow(dtvcc_service_decoder *decoder, int window_id
if (anchor_horizontal > CCX_DTVCC_SCREENGRID_COLUMNS - col_count)
anchor_horizontal = CCX_DTVCC_SCREENGRID_COLUMNS - col_count;
if (window->is_defined)
{
if (row_count < window->row_count)
{
// Remove the oldest row if the row count is reduced
for (int i = row_count; i < window->row_count; i++)
{
dtvcc_window_rollup(decoder, window);
}
}
}
window->priority = priority;
window->col_lock = col_lock;
window->row_lock = row_lock;
@@ -1349,14 +1329,6 @@ void dtvcc_handle_SPL_SetPenLocation(dtvcc_service_decoder *decoder, unsigned ch
}
dtvcc_window *window = &decoder->windows[decoder->current_window];
if (row >= window->row_count || col >= window->col_count)
{
ccx_common_logging.log_ftn("[CEA-708] dtvcc_handle_SPL_SetPenLocation: "
"Invalid pen location %d:%d for window size %dx%d, rejecting command\n",
row, col, window->row_count, window->col_count);
return;
}
window->pen_row = row;
window->pen_column = col;
}
@@ -1495,12 +1467,7 @@ int dtvcc_handle_C0(dtvcc_ctx *dtvcc,
else if (c0 >= 0x18 && c0 <= 0x1F)
{
if (c0 == DTVCC_C0_P16) // PE16
{
if (data_length >= 3)
dtvcc_handle_C0_P16(decoder, data + 1);
else
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_handle_C0: Not enough data for P16\n");
}
dtvcc_handle_C0_P16(decoder, data + 1);
len = 3;
}
if (len == -1)
@@ -1654,9 +1621,6 @@ int dtvcc_handle_extended_char(dtvcc_service_decoder *decoder, unsigned char *da
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] In dtvcc_handle_extended_char, "
"first data code: [%c], length: [%u]\n",
data[0], data_length);
if (data_length < 1)
return 0;
unsigned char c = 0x20; // Default to space
unsigned char code = data[0];
if (/* data[i]>=0x00 && */ code <= 0x1F) // Comment to silence warning
@@ -1725,17 +1689,8 @@ void dtvcc_process_service_block(dtvcc_ctx *dtvcc,
}
else // Use extended set
{
if (i + 1 >= data_length)
{
used = 1; // skip EXT1
}
else
{
used = dtvcc_handle_extended_char(decoder,
data + i + 1,
data_length - i - 1) +
1;
}
used = dtvcc_handle_extended_char(decoder, data + i + 1, data_length - 1);
used++; // Since we had DTVCC_C0_EXT1
}
i += used;
}
@@ -1787,12 +1742,6 @@ void dtvcc_process_current_packet(dtvcc_ctx *dtvcc, int len)
if (service_number == 7) // There is an extended header
{
if (pos + 1 >= dtvcc->current_packet + len)
{
ccx_common_logging.debug_ftn(CCX_DMT_708, "[CEA-708] dtvcc_process_current_packet: "
"Truncated extended header, stopping.\n");
break;
}
pos++;
service_number = (pos[0] & 0x3F); // 6 more significant bits
// printf ("Extended header: Service number: [%d]\n",service_number);

View File

@@ -6,7 +6,7 @@
#include "ccx_common_constants.h"
#include "ccx_common_structs.h"
#define CCX_DTVCC_MAX_PACKET_LENGTH 128 // According to EIA-708B, part 5
#define CCX_DTVCC_MAX_PACKET_LENGTH 128 //According to EIA-708B, part 5
#define CCX_DTVCC_MAX_SERVICES 63
#define CCX_DTVCC_MAX_ROWS 15
@@ -14,7 +14,7 @@
* This value should be 32, but there were 16-bit encoded samples (from Korea),
* where RowCount calculated another way and equals 46 (23[8bit]*2)
*/
#define CCX_DTVCC_MAX_COLUMNS (32 * 2)
#define CCX_DTVCC_MAX_COLUMNS (32*2)
#define CCX_DTVCC_SCREENGRID_ROWS 75
#define CCX_DTVCC_SCREENGRID_COLUMNS 210
@@ -30,14 +30,14 @@
enum DTVCC_COMMANDS_C0_CODES
{
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_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
DTVCC_C0_P16 = 0x18
};
enum DTVCC_COMMANDS_C1_CODES
@@ -86,21 +86,21 @@ struct DTVCC_S_COMMANDS_C1
enum dtvcc_window_justify
{
DTVCC_WINDOW_JUSTIFY_LEFT = 0,
DTVCC_WINDOW_JUSTIFY_RIGHT = 1,
DTVCC_WINDOW_JUSTIFY_CENTER = 2,
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 dtvcc_window_pd // Print Direction
enum dtvcc_window_pd //Print Direction
{
DTVCC_WINDOW_PD_LEFT_RIGHT = 0, // left -> right
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 dtvcc_window_sd // Scroll Direction
enum dtvcc_window_sd //Scroll Direction
{
DTVCC_WINDOW_SD_LEFT_RIGHT = 0,
DTVCC_WINDOW_SD_RIGHT_LEFT = 1,
@@ -108,14 +108,14 @@ enum dtvcc_window_sd // Scroll Direction
DTVCC_WINDOW_SD_BOTTOM_TOP = 3
};
enum dtvcc_window_sde // Scroll Display Effect
enum dtvcc_window_sde //Scroll Display Effect
{
DTVCC_WINDOW_SDE_SNAP = 0,
DTVCC_WINDOW_SDE_FADE = 1,
DTVCC_WINDOW_SDE_WIPE = 2
};
enum dtvcc_window_ed // Effect Direction
enum dtvcc_window_ed //Effect Direction
{
DTVCC_WINDOW_ED_LEFT_RIGHT = 0,
DTVCC_WINDOW_ED_RIGHT_LEFT = 1,
@@ -123,91 +123,91 @@ enum dtvcc_window_ed // Effect Direction
DTVCC_WINDOW_ED_BOTTOM_TOP = 3
};
enum dtvcc_window_fo // Fill Opacity
enum dtvcc_window_fo //Fill Opacity
{
DTVCC_WINDOW_FO_SOLID = 0,
DTVCC_WINDOW_FO_FLASH = 1,
DTVCC_WINDOW_FO_TRANSLUCENT = 2,
DTVCC_WINDOW_FO_SOLID = 0,
DTVCC_WINDOW_FO_FLASH = 1,
DTVCC_WINDOW_FO_TRANSLUCENT = 2,
DTVCC_WINDOW_FO_TRANSPARENT = 3
};
enum dtvcc_window_border
{
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
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 dtvcc_pen_size
{
DTVCC_PEN_SIZE_SMALL = 0,
DTVCC_PEN_SIZE_SMALL = 0,
DTVCC_PEN_SIZE_STANDART = 1,
DTVCC_PEN_SIZE_LARGE = 2
DTVCC_PEN_SIZE_LARGE = 2
};
enum dtvcc_pen_font_style
{
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
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 dtvcc_pen_text_tag
{
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
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 dtvcc_pen_offset
{
DTVCC_PEN_OFFSET_SUBSCRIPT = 0,
DTVCC_PEN_OFFSET_NORMAL = 1,
DTVCC_PEN_OFFSET_SUPERSCRIPT = 2
DTVCC_PEN_OFFSET_SUBSCRIPT = 0,
DTVCC_PEN_OFFSET_NORMAL = 1,
DTVCC_PEN_OFFSET_SUPERSCRIPT = 2
};
enum dtvcc_pen_edge
{
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
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 dtvcc_pen_anchor_point
{
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
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 dtvcc_pen_color
@@ -252,20 +252,12 @@ typedef struct dtvcc_window_attribs
*/
typedef struct dtvcc_symbol
{
unsigned short sym; // symbol itself, at least 16 bit
unsigned char init; // initialized or not. could be 0 or 1
unsigned short sym; //symbol itself, at least 16 bit
unsigned char init; //initialized or not. could be 0 or 1
} 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; \
}
#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;}
#define CCX_DTVCC_SYM(x) ((unsigned char)(x.sym))
#define CCX_DTVCC_SYM_IS_EMPTY(x) (x.init == 0)
#define CCX_DTVCC_SYM_IS_SET(x) (x.init == 1)
@@ -352,7 +344,7 @@ typedef struct dtvcc_ctx
{
int is_active;
int active_services_count;
int services_active[CCX_DTVCC_MAX_SERVICES]; // 0 - inactive, 1 - active
int services_active[CCX_DTVCC_MAX_SERVICES]; //0 - inactive, 1 - active
int report_enabled;
ccx_decoder_dtvcc_report *report;
@@ -365,20 +357,21 @@ typedef struct dtvcc_ctx
int last_sequence;
void *encoder; // we can't include header, so keeping it this way
void *encoder; //we can't include header, so keeping it this way
int no_rollup;
struct ccx_common_timing_ctx *timing;
} dtvcc_ctx;
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 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);
dtvcc_service_decoder *decoder,
unsigned char *data,
int data_length);
void dtvcc_tv_clear(dtvcc_service_decoder *decoder);
int dtvcc_decoder_has_visible_windows(dtvcc_service_decoder *decoder);
@@ -388,9 +381,9 @@ 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);
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);
@@ -413,7 +406,7 @@ 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,
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,
@@ -433,13 +426,13 @@ 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);
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);
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);

View File

@@ -57,7 +57,6 @@ void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int
return;
char *buf = (char *)encoder->buffer;
size_t remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
dtvcc_pen_color new_pen_color;
if (column_index >= CCX_DTVCC_SCREENGRID_COLUMNS)
@@ -67,11 +66,7 @@ void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int
if (pen_color.fg_color != new_pen_color.fg_color)
{
if (pen_color.fg_color != 0x3f && !open)
{
int written = snprintf(buf + (*buf_len), remaining, "</font>"); // should close older non-white color
if (written > 0 && (size_t)written < remaining)
(*buf_len) += written;
}
(*buf_len) += sprintf(buf + (*buf_len), "</font>"); // should close older non-white color
if (new_pen_color.fg_color != 0x3f && open)
{
@@ -80,10 +75,7 @@ void dtvcc_change_pen_colors(dtvcc_tv_screen *tv, dtvcc_pen_color pen_color, int
red = (255 / 3) * red;
green = (255 / 3) * green;
blue = (255 / 3) * blue;
remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
int written = snprintf(buf + (*buf_len), remaining, "<font color=\"#%02x%02x%02x\">", red, green, blue);
if (written > 0 && (size_t)written < remaining)
(*buf_len) += written;
(*buf_len) += sprintf(buf + (*buf_len), "<font color=\"#%02x%02x%02x\">", red, green, blue);
}
}
}
@@ -94,8 +86,6 @@ void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs
return;
char *buf = (char *)encoder->buffer;
size_t remaining;
int written;
dtvcc_pen_attribs new_pen_attribs;
if (column_index >= CCX_DTVCC_SCREENGRID_COLUMNS)
@@ -104,47 +94,33 @@ void dtvcc_change_pen_attribs(dtvcc_tv_screen *tv, dtvcc_pen_attribs pen_attribs
new_pen_attribs = tv->pen_attribs[row_index][column_index];
if (pen_attribs.italic != new_pen_attribs.italic)
{
remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
if (pen_attribs.italic && !open)
{
written = snprintf(buf + (*buf_len), remaining, "</i>");
if (written > 0 && (size_t)written < remaining)
(*buf_len) += written;
}
(*buf_len) += sprintf(buf + (*buf_len), "</i>");
if (!pen_attribs.italic && open)
{
written = snprintf(buf + (*buf_len), remaining, "<i>");
if (written > 0 && (size_t)written < remaining)
(*buf_len) += written;
}
(*buf_len) += sprintf(buf + (*buf_len), "<i>");
}
if (pen_attribs.underline != new_pen_attribs.underline)
{
remaining = INITIAL_ENC_BUFFER_CAPACITY - *buf_len;
if (pen_attribs.underline && !open)
{
written = snprintf(buf + (*buf_len), remaining, "</u>");
if (written > 0 && (size_t)written < remaining)
(*buf_len) += written;
}
(*buf_len) += sprintf(buf + (*buf_len), "</u>");
if (!pen_attribs.underline && open)
{
written = snprintf(buf + (*buf_len), remaining, "<u>");
if (written > 0 && (size_t)written < remaining)
(*buf_len) += written;
}
(*buf_len) += sprintf(buf + (*buf_len), "<u>");
}
}
size_t write_utf16_char(unsigned short utf16_char, char *out)
{
// Always write 2 bytes for consistent UTF-16BE encoding.
// Previously, this function wrote 1 byte for ASCII characters and 2 bytes
// for non-ASCII, creating an invalid mix that iconv couldn't handle properly.
// This caused garbled output with Japanese/Chinese characters (issue #1451).
out[0] = (unsigned char)(utf16_char >> 8);
out[1] = (unsigned char)(utf16_char & 0xff);
return 2;
if ((utf16_char >> 8) != 0)
{
out[0] = (unsigned char)(utf16_char >> 8);
out[1] = (unsigned char)(utf16_char & 0xff);
return 2;
}
else
{
out[0] = (unsigned char)(utf16_char);
return 1;
}
}
void dtvcc_write_row(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, int row_index, struct encoder_ctx *encoder, int use_colors)
@@ -231,31 +207,16 @@ void dtvcc_write_srt(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s
char *buf = (char *)encoder->buffer;
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY);
size_t buf_len = 0;
size_t remaining = INITIAL_ENC_BUFFER_CAPACITY;
int written;
written = snprintf(buf, remaining, "%u%s", encoder->cea_708_counter, encoder->encoded_crlf);
if (written > 0 && (size_t)written < remaining)
buf_len += written;
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
sprintf(buf, "%u%s", encoder->cea_708_counter, encoder->encoded_crlf);
print_mstime_buff(tv->time_ms_show + encoder->subs_delay,
"%02u:%02u:%02u,%03u", buf + buf_len);
buf_len = strlen(buf);
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
written = snprintf(buf + buf_len, remaining, " --> ");
if (written > 0 && (size_t)written < remaining)
buf_len += written;
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
"%02u:%02u:%02u,%03u", buf + strlen(buf));
sprintf(buf + strlen(buf), " --> ");
print_mstime_buff(tv->time_ms_hide + encoder->subs_delay,
"%02u:%02u:%02u,%03u", buf + buf_len);
buf_len = strlen(buf);
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
written = snprintf(buf + buf_len, remaining, "%s", (char *)encoder->encoded_crlf);
if (written > 0 && (size_t)written < remaining)
buf_len += written;
"%02u:%02u:%02u,%03u", buf + strlen(buf));
sprintf(buf + strlen(buf), "%s", (char *)encoder->encoded_crlf);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
@@ -302,47 +263,28 @@ void dtvcc_write_transcript(dtvcc_writer_ctx *writer, dtvcc_service_decoder *dec
return;
char *buf = (char *)encoder->buffer;
size_t buf_len;
size_t remaining;
int written;
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
if (!dtvcc_is_row_empty(tv, i))
{
buf[0] = 0;
buf_len = 0;
if (encoder->transcript_settings->showStartTime)
{
print_mstime_buff(tv->time_ms_show + encoder->subs_delay,
"%02u:%02u:%02u,%03u|", buf + buf_len);
buf_len = strlen(buf);
}
"%02u:%02u:%02u,%03u|", buf + strlen(buf));
if (encoder->transcript_settings->showEndTime)
{
print_mstime_buff(tv->time_ms_hide + encoder->subs_delay,
"%02u:%02u:%02u,%03u|", buf + buf_len);
buf_len = strlen(buf);
}
"%02u:%02u:%02u,%03u|", buf + strlen(buf));
if (encoder->transcript_settings->showCC)
{
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
written = snprintf(buf + buf_len, remaining, "CC1|"); // always CC1 because CEA-708 is field-independent
if (written > 0 && (size_t)written < remaining)
buf_len += written;
}
sprintf(buf + strlen(buf), "CC1|"); // always CC1 because CEA-708 is field-independent
if (encoder->transcript_settings->showMode)
{
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len;
written = snprintf(buf + buf_len, remaining, "POP|"); // TODO caption mode(pop, rollup, etc.)
if (written > 0 && (size_t)written < remaining)
buf_len += written;
}
sprintf(buf + strlen(buf), "POP|"); // TODO caption mode(pop, rollup, etc.)
const size_t buf_len = strlen(buf);
if (buf_len != 0)
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
@@ -358,33 +300,22 @@ 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);
size_t buf_len = 0;
size_t remaining = INITIAL_ENC_BUFFER_CAPACITY;
int written;
#define SAMI_SNPRINTF(fmt, ...) \
do \
{ \
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len; \
written = snprintf(buf + buf_len, remaining, fmt, ##__VA_ARGS__); \
if (written > 0 && (size_t)written < remaining) \
buf_len += written; \
} while (0)
SAMI_SNPRINTF("<sami>%s", encoder->encoded_crlf);
SAMI_SNPRINTF("<head>%s", encoder->encoded_crlf);
SAMI_SNPRINTF("<style type=\"text/css\">%s", encoder->encoded_crlf);
SAMI_SNPRINTF("<!--%s", encoder->encoded_crlf);
SAMI_SNPRINTF("p {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;%s",
encoder->encoded_crlf);
SAMI_SNPRINTF("text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}%s",
encoder->encoded_crlf);
SAMI_SNPRINTF(".unknowncc {Name:Unknown; lang:en-US; SAMIType:CC;}%s", encoder->encoded_crlf);
SAMI_SNPRINTF("-->%s", encoder->encoded_crlf);
SAMI_SNPRINTF("</style>%s", encoder->encoded_crlf);
SAMI_SNPRINTF("</head>%s%s", encoder->encoded_crlf, encoder->encoded_crlf);
SAMI_SNPRINTF("<body>%s", encoder->encoded_crlf);
#undef SAMI_SNPRINTF
buf_len += sprintf(buf + buf_len, "<sami>%s", encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, "<head>%s", encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, "<style type=\"text/css\">%s", encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, "<!--%s", encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len,
"p {margin-left: 16pt; margin-right: 16pt; margin-bottom: 16pt; margin-top: 16pt;%s",
encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len,
"text-align: center; font-size: 18pt; font-family: arial; font-weight: bold; color: #f0f0f0;}%s",
encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, ".unknowncc {Name:Unknown; lang:en-US; SAMIType:CC;}%s", encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, "-->%s", encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, "</style>%s", encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, "</head>%s%s", encoder->encoded_crlf, encoder->encoded_crlf);
buf_len += sprintf(buf + buf_len, "<body>%s", encoder->encoded_crlf);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, buf_len);
}
@@ -392,9 +323,8 @@ 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)
{
char *buf = (char *)encoder->buffer;
int written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "</body></sami>");
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
sprintf(buf, "</body></sami>");
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd,
encoder->encoded_crlf, encoder->encoded_crlf_length);
}
@@ -412,14 +342,12 @@ void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder,
dtvcc_write_sami_header(tv, encoder);
char *buf = (char *)encoder->buffer;
int written;
buf[0] = 0;
written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "<sync start=%llu><p class=\"unknowncc\">%s",
(unsigned long long)tv->time_ms_show + encoder->subs_delay,
encoder->encoded_crlf);
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
sprintf(buf, "<sync start=%llu><p class=\"unknowncc\">%s",
(unsigned long long)tv->time_ms_show + encoder->subs_delay,
encoder->encoded_crlf);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
for (int i = 0; i < CCX_DTVCC_SCREENGRID_ROWS; i++)
{
@@ -433,11 +361,10 @@ void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder,
}
}
written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "<sync start=%llu><p class=\"unknowncc\">&nbsp;</p></sync>%s%s",
(unsigned long long)tv->time_ms_hide + encoder->subs_delay,
encoder->encoded_crlf, encoder->encoded_crlf);
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
sprintf(buf, "<sync start=%llu><p class=\"unknowncc\">&nbsp;</p></sync>%s%s",
(unsigned long long)tv->time_ms_hide + encoder->subs_delay,
encoder->encoded_crlf, encoder->encoded_crlf);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
}
unsigned char adjust_odd_parity(const unsigned char value)
@@ -461,12 +388,11 @@ unsigned char adjust_odd_parity(const unsigned char value)
void dtvcc_write_scc_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder)
{
char *buf = (char *)encoder->buffer;
// 18 characters long + 2 new lines = 20 characters total
memset(buf, 0, INITIAL_ENC_BUFFER_CAPACITY);
int written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "Scenarist_SCC V1.0\n\n");
// 18 characters long + 2 new lines
memset(buf, 0, 20);
sprintf(buf, "Scenarist_SCC V1.0\n\n");
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, written);
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
}
int count_captions_lines_scc(dtvcc_tv_screen *tv)
@@ -489,31 +415,22 @@ int count_captions_lines_scc(dtvcc_tv_screen *tv)
* 2 line length subtitles can be placed in 14th and 15th row
* 3 line length subtitles can be placed in 13th, 14th and 15th row
*/
void add_needed_scc_labels(char *buf, size_t buf_size, size_t *buf_len, int total_subtitle_count, int current_subtitle_count)
void add_needed_scc_labels(char *buf, int total_subtitle_count, int current_subtitle_count)
{
size_t remaining = buf_size - *buf_len;
int written;
const char *label;
switch (total_subtitle_count)
{
case 1:
// row 15, column 00
label = " 94e0 94e0";
sprintf(buf + strlen(buf), " 94e0 94e0");
break;
case 2:
// 9440: row 14, column 00 | 94e0: row 15, column 00
label = (current_subtitle_count == 1) ? " 9440 9440" : " 94e0 94e0";
sprintf(buf + strlen(buf), current_subtitle_count == 1 ? " 9440 9440" : " 94e0 94e0");
break;
default:
// 13e0: row 13, column 04 | 9440: row 14, column 00 | 94e0: row 15, column 00
label = (current_subtitle_count == 1) ? " 13e0 13e0" : ((current_subtitle_count == 2) ? " 9440 9440" : " 94e0 94e0");
break;
sprintf(buf + strlen(buf), current_subtitle_count == 1 ? " 13e0 13e0" : (current_subtitle_count == 2 ? " 9440 9440" : " 94e0 94e0"));
}
written = snprintf(buf + *buf_len, remaining, "%s", label);
if (written > 0 && (size_t)written < remaining)
*buf_len += written;
}
void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder)
@@ -530,55 +447,38 @@ void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s
dtvcc_write_scc_header(tv, encoder);
char *buf = (char *)encoder->buffer;
size_t buf_len;
size_t remaining;
int written;
struct ccx_boundary_time time_show = get_time(tv->time_ms_show + encoder->subs_delay);
// when hiding subtract a frame (1 frame = 34 ms)
struct ccx_boundary_time time_end = get_time(tv->time_ms_hide + encoder->subs_delay - 34);
#define SCC_SNPRINTF(fmt, ...) \
do \
{ \
remaining = INITIAL_ENC_BUFFER_CAPACITY - buf_len; \
written = snprintf(buf + buf_len, remaining, fmt, ##__VA_ARGS__); \
if (written > 0 && (size_t)written < remaining) \
buf_len += written; \
} while (0)
if (tv->old_cc_time_end > time_show.time_in_ms)
{
// Correct the frame delay
time_show.time_in_ms -= 1000 / 29.97;
print_scc_time(time_show, buf);
buf_len = strlen(buf);
SCC_SNPRINTF("\t942c 942c");
sprintf(buf + strlen(buf), "\t942c 942c");
time_show.time_in_ms += 1000 / 29.97;
// Clear the buffer and start pop on caption
SCC_SNPRINTF("94ae 94ae 9420 9420");
sprintf(buf + strlen(buf), "94ae 94ae 9420 9420");
}
else if (tv->old_cc_time_end < time_show.time_in_ms)
{
// Clear the screen for new caption
struct ccx_boundary_time time_to_display = get_time(tv->old_cc_time_end);
print_scc_time(time_to_display, buf);
buf_len = strlen(buf);
SCC_SNPRINTF("\t942c 942c \n\n");
sprintf(buf + strlen(buf), "\t942c 942c \n\n");
// Correct the frame delay
time_show.time_in_ms -= 1000 / 29.97;
// Clear the buffer and start pop on caption in new time
print_scc_time(time_show, buf + buf_len);
buf_len = strlen(buf);
SCC_SNPRINTF("\t94ae 94ae 9420 9420");
print_scc_time(time_show, buf);
sprintf(buf + strlen(buf), "\t94ae 94ae 9420 9420");
time_show.time_in_ms += 1000 / 29.97;
}
else
{
time_show.time_in_ms -= 1000 / 29.97;
print_scc_time(time_show, buf);
buf_len = strlen(buf);
SCC_SNPRINTF("\t942c 942c 94ae 94ae 9420 9420");
sprintf(buf + strlen(buf), "\t942c 942c 94ae 94ae 9420 9420");
time_show.time_in_ms += 1000 / 29.97;
}
@@ -590,29 +490,27 @@ void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, s
if (!dtvcc_is_row_empty(tv, i))
{
current_subtitle_count++;
add_needed_scc_labels(buf, INITIAL_ENC_BUFFER_CAPACITY, &buf_len, total_subtitle_count, current_subtitle_count);
add_needed_scc_labels(buf, total_subtitle_count, current_subtitle_count);
int first, last, bytes_written = 0;
dtvcc_get_write_interval(tv, i, &first, &last);
for (int j = first; j <= last; j++)
{
if (bytes_written % 2 == 0)
SCC_SNPRINTF(" ");
SCC_SNPRINTF("%x", adjust_odd_parity(tv->chars[i][j].sym));
sprintf(buf + strlen(buf), " ");
sprintf(buf + strlen(buf), "%x", adjust_odd_parity(tv->chars[i][j].sym));
bytes_written += 1;
}
// if byte pair are not even then make it even by adding 0x80 as padding
if (bytes_written % 2 == 1)
SCC_SNPRINTF("80 ");
sprintf(buf + strlen(buf), "80 ");
else
SCC_SNPRINTF(" ");
sprintf(buf + strlen(buf), " ");
}
}
// Display caption (942f 942f)
SCC_SNPRINTF("942f 942f \n\n");
#undef SCC_SNPRINTF
sprintf(buf + strlen(buf), "942f 942f \n\n");
write_wrapped(encoder->dtvcc_writers[tv->service_number - 1].fd, buf, strlen(buf));
tv->old_cc_time_end = time_end.time_in_ms;
@@ -681,7 +579,7 @@ void dtvcc_writer_init(dtvcc_writer_ctx *writer,
const char *ext = get_file_extension(write_format);
char suffix[32];
snprintf(suffix, sizeof(suffix), CCX_DTVCC_FILENAME_TEMPLATE, program_number, service_number);
sprintf(suffix, CCX_DTVCC_FILENAME_TEMPLATE, program_number, service_number);
writer->filename = create_outfilename(base_filename, suffix, ext);
if (!writer->filename)

View File

@@ -31,7 +31,7 @@ void dtvcc_write_sami_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void dtvcc_write_sami_footer(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void dtvcc_write_sami(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
void dtvcc_write_scc_header(dtvcc_tv_screen *tv, struct encoder_ctx *encoder);
void add_needed_scc_labels(char *buf, size_t buf_size, size_t *buf_len, int total_subtitle_count, int current_subtitle_count);
void add_needed_scc_labels(char *buf, int total_subtitle_count, int current_subtitle_count);
void dtvcc_write_scc(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);
void dtvcc_write(dtvcc_writer_ctx *writer, dtvcc_service_decoder *decoder, struct encoder_ctx *encoder);

View File

@@ -21,22 +21,23 @@ extern void ccxr_flush_decoder(struct dtvcc_ctx *dtvcc, struct dtvcc_service_dec
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;
LLONG ccxr_get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field);
LLONG ccxr_get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field);
/* This function returns a FTS that is guaranteed to be at least 1 ms later than the end of the previous screen. It shouldn't be needed
obviously but it guarantees there's no timing overlap */
LLONG get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field)
{
LLONG fts = ccxr_get_visible_start(ctx, current_field);
LLONG fts = get_fts(ctx, current_field);
if (fts <= ctx->minimum_fts)
fts = ctx->minimum_fts + 1;
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible Start time=%s\n", print_mstime_static(fts));
return fts;
}
/* This function returns the current FTS and saves it so it can be used by get_visible_start */
/* This function returns the current FTS and saves it so it can be used by ctxget_visible_start */
LLONG get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field)
{
LLONG fts = ccxr_get_visible_end(ctx, current_field);
LLONG fts = get_fts(ctx, current_field);
if (fts > ctx->minimum_fts)
ctx->minimum_fts = fts;
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_608, "Visible End time=%s\n", print_mstime_static(fts));
return fts;
}
@@ -147,11 +148,7 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
else
writercwtdata(ctx, cc_block, sub);
}
// For container formats (H.264, MPEG-2 PES), don't increment cb_field
// because the frame PTS already represents the correct timestamp.
// The cb_field offset is only meaningful for raw/elementary streams.
if (ctx->in_bufferdatatype != CCX_H264 && ctx->in_bufferdatatype != CCX_PES)
cb_field1++;
cb_field1++;
break;
case 1:
dbg_print(CCX_DMT_CBRAW, " .. %s ..\n", debug_608_to_ASC(cc_block, 1));
@@ -175,9 +172,7 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
else
writercwtdata(ctx, cc_block, sub);
}
// For container formats, don't increment cb_field (see comment above)
if (ctx->in_bufferdatatype != CCX_H264 && ctx->in_bufferdatatype != CCX_PES)
cb_field2++;
cb_field2++;
break;
case 2: // EIA-708
// DTVCC packet data
@@ -202,9 +197,7 @@ int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle
if (ctx->write_format == CCX_OF_RCWT)
writercwtdata(ctx, cc_block, sub);
}
// For container formats, don't increment cb_708 (see comment above)
if (ctx->in_bufferdatatype != CCX_H264 && ctx->in_bufferdatatype != CCX_PES)
cb_708++;
cb_708++;
// Check for bytes read
// printf ("Warning: Losing EIA-708 data!\n");
break;
@@ -224,43 +217,13 @@ 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;
#ifndef DISABLE_RUST
ccxr_dtvcc_free(lctx->dtvcc_rust);
lctx->dtvcc_rust = NULL;
#else
dtvcc_free(&lctx->dtvcc);
#endif
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);
dinit_timing_ctx(&lctx->timing);
free_decoder_context(lctx->prev);
free_subtitle(lctx->dec_sub.prev);
/* Free the embedded dec_sub's data field (allocated by write_cc_buffer) */
if (lctx->dec_sub.datatype == CC_DATATYPE_DVB)
{
struct cc_bitmap *bitmap = (struct cc_bitmap *)lctx->dec_sub.data;
if (bitmap)
{
freep(&bitmap->data0);
freep(&bitmap->data1);
}
}
/* Free any leftover XDS strings that weren't processed by the encoder */
if (lctx->dec_sub.type == CC_608 && lctx->dec_sub.data)
{
struct eia608_screen *data = (struct eia608_screen *)lctx->dec_sub.data;
for (int i = 0; i < lctx->dec_sub.nb_data; i++, data++)
{
if (data->format == SFORMAT_XDS && data->xds_str)
{
freep(&data->xds_str);
}
}
}
freep(&lctx->dec_sub.data);
/* Note: xds_ctx is freed in general_loop.c, mp4.c etc. during normal processing.
Don't free it here as it may cause double-free if already freed elsewhere. */
freep(ctx);
}
@@ -270,27 +233,11 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
ctx = malloc(sizeof(struct lib_cc_decode));
if (!ctx)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory allocating ctx.");
// Initialize all pointers to NULL
ctx->avc_ctx = NULL;
ctx->timing = NULL;
ctx->dtvcc = NULL;
ctx->context_cc608_field_1 = NULL;
ctx->context_cc608_field_2 = NULL;
ctx->xds_ctx = NULL;
ctx->vbi_decoder = NULL;
ctx->prev = NULL;
memset(&ctx->dec_sub, 0, sizeof(ctx->dec_sub));
return NULL;
ctx->avc_ctx = init_avc();
if (!ctx->avc_ctx)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing avc_ctx.");
ctx->codec = setting->codec;
ctx->timing = init_timing_ctx(&ccx_common_timing_settings);
if (!ctx->timing)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing timing.");
setting->settings_dtvcc->timing = ctx->timing;
@@ -299,16 +246,8 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
ctx->no_rollup = setting->no_rollup;
ctx->noscte20 = setting->noscte20;
#ifndef DISABLE_RUST
ctx->dtvcc_rust = ccxr_dtvcc_init(setting->settings_dtvcc);
ctx->dtvcc = NULL; // Not used when Rust is enabled
#else
ctx->dtvcc = dtvcc_init(setting->settings_dtvcc);
if (!ctx->dtvcc)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing dtvcc.");
ctx->dtvcc->is_active = setting->settings_dtvcc->enabled;
ctx->dtvcc_rust = NULL;
#endif
if (setting->codec == CCX_CODEC_ATSC_CC)
{
@@ -321,8 +260,6 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
setting->cc_to_stdout,
setting->output_format,
ctx->timing);
if (!ctx->context_cc608_field_1)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing context_cc608_field_1.");
ctx->context_cc608_field_2 = ccx_decoder_608_init_library(
setting->settings_608,
setting->cc_channel,
@@ -331,8 +268,11 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
setting->cc_to_stdout,
setting->output_format,
ctx->timing);
if (!ctx->context_cc608_field_2)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing context_cc608_field_2.");
}
else
{
ctx->context_cc608_field_1 = NULL;
ctx->context_cc608_field_2 = NULL;
}
ctx->current_field = 1;
ctx->private_data = setting->private_data;
@@ -437,8 +377,6 @@ struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *sett
setting->xds_write_to_file = 0;
}
ctx->xds_ctx = ccx_decoders_xds_init_library(ctx->timing, setting->xds_write_to_file);
if (!ctx->xds_ctx)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In init_cc_decode: Out of memory initializing xds_ctx.");
ctx->vbi_decoder = NULL;
ctx->ocr_quantmode = setting->ocr_quantmode;
@@ -488,13 +426,6 @@ void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub)
}
}
}
#ifndef DISABLE_RUST
if (ccxr_dtvcc_is_active(ctx->dtvcc_rust))
{
ctx->current_field = 3;
ccxr_flush_active_decoders(ctx->dtvcc_rust);
}
#else
if (ctx->dtvcc->is_active)
{
for (int i = 0; i < CCX_DTVCC_MAX_SERVICES; i++)
@@ -509,86 +440,52 @@ void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub)
}
}
}
#endif
}
struct encoder_ctx *copy_encoder_context(struct encoder_ctx *ctx)
{
struct encoder_ctx *ctx_copy = NULL;
ctx_copy = malloc(sizeof(struct encoder_ctx));
if (!ctx_copy)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating ctx_copy.");
memcpy(ctx_copy, ctx, sizeof(struct encoder_ctx));
// Initialize copied pointers to NULL before re-allocating
ctx_copy->buffer = NULL;
ctx_copy->first_input_file = NULL;
ctx_copy->out = NULL;
ctx_copy->timing = NULL;
ctx_copy->transcript_settings = NULL;
ctx_copy->subline = NULL;
ctx_copy->start_credits_text = NULL;
ctx_copy->end_credits_text = NULL;
ctx_copy->prev = NULL;
ctx_copy->last_string = NULL;
if (ctx->buffer)
{
ctx_copy->buffer = malloc(ctx->capacity * sizeof(unsigned char));
if (!ctx_copy->buffer)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating buffer.");
memcpy(ctx_copy->buffer, ctx->buffer, ctx->capacity * sizeof(unsigned char));
}
if (ctx->first_input_file)
{
size_t len = strlen(ctx->first_input_file) + 1;
ctx_copy->first_input_file = malloc(len);
if (!ctx_copy->first_input_file)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating first_input_file.");
memcpy(ctx_copy->first_input_file, ctx->first_input_file, len);
ctx_copy->first_input_file = malloc(strlen(ctx->first_input_file) * sizeof(char));
memcpy(ctx_copy->first_input_file, ctx->first_input_file, strlen(ctx->first_input_file) * sizeof(char));
}
if (ctx->out)
{
ctx_copy->out = malloc(sizeof(struct ccx_s_write));
if (!ctx_copy->out)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating out.");
memcpy(ctx_copy->out, ctx->out, sizeof(struct ccx_s_write));
}
if (ctx->timing)
{
ctx_copy->timing = malloc(sizeof(struct ccx_common_timing_ctx));
if (!ctx_copy->timing)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating timing.");
memcpy(ctx_copy->timing, ctx->timing, sizeof(struct ccx_common_timing_ctx));
}
if (ctx->transcript_settings)
{
ctx_copy->transcript_settings = malloc(sizeof(struct ccx_encoders_transcript_format));
if (!ctx_copy->transcript_settings)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating transcript_settings.");
memcpy(ctx_copy->transcript_settings, ctx->transcript_settings, sizeof(struct ccx_encoders_transcript_format));
}
if (ctx->subline)
{
ctx_copy->subline = malloc(SUBLINESIZE);
if (!ctx_copy->subline)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating subline.");
memcpy(ctx_copy->subline, ctx->subline, SUBLINESIZE);
}
if (ctx->start_credits_text)
{
size_t len = strlen(ctx->start_credits_text) + 1;
ctx_copy->start_credits_text = malloc(len);
if (!ctx_copy->start_credits_text)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating start_credits_text.");
memcpy(ctx_copy->start_credits_text, ctx->start_credits_text, len);
ctx_copy->start_credits_text = malloc(strlen(ctx->start_credits_text) * sizeof(char));
memcpy(ctx_copy->start_credits_text, ctx->start_credits_text, (strlen(ctx->start_credits_text) + 1) * sizeof(char));
}
if (ctx->end_credits_text)
{
size_t len = strlen(ctx->end_credits_text) + 1;
ctx_copy->end_credits_text = malloc(len);
if (!ctx_copy->end_credits_text)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_encoder_context: Out of memory allocating end_credits_text.");
memcpy(ctx_copy->end_credits_text, ctx->end_credits_text, len);
ctx_copy->end_credits_text = malloc(strlen(ctx->end_credits_text) * sizeof(char));
memcpy(ctx_copy->end_credits_text, ctx->end_credits_text, (strlen(ctx->end_credits_text) + 1) * sizeof(char));
}
return ctx_copy;
}
@@ -596,67 +493,42 @@ struct lib_cc_decode *copy_decoder_context(struct lib_cc_decode *ctx)
{
struct lib_cc_decode *ctx_copy = NULL;
ctx_copy = malloc(sizeof(struct lib_cc_decode));
if (!ctx_copy)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating ctx_copy.");
memcpy(ctx_copy, ctx, sizeof(struct lib_cc_decode));
// Initialize copied pointers to NULL before re-allocating
ctx_copy->context_cc608_field_1 = NULL;
ctx_copy->context_cc608_field_2 = NULL;
ctx_copy->timing = NULL;
ctx_copy->avc_ctx = NULL;
ctx_copy->private_data = NULL;
ctx_copy->dtvcc = NULL;
ctx_copy->xds_ctx = NULL;
ctx_copy->vbi_decoder = NULL;
if (ctx->context_cc608_field_1)
{
ctx_copy->context_cc608_field_1 = malloc(sizeof(struct ccx_decoder_608_context));
if (!ctx_copy->context_cc608_field_1)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating context_cc608_field_1.");
memcpy(ctx_copy->context_cc608_field_1, ctx->context_cc608_field_1, sizeof(struct ccx_decoder_608_context));
}
if (ctx->context_cc608_field_2)
{
ctx_copy->context_cc608_field_2 = malloc(sizeof(struct ccx_decoder_608_context));
if (!ctx_copy->context_cc608_field_2)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating context_cc608_field_2.");
memcpy(ctx_copy->context_cc608_field_2, ctx->context_cc608_field_2, sizeof(struct ccx_decoder_608_context));
}
if (ctx->timing)
{
ctx_copy->timing = malloc(sizeof(struct ccx_common_timing_ctx));
if (!ctx_copy->timing)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating timing.");
memcpy(ctx_copy->timing, ctx->timing, sizeof(struct ccx_common_timing_ctx));
}
if (ctx->avc_ctx)
{
ctx_copy->avc_ctx = malloc(sizeof(struct avc_ctx));
if (!ctx_copy->avc_ctx)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating avc_ctx.");
memcpy(ctx_copy->avc_ctx, ctx->avc_ctx, sizeof(struct avc_ctx));
}
ctx_copy->private_data = NULL;
if (ctx->dtvcc)
{
ctx_copy->dtvcc = malloc(sizeof(struct dtvcc_ctx));
if (!ctx_copy->dtvcc)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating dtvcc.");
memcpy(ctx_copy->dtvcc, ctx->dtvcc, sizeof(struct dtvcc_ctx));
}
if (ctx->xds_ctx)
{
ctx_copy->xds_ctx = malloc(sizeof(struct ccx_decoders_xds_context));
if (!ctx_copy->xds_ctx)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating xds_ctx.");
memcpy(ctx_copy->xds_ctx, ctx->xds_ctx, sizeof(struct ccx_decoders_xds_context));
}
if (ctx->vbi_decoder)
{
ctx_copy->vbi_decoder = malloc(sizeof(struct ccx_decoder_vbi_ctx));
if (!ctx_copy->vbi_decoder)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_decoder_context: Out of memory allocating vbi_decoder.");
memcpy(ctx_copy->vbi_decoder, ctx->vbi_decoder, sizeof(struct ccx_decoder_vbi_ctx));
}
return ctx_copy;
@@ -665,17 +537,12 @@ struct cc_subtitle *copy_subtitle(struct cc_subtitle *sub)
{
struct cc_subtitle *sub_copy = NULL;
sub_copy = malloc(sizeof(struct cc_subtitle));
if (!sub_copy)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_subtitle: Out of memory allocating sub_copy.");
memcpy(sub_copy, sub, sizeof(struct cc_subtitle));
sub_copy->datatype = sub->datatype;
sub_copy->data = NULL;
if (sub->data)
{
sub_copy->data = malloc(sub->nb_data * sizeof(struct eia608_screen));
if (!sub_copy->data)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In copy_subtitle: Out of memory allocating data.");
memcpy(sub_copy->data, sub->data, sub->nb_data * sizeof(struct eia608_screen));
}
return sub_copy;

View File

@@ -11,31 +11,25 @@
extern uint64_t utc_refvalue; // UTC referential value
// Declarations
LLONG get_visible_start(struct ccx_common_timing_ctx *ctx, int current_field);
LLONG get_visible_end(struct ccx_common_timing_ctx *ctx, int current_field);
LLONG get_visible_start (struct ccx_common_timing_ctx *ctx, int current_field);
LLONG get_visible_end (struct ccx_common_timing_ctx *ctx, int current_field);
unsigned int get_decoder_str_basic(unsigned char *buffer, unsigned char *line, int trim_subs, enum ccx_encoding_type encoding);
void ccx_decoders_common_settings_init(LLONG subs_delay, enum ccx_output_format output_format);
int validate_cc_data_pair(unsigned char *cc_data_pair);
int process_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, struct cc_subtitle *sub);
int do_cb(struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle *sub);
void printdata(struct lib_cc_decode *ctx, const unsigned char *data1, int length1,
const unsigned char *data2, int length2, struct cc_subtitle *sub);
struct lib_cc_decode *init_cc_decode(struct ccx_decoders_common_settings_t *setting);
int validate_cc_data_pair (unsigned char *cc_data_pair);
int process_cc_data (struct encoder_ctx *enc_ctx, struct lib_cc_decode *ctx, unsigned char *cc_data, int cc_count, struct cc_subtitle *sub);
int do_cb (struct lib_cc_decode *ctx, unsigned char *cc_block, struct cc_subtitle *sub);
void printdata (struct lib_cc_decode *ctx, const unsigned char *data1, int length1,
const unsigned char *data2, int length2, struct cc_subtitle *sub);
struct lib_cc_decode* init_cc_decode (struct ccx_decoders_common_settings_t *setting);
void dinit_cc_decode(struct lib_cc_decode **ctx);
void flush_cc_decode(struct lib_cc_decode *ctx, struct cc_subtitle *sub);
struct encoder_ctx *copy_encoder_context(struct encoder_ctx *ctx);
struct lib_cc_decode *copy_decoder_context(struct lib_cc_decode *ctx);
struct cc_subtitle *copy_subtitle(struct cc_subtitle *sub);
struct encoder_ctx* copy_encoder_context(struct encoder_ctx *ctx);
struct lib_cc_decode* copy_decoder_context(struct lib_cc_decode *ctx);
struct cc_subtitle* copy_subtitle(struct cc_subtitle *sub);
void free_encoder_context(struct encoder_ctx *ctx);
void free_decoder_context(struct lib_cc_decode *ctx);
void free_subtitle(struct cc_subtitle *sub);
#ifndef DISABLE_RUST
// Rust FFI function to flush active CEA-708 service decoders
extern void ccxr_flush_active_decoders(void *dtvcc_rust);
#endif
void free_subtitle(struct cc_subtitle* sub);
#endif

View File

@@ -345,11 +345,6 @@ static struct ISDBText *allocate_text_node(ISDBSubLayout *ls)
text->used = 0;
text->buf = malloc(128);
if (!text->buf)
{
free(text);
return NULL;
}
text->len = 128;
*text->buf = 0;
return text;
@@ -724,17 +719,16 @@ static int parse_csi(ISDBSubContext *ctx, const uint8_t *buf, int len)
// Copy buf in arg
for (i = 0; *buf != 0x20; i++)
{
if (i >= sizeof(arg) - 1)
if (i >= (sizeof(arg)) + 1)
{
isdb_log("UnExpected CSI: too long");
isdb_log("UnExpected CSI %d >= %d", sizeof(arg) + 1, i);
break;
}
arg[i] = *buf;
buf++;
}
/* ignore terminating 0x20 character */
if (i < sizeof(arg))
arg[i] = *buf++;
arg[i] = *buf++;
switch (*buf)
{

View File

@@ -9,4 +9,5 @@ int isdbsub_decode(struct lib_cc_decode *dec_ctx, const uint8_t *buf, size_t buf
void delete_isdb_decoder(void **isdb_ctx);
void *init_isdb_decoder(void);
#endif

View File

@@ -11,10 +11,11 @@
#define CCX_DECODER_608_SCREEN_ROWS 15
#define CCX_DECODER_608_SCREEN_WIDTH 32
#define MAXBFRAMES 50
#define SORTBUF (2 * MAXBFRAMES + 1)
#define SORTBUF (2*MAXBFRAMES+1)
/* flag raised when end of display marker arrives in Dvb Subtitle */
#define SUB_EOD_MARKER (1 << 0)
#define SUB_EOD_MARKER (1 << 0 )
struct cc_bitmap
{
int x;
@@ -76,13 +77,13 @@ enum ccx_decoder_608_color_code
};
/**
* This structure have fields which need to be ignored according to format,
* for example if format is SFORMAT_XDS then all fields other then
* xds related (xds_str, xds_len and cur_xds_packet_class) should be
* ignored and not to be dereferenced.
*
* TODO use union inside struct for each kind of fields
*/
* This structure have fields which need to be ignored according to format,
* for example if format is SFORMAT_XDS then all fields other then
* xds related (xds_str, xds_len and cur_xds_packet_class) should be
* ignored and not to be dereferenced.
*
* TODO use union inside struct for each kind of fields
*/
struct eia608_screen // A CC buffer
{
/** format of data inside this structure */
@@ -90,8 +91,8 @@ struct eia608_screen // A CC buffer
unsigned char characters[CCX_DECODER_608_SCREEN_ROWS][CCX_DECODER_608_SCREEN_WIDTH + 1];
enum ccx_decoder_608_color_code colors[CCX_DECODER_608_SCREEN_ROWS][CCX_DECODER_608_SCREEN_WIDTH + 1];
enum font_bits fonts[CCX_DECODER_608_SCREEN_ROWS][CCX_DECODER_608_SCREEN_WIDTH + 1]; // Extra char at the end for a 0
int row_used[CCX_DECODER_608_SCREEN_ROWS]; // Any data in row?
int empty; // Buffer completely empty?
int row_used[CCX_DECODER_608_SCREEN_ROWS]; // Any data in row?
int empty; // Buffer completely empty?
/** start time of this CC buffer */
LLONG start_time;
/** end time of this CC buffer */
@@ -109,20 +110,20 @@ struct eia608_screen // A CC buffer
struct ccx_decoders_common_settings_t
{
LLONG subs_delay; // ms to delay (or advance) subs
enum ccx_output_format output_format; // What kind of output format should be used?
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
LLONG subs_delay; // ms to delay (or advance) subs
enum ccx_output_format output_format; // What kind of output format should be used?
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
int cc_to_stdout;
int extract; // Extract 1st, 2nd or both fields
int fullbin; // Disable pruning of padding cc blocks
int extract; // Extract 1st, 2nd or both fields
int fullbin; // Disable pruning of padding cc blocks
int no_rollup;
int noscte20;
struct ccx_decoder_608_settings *settings_608; // Contains the settings for the 608 decoder.
ccx_decoder_dtvcc_settings *settings_dtvcc; // Same for cea 708 captions decoder (dtvcc)
int cc_channel; // Channel we want to dump in srt mode
struct ccx_decoder_608_settings *settings_608; // Contains the settings for the 608 decoder.
ccx_decoder_dtvcc_settings *settings_dtvcc; // Same for cea 708 captions decoder (dtvcc)
int cc_channel; // Channel we want to dump in srt mode
unsigned send_to_srv;
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
int program_number;
enum ccx_code_type codec;
int xds_write_to_file;
@@ -141,17 +142,17 @@ struct lib_cc_decode
void *context_cc608_field_1;
void *context_cc608_field_2;
int no_rollup; // If 1, write one line at a time
int no_rollup; // If 1, write one line at a time
int noscte20;
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
enum ccx_output_format write_format; // 0 = Raw, 1 = srt, 2 = SMI
int fix_padding; // Replace 0000 with 8080 in HDTV (needed for some cards)
enum ccx_output_format write_format; // 0 = Raw, 1 = srt, 2 = SMI
struct ccx_boundary_time extraction_start, extraction_end; // Segment we actually process
LLONG subs_delay; // ms to delay (or advance) subs
int extract; // Extract 1st, 2nd or both fields
int fullbin; // Disable pruning of padding cc blocks
LLONG subs_delay; // ms to delay (or advance) subs
int extract; // Extract 1st, 2nd or both fields
int fullbin; // Disable pruning of padding cc blocks
struct cc_subtitle dec_sub;
enum ccx_bufferdata_type in_bufferdatatype;
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
unsigned int hauppauge_mode; // If 1, use PID=1003, process specially and so on
int frames_since_last_gop;
/* GOP-based timing */
@@ -184,7 +185,7 @@ struct lib_cc_decode
int in_pic_data;
unsigned int current_progressive_sequence;
unsigned int current_pulldownfields;
unsigned int current_pulldownfields ;
int temporal_reference;
enum ccx_frame_type picture_coding_type;
@@ -196,19 +197,18 @@ struct lib_cc_decode
/* Required in es_function.c and es_userdata.c */
unsigned top_field_first; // Needs to be global
/* Stats. Modified in es_userdata.c*/
int stat_numuserheaders;
int stat_dvdccheaders;
int stat_scte20ccheaders;
int stat_replay5000headers;
int stat_replay4000headers;
int stat_dishheaders;
int stat_hdtv;
int stat_divicom;
int false_pict_header;
/* Stats. Modified in es_userdata.c*/
int stat_numuserheaders;
int stat_dvdccheaders;
int stat_scte20ccheaders;
int stat_replay5000headers;
int stat_replay4000headers;
int stat_dishheaders;
int stat_hdtv;
int stat_divicom;
int false_pict_header;
dtvcc_ctx *dtvcc;
void *dtvcc_rust; // Persistent Rust CEA-708 decoder context
int current_field;
// Analyse/use the picture information
int maxtref; // Use to remember the temporal reference number
@@ -217,7 +217,7 @@ struct lib_cc_decode
// Store fts;
LLONG cc_fts[SORTBUF];
// Store HD CC packets
unsigned char cc_data_pkts[SORTBUF][10 * 31 * 3 + 1]; // *10, because MP4 seems to have different limits
unsigned char cc_data_pkts[SORTBUF][10*31*3+1]; // *10, because MP4 seems to have different limits
// The sequence number of the current anchor frame. All currently read
// B-Frames belong to this I- or P-frame.
@@ -227,7 +227,7 @@ struct lib_cc_decode
int (*writedata)(const unsigned char *data, int length, void *private_data, struct cc_subtitle *sub);
// dvb subtitle related
//dvb subtitle related
int ocr_quantmode;
struct lib_cc_decode *prev;
};

View File

@@ -18,10 +18,11 @@ struct ccx_decoder_vbi_ctx
{
int vbi_decoder_inited;
vbi_raw_decoder zvbi_decoder;
// vbi3_raw_decoder zvbi_decoder;
//vbi3_raw_decoder zvbi_decoder;
#ifdef VBI_DEBUG
FILE *vbi_debug_dump;
#endif
};
#endif

View File

@@ -175,8 +175,6 @@ void xdsprint(struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx, con
if (n > -1 && n < size)
{
write_xds_string(sub, ctx, p, n);
/* Note: Don't free(p) here - the pointer is stored in data->xds_str
and will be freed by the encoder or decoder cleanup code */
return;
}
/* Else try again with more space. */
@@ -349,9 +347,9 @@ void xds_do_copy_generation_management_system(struct cc_subtitle *sub, struct cc
const char *copytext[4] = {"Copy permitted (no restrictions)", "No more copies (one generation copy has been made)",
"One generation of copies can be made", "No copying is permitted"};
const char *apstext[4] = {"No APS", "PSP On; Split Burst Off", "PSP On; 2 line Split Burst On", "PSP On; 4 line Split Burst On"};
snprintf(copy_permited, sizeof(copy_permited), "CGMS: %s", copytext[cgms_a_b4 * 2 + cgms_a_b3]);
snprintf(aps, sizeof(aps), "APS: %s", apstext[aps_b2 * 2 + aps_b1]);
snprintf(rcd, sizeof(rcd), "Redistribution Control Descriptor: %d", rcd0);
sprintf(copy_permited, "CGMS: %s", copytext[cgms_a_b4 * 2 + cgms_a_b3]);
sprintf(aps, "APS: %s", apstext[aps_b2 * 2 + aps_b1]);
sprintf(rcd, "Redistribution Control Descriptor: %d", rcd0);
}
xdsprint(sub, ctx, copy_permited);
@@ -409,45 +407,30 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
const char *agetext[8] = {"None", "TV-Y (All Children)", "TV-Y7 (Older Children)",
"TV-G (General Audience)", "TV-PG (Parental Guidance Suggested)",
"TV-14 (Parents Strongly Cautioned)", "TV-MA (Mature Audience Only)", "None"};
snprintf(age, sizeof(age), "ContentAdvisory: US TV Parental Guidelines. Age Rating: %s", agetext[g2 * 4 + g1 * 2 + g0]);
sprintf(age, "ContentAdvisory: US TV Parental Guidelines. Age Rating: %s", agetext[g2 * 4 + g1 * 2 + g0]);
content[0] = 0;
size_t content_len = 0;
if (!g2 && g1 && !g0) // For TV-Y7 (Older children), the Violence bit is "fantasy violence"
{
if (FV)
{
snprintf(content, sizeof(content), "[Fantasy Violence] ");
content_len = strlen(content);
}
strcpy(content, "[Fantasy Violence] ");
}
else // For all others, is real
{
if (FV)
{
snprintf(content, sizeof(content), "[Violence] ");
content_len = strlen(content);
}
strcpy(content, "[Violence] ");
}
if (S)
{
snprintf(content + content_len, sizeof(content) - content_len, "[Sexual Situations] ");
content_len = strlen(content);
}
strcat(content, "[Sexual Situations] ");
if (La3)
{
snprintf(content + content_len, sizeof(content) - content_len, "[Adult Language] ");
content_len = strlen(content);
}
strcat(content, "[Adult Language] ");
if (Da2)
{
snprintf(content + content_len, sizeof(content) - content_len, "[Sexually Suggestive Dialog] ");
}
strcat(content, "[Sexually Suggestive Dialog] ");
supported = 1;
}
if (!a0) // MPA
{
const char *ratingtext[8] = {"N/A", "G", "PG", "PG-13", "R", "NC-17", "X", "Not Rated"};
snprintf(rating, sizeof(rating), "ContentAdvisory: MPA Rating: %s", ratingtext[r2 * 4 + r1 * 2 + r0]);
sprintf(rating, "ContentAdvisory: MPA Rating: %s", ratingtext[r2 * 4 + r1 * 2 + r0]);
supported = 1;
}
if (a0 && a1 && !Da2 && !La3) // Canadian English Language Rating
@@ -455,7 +438,7 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
const char *ratingtext[8] = {"Exempt", "Children", "Children eight years and older",
"General programming suitable for all audiences", "Parental Guidance",
"Viewers 14 years and older", "Adult Programming", "[undefined]"};
snprintf(rating, sizeof(rating), "ContentAdvisory: Canadian English Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
sprintf(rating, "ContentAdvisory: Canadian English Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
supported = 1;
}
if (a0 && a1 && Da2 && !La3) // Canadian French Language Rating
@@ -464,7 +447,7 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
"Cette ?mission peut ne pas convenir aux enfants de moins de 13 ans",
"Cette ?mission ne convient pas aux moins de 16 ans",
"Cette ?mission est r?serv?e aux adultes", "[invalid]", "[invalid]"};
snprintf(rating, sizeof(rating), "ContentAdvisory: Canadian French Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
sprintf(rating, "ContentAdvisory: Canadian French Rating: %s", ratingtext[g2 * 4 + g1 * 2 + g0]);
supported = 1;
}
}
@@ -472,17 +455,14 @@ void xds_do_content_advisory(struct cc_subtitle *sub, struct ccx_decoders_xds_co
if (!a1 && a0) // US TV parental guidelines
{
xdsprint(sub, ctx, age);
if (content[0]) // Only output content if not empty
xdsprint(sub, ctx, content);
xdsprint(sub, ctx, content);
if (changed)
{
ccx_common_logging.log_ftn("\rXDS: %s\n ", age);
if (content[0])
ccx_common_logging.log_ftn("\rXDS: %s\n ", content);
ccx_common_logging.log_ftn("\rXDS: %s\n ", content);
}
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n", age);
if (content[0])
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n", content);
ccx_common_logging.debug_ftn(CCX_DMT_DECODER_XDS, "\rXDS: %s\n", content);
}
if (!a0 || // MPA
(a0 && a1 && !Da2 && !La3) || // Canadian English Language Rating
@@ -504,10 +484,6 @@ int xds_do_current_and_future(struct cc_subtitle *sub, struct ccx_decoders_xds_c
int was_proc = 0;
char *str = malloc(1024);
if (!str)
{
return CCX_ENOMEM;
}
char *tstr = NULL;
int str_len = 1024;
@@ -736,8 +712,7 @@ int xds_do_current_and_future(struct cc_subtitle *sub, struct ccx_decoders_xds_c
if (changed)
{
ccx_common_logging.log_ftn("\rXDS description line %d: %s\n", line_num, xds_desc);
strncpy(ctx->xds_program_description[line_num], xds_desc, 32);
ctx->xds_program_description[line_num][32] = '\0';
strcpy(ctx->xds_program_description[line_num], xds_desc);
}
else
{
@@ -774,8 +749,7 @@ int xds_do_channel(struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx
if (strcmp(xds_network_name, ctx->current_xds_network_name)) // Change of station
{
ccx_common_logging.log_ftn("XDS Notice: Network is now %s\n", xds_network_name);
strncpy(ctx->current_xds_network_name, xds_network_name, 32);
ctx->current_xds_network_name[32] = '\0';
strcpy(ctx->current_xds_network_name, xds_network_name);
}
break;
case XDS_TYPE_CALL_LETTERS_AND_CHANNEL:
@@ -826,19 +800,12 @@ int xds_do_private_data(struct cc_subtitle *sub, struct ccx_decoders_xds_context
if (!ctx)
return CCX_EINVAL;
size_t str_size = (ctx->cur_xds_payload_length * 3) + 1;
str = malloc(str_size);
str = malloc((ctx->cur_xds_payload_length * 3) + 1);
if (str == NULL) // Only thing we can do with private data is dump it.
return 1;
str[0] = '\0';
size_t offset = 0;
for (i = 2; i < ctx->cur_xds_payload_length - 1; i++)
{
int written = snprintf(str + offset, str_size - offset, "%02X ", ctx->cur_xds_payload[i]);
if (written > 0)
offset += written;
}
sprintf(str, "%02X ", ctx->cur_xds_payload[i]);
xdsprint(sub, ctx, str);
free(str);

View File

@@ -4,11 +4,11 @@
#include "ccx_decoders_common.h"
#define NUM_BYTES_PER_PACKET 35 // Class + type (repeated for convenience) + data + zero
#define NUM_XDS_BUFFERS 9 // CEA recommends no more than one level of interleaving. Play it safe
#define NUM_XDS_BUFFERS 9 // CEA recommends no more than one level of interleaving. Play it safe
struct ccx_decoders_xds_context;
void process_xds_bytes(struct ccx_decoders_xds_context *ctx, const unsigned char hi, int lo);
void do_end_of_xds(struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx, unsigned char expected_checksum);
void process_xds_bytes (struct ccx_decoders_xds_context *ctx, const unsigned char hi, int lo);
void do_end_of_xds (struct cc_subtitle *sub, struct ccx_decoders_xds_context *ctx, unsigned char expected_checksum);
struct ccx_decoders_xds_context *ccx_decoders_xds_init_library(struct ccx_common_timing_ctx *timing, int xds_write_to_file);

View File

@@ -3,20 +3,9 @@
#include "lib_ccx.h"
#include "utility.h"
#include "ffmpeg_intgr.h"
#ifndef DISABLE_RUST
void ccxr_demuxer_reset(struct ccx_demuxer *ctx);
void ccxr_demuxer_close(struct ccx_demuxer *ctx);
int ccxr_demuxer_isopen(const struct ccx_demuxer *ctx);
int ccxr_demuxer_open(struct ccx_demuxer *ctx, const char *file);
LLONG ccxr_demuxer_get_file_size(struct ccx_demuxer *ctx);
void ccxr_demuxer_print_cfg(const struct ccx_demuxer *ctx);
#endif
static void ccx_demuxer_reset(struct ccx_demuxer *ctx)
{
#ifndef DISABLE_RUST
ccxr_demuxer_reset(ctx);
#else
ctx->startbytes_pos = 0;
ctx->startbytes_avail = 0;
ctx->num_of_PIDs = 0;
@@ -28,14 +17,10 @@ static void ccx_demuxer_reset(struct ccx_demuxer *ctx)
}
memset(ctx->stream_id_of_each_pid, 0, (MAX_PSI_PID + 1) * sizeof(uint8_t));
memset(ctx->PIDs_programs, 0, 65536 * sizeof(struct PMT_entry *));
#endif
}
static void ccx_demuxer_close(struct ccx_demuxer *ctx)
{
#ifndef DISABLE_RUST
ccxr_demuxer_close(ctx);
#else
ctx->past = 0;
if (ctx->infd != -1 && ccx_options.input_source == CCX_DS_FILE)
{
@@ -43,23 +28,14 @@ static void ccx_demuxer_close(struct ccx_demuxer *ctx)
ctx->infd = -1;
activity_input_file_closed();
}
#endif
}
static int ccx_demuxer_isopen(struct ccx_demuxer *ctx)
{
#ifndef DISABLE_RUST
return ccxr_demuxer_isopen(ctx);
#else
return ctx->infd != -1;
#endif
}
static int ccx_demuxer_open(struct ccx_demuxer *ctx, const char *file)
{
#ifndef DISABLE_RUST
return ccxr_demuxer_open(ctx, file);
#else
ctx->past = 0;
ctx->min_global_timestamp = 0;
ctx->global_timestamp_inited = 0;
@@ -217,14 +193,9 @@ static int ccx_demuxer_open(struct ccx_demuxer *ctx, const char *file)
}
return 0;
#endif
}
LLONG ccx_demuxer_get_file_size(struct ccx_demuxer *ctx)
{
#ifndef DISABLE_RUST
return ccxr_demuxer_get_file_size(ctx);
#else
LLONG ret = 0;
int in = ctx->infd;
LLONG current = LSEEK(in, 0, SEEK_CUR);
@@ -237,7 +208,6 @@ LLONG ccx_demuxer_get_file_size(struct ccx_demuxer *ctx)
return -1;
return length;
#endif
}
static int ccx_demuxer_get_stream_mode(struct ccx_demuxer *ctx)
@@ -247,9 +217,6 @@ static int ccx_demuxer_get_stream_mode(struct ccx_demuxer *ctx)
static void ccx_demuxer_print_cfg(struct ccx_demuxer *ctx)
{
#ifndef DISABLE_RUST
ccxr_demuxer_print_cfg(ctx);
#else
switch (ctx->auto_stream)
{
case CCX_SM_ELEMENTARY_OR_NOT_FOUND:
@@ -285,9 +252,6 @@ static void ccx_demuxer_print_cfg(struct ccx_demuxer *ctx)
case CCX_SM_MXF:
mprint("MXF");
break;
case CCX_SM_SCC:
mprint("SCC");
break;
#ifdef WTV_DEBUG
case CCX_SM_HEX_DUMP:
mprint("Hex");
@@ -297,7 +261,6 @@ static void ccx_demuxer_print_cfg(struct ccx_demuxer *ctx)
fatal(CCX_COMMON_EXIT_BUG_BUG, "BUG: Unknown stream mode. Please file a bug report on Github.\n");
break;
}
#endif
}
void ccx_demuxer_delete(struct ccx_demuxer **ctx)
@@ -329,7 +292,7 @@ void ccx_demuxer_delete(struct ccx_demuxer **ctx)
struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg)
{
int i;
struct ccx_demuxer *ctx = calloc(1, sizeof(struct ccx_demuxer));
struct ccx_demuxer *ctx = malloc(sizeof(struct ccx_demuxer));
if (!ctx)
return NULL;
@@ -351,6 +314,7 @@ struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg)
{
ctx->pinfo[i].got_important_streams_min_pts[j] = UINT64_MAX;
}
ctx->pinfo[i].initialized_ocr = 0;
ctx->pinfo[i].version = 0xFF; // Not real in a real stream since it's 5 bits. FF => Not initialized
}
@@ -443,4 +407,4 @@ struct demuxer_data *alloc_demuxer_data(void)
data->next_stream = 0;
data->next_program = 0;
return data;
}
}

View File

@@ -25,21 +25,22 @@ enum STREAM_TYPE
};
struct ccx_demux_report
{
unsigned program_cnt;
unsigned dvb_sub_pid[SUB_STREAMS_CNT];
unsigned tlt_sub_pid[SUB_STREAMS_CNT];
unsigned mp4_cc_track_cnt;
unsigned program_cnt;
unsigned dvb_sub_pid[SUB_STREAMS_CNT];
unsigned tlt_sub_pid[SUB_STREAMS_CNT];
unsigned mp4_cc_track_cnt;
};
struct program_info
{
int pid;
int program_number;
uint8_t analysed_PMT_once : 1;
int initialized_ocr; // Avoid initializing the OCR more than once
uint8_t analysed_PMT_once:1;
uint8_t version;
uint8_t saved_section[1021];
int32_t crc;
uint8_t valid_crc : 1;
uint8_t valid_crc:1;
char name[MAX_PROGRAM_NAME_LEN];
/**
* -1 pid represent that pcr_pid is not available
@@ -47,7 +48,6 @@ struct program_info
int16_t pcr_pid;
uint64_t got_important_streams_min_pts[COUNT];
int has_all_min_pts;
char virtual_channel[16]; // Stores ATSC virtual channel like "2.1"
};
struct cap_info
@@ -56,9 +56,9 @@ struct cap_info
int program_number;
enum ccx_stream_type stream;
enum ccx_code_type codec;
int64_t capbufsize;
long capbufsize;
unsigned char *capbuf;
int64_t capbuflen; // Bytes read in capbuf
long capbuflen; // Bytes read in capbuf
int saw_pesstart;
int prev_counter;
void *codec_private_data;
@@ -77,6 +77,7 @@ struct cap_info
List joining all sibling Stream in Program
*/
struct list_head pg_stream;
};
struct ccx_demuxer
{
@@ -96,6 +97,7 @@ struct ccx_demuxer
int flag_ts_forced_cappid;
int ts_datastreamtype;
struct program_info pinfo[MAX_PROGRAM];
int nb_program;
/* subtitle codec type */
@@ -117,10 +119,10 @@ struct ccx_demuxer
struct PSI_buffer *PID_buffers[MAX_PSI_PID];
int PIDs_seen[MAX_PID];
/*51 possible stream ids in total, 0xbd is private stream, 0xbe is padding stream,
0xbf private stream 2, 0xc0 - 0xdf audio, 0xe0 - 0xef video
/*51 possible stream ids in total, 0xbd is private stream, 0xbe is padding stream,
0xbf private stream 2, 0xc0 - 0xdf audio, 0xe0 - 0xef video
(stream ids range from 0xbd to 0xef so 0xef - 0xbd + 1 = 51)*/
// uint8_t found_stream_ids[MAX_NUM_OF_STREAMIDS];
//uint8_t found_stream_ids[MAX_NUM_OF_STREAMIDS];
uint8_t stream_id_of_each_pid[MAX_PSI_PID + 1];
uint64_t min_pts[MAX_PSI_PID + 1];
@@ -139,7 +141,7 @@ struct ccx_demuxer
unsigned last_pat_length;
unsigned char *filebuffer;
LLONG filebuffer_start; // Position of buffer start relative to file
LLONG filebuffer_start; // Position of buffer start relative to file
unsigned int filebuffer_pos; // Position of pointer relative to buffer start
unsigned int bytesinbuffer; // Number of bytes we actually have on buffer
@@ -154,7 +156,7 @@ struct ccx_demuxer
void *parent;
// Will contain actual Demuxer Context
//Will contain actual Demuxer Context
void *private_data;
void (*print_cfg)(struct ccx_demuxer *ctx);
void (*reset)(struct ccx_demuxer *ctx);
@@ -162,7 +164,7 @@ struct ccx_demuxer
int (*open)(struct ccx_demuxer *ctx, const char *file_name);
int (*is_open)(struct ccx_demuxer *ctx);
int (*get_stream_mode)(struct ccx_demuxer *ctx);
LLONG (*get_filesize)(struct ccx_demuxer *ctx);
LLONG (*get_filesize) (struct ccx_demuxer *ctx);
};
struct demuxer_data
@@ -180,21 +182,21 @@ struct demuxer_data
struct demuxer_data *next_program;
};
struct cap_info *get_sib_stream_by_type(struct cap_info *program, enum ccx_code_type type);
struct cap_info *get_sib_stream_by_type(struct cap_info* program, enum ccx_code_type type);
struct ccx_demuxer *init_demuxer(void *parent, struct demuxer_cfg *cfg);
void ccx_demuxer_delete(struct ccx_demuxer **ctx);
struct demuxer_data *alloc_demuxer_data(void);
struct demuxer_data* alloc_demuxer_data(void);
void delete_demuxer_data(struct demuxer_data *data);
int update_capinfo(struct ccx_demuxer *ctx, int pid, enum ccx_stream_type stream, enum ccx_code_type codec, int pn, void *private_data);
struct cap_info *get_cinfo(struct ccx_demuxer *ctx, int pid);
struct cap_info * get_cinfo(struct ccx_demuxer *ctx, int pid);
int need_cap_info(struct ccx_demuxer *ctx, int program_number);
int need_cap_info_for_pid(struct ccx_demuxer *ctx, int pid);
struct demuxer_data *get_best_data(struct demuxer_data *data);
struct demuxer_data *get_data_stream(struct demuxer_data *data, int pid);
int get_best_stream(struct ccx_demuxer *ctx);
void ignore_other_stream(struct ccx_demuxer *ctx, int pid);
void dinit_cap(struct ccx_demuxer *ctx);
void dinit_cap (struct ccx_demuxer *ctx);
int get_programme_number(struct ccx_demuxer *ctx, int pid);
struct cap_info *get_best_sib_stream(struct cap_info *program);
void ignore_other_sib_stream(struct cap_info *head, int pid);
struct cap_info* get_best_sib_stream(struct cap_info* program);
void ignore_other_sib_stream(struct cap_info* head, int pid);
#endif

View File

@@ -14,6 +14,13 @@
#define IS_KLV_KEY(x, y) (!memcmp(x, y, sizeof(y)))
#define IS_KLV_KEY_ANY_VERSION(x, y) (!memcmp(x, y, 7) && !memcmp(x + 8, y + 8, sizeof(y) - 8))
enum MXFCaptionType
{
MXF_CT_VBI,
MXF_CT_ANC,
};
typedef uint8_t UID[16];
typedef struct KLVPacket
{
UID key;
@@ -28,12 +35,29 @@ typedef struct MXFCodecUL
typedef int ReadFunc(struct ccx_demuxer *ctx, uint64_t size);
typedef struct
{
int track_id;
uint8_t track_number[4];
} MXFTrack;
typedef struct MXFReadTableEntry
{
const UID key;
ReadFunc *read;
} MXFReadTableEntry;
typedef struct MXFContext
{
enum MXFCaptionType type;
int cap_track_id;
UID cap_essence_key;
MXFTrack tracks[32];
int nb_tracks;
int cap_count;
struct ccx_rational edit_rate;
} MXFContext;
typedef struct MXFLocalTAGS
{
uint16_t tag;
@@ -75,15 +99,12 @@ enum MXFLocalTag
void update_tid_lut(struct MXFContext *ctx, uint32_t track_id, uint8_t *track_number, struct ccx_rational edit_rate)
{
int i;
debug("update_tid_lut: track_id=%u (0x%x), track_number=%02X%02X%02X%02X, cap_track_id=%u\n",
track_id, track_id, track_number[0], track_number[1], track_number[2], track_number[3], ctx->cap_track_id);
// Update essence element key if we have track Id of caption
if (ctx->cap_track_id == track_id)
{
memcpy(ctx->cap_essence_key, mxf_essence_element_key, 12);
memcpy(ctx->cap_essence_key + 12, track_number, 4);
ctx->edit_rate = edit_rate;
debug("MXF: Found caption track, track_id=%u\n", track_id);
}
for (i = 0; i < ctx->nb_tracks; i++)
@@ -251,7 +272,6 @@ static int mxf_read_vanc_vbi_desc(struct ccx_demuxer *demux, uint64_t size)
{
case MXF_TAG_LTRACK_ID:
ctx->cap_track_id = buffered_get_be32(demux);
debug("MXF: VANC/VBI descriptor found, Linked Track ID = %u\n", ctx->cap_track_id);
update_cap_essence_key(ctx, ctx->cap_track_id);
break;
default:
@@ -308,17 +328,6 @@ static int mxf_read_cdp_data(struct ccx_demuxer *demux, int size, struct demuxer
log("Incomplete CDP packet\n");
ret = buffered_read(demux, data->buffer + data->len, cc_count * 3);
// Log first few bytes of cc_data for debugging
if (cc_count > 0)
{
unsigned char *cc_ptr = data->buffer + data->len;
debug("cc_data (first 6 triplets): ");
for (int j = 0; j < (cc_count < 6 ? cc_count : 6); j++)
{
debug("%02X%02X%02X ", cc_ptr[j * 3], cc_ptr[j * 3 + 1], cc_ptr[j * 3 + 2]);
}
debug("\n");
}
data->len += cc_count * 3;
demux->past += cc_count * 3;
len += ret;
@@ -376,10 +385,7 @@ static int mxf_read_vanc_data(struct ccx_demuxer *demux, uint64_t size, struct d
// uint8_t count; /* Currently unused */
if (size < 19)
{
debug("VANC data too small: %" PRIu64 " < 19\n", size);
goto error;
}
ret = buffered_read(demux, vanc_header, 16);
@@ -388,39 +394,31 @@ static int mxf_read_vanc_data(struct ccx_demuxer *demux, uint64_t size, struct d
return CCX_EOF;
len += ret;
debug("VANC header: num_packets=%d, line=0x%02x%02x, wrap_type=0x%02x, sample_config=0x%02x\n",
vanc_header[1], vanc_header[2], vanc_header[3], vanc_header[4], vanc_header[5]);
for (int i = 0; i < vanc_header[1]; i++)
{
DID = buffered_get_byte(demux);
len++;
debug("VANC packet %d: DID=0x%02x\n", i, DID);
if (!(DID == 0x61 || DID == 0x80))
{
debug("DID 0x%02x not recognized as caption DID\n", DID);
goto error;
}
SDID = buffered_get_byte(demux);
len++;
debug("VANC packet %d: SDID=0x%02x\n", i, SDID);
if (SDID == 0x01)
debug("Caption Type 708\n");
else if (SDID == 0x02)
debug("Caption Type 608\n");
cdp_size = buffered_get_byte(demux);
debug("VANC packet %d: cdp_size=%d\n", i, cdp_size);
if (cdp_size + 19 > size)
{
log("Incomplete cdp(%d) in anc data(%" PRIu64 ")\n", cdp_size, size);
debug("Incomplete cdp(%d) in anc data(%d)\n", cdp_size, size);
goto error;
}
len++;
ret = mxf_read_cdp_data(demux, cdp_size, data);
debug("mxf_read_cdp_data returned %d, data->len=%d\n", ret, data->len);
len += ret;
// len += (3 + count + 4);
}
@@ -437,33 +435,15 @@ static int mxf_read_essence_element(struct ccx_demuxer *demux, uint64_t size, st
int ret;
struct MXFContext *ctx = demux->private_data;
debug("mxf_read_essence_element: ctx->type=%d (ANC=%d, VBI=%d), size=%" PRIu64 "\n",
ctx->type, MXF_CT_ANC, MXF_CT_VBI, size);
if (ctx->type == MXF_CT_ANC)
{
data->bufferdatatype = CCX_RAW_TYPE;
ret = mxf_read_vanc_data(demux, size, data);
debug("mxf_read_vanc_data returned %d, data->len=%d\n", ret, data->len);
// Calculate PTS in 90kHz units from frame count and edit rate
// edit_rate is frames per second (e.g., 25/1 for 25fps)
// PTS = frame_count * 90000 / fps = frame_count * 90000 * edit_rate.den / edit_rate.num
if (ctx->edit_rate.num > 0 && ctx->edit_rate.den > 0)
{
data->pts = (int64_t)ctx->cap_count * 90000 * ctx->edit_rate.den / ctx->edit_rate.num;
}
else
{
// Fallback to 25fps if edit_rate not set
data->pts = (int64_t)ctx->cap_count * 90000 / 25;
}
debug("Frame %d, PTS=%" PRId64 " (edit_rate=%d/%d)\n",
ctx->cap_count, data->pts, ctx->edit_rate.num, ctx->edit_rate.den);
data->pts = ctx->cap_count;
ctx->cap_count++;
}
else
{
debug("Skipping essence element (not ANC type)\n");
ret = buffered_skip(demux, size);
demux->past += ret;
}
@@ -558,7 +538,6 @@ static int read_packet(struct ccx_demuxer *demux, struct demuxer_data *data)
KLVPacket klv;
const MXFReadTableEntry *reader;
struct MXFContext *ctx = demux->private_data;
static int first_essence_logged = 0;
while ((ret = klv_read_packet(&klv, demux)) == 0)
{
debug("Key %02X%02X%02X%02X%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X%02X%02X size %" PRIu64 "\n",
@@ -568,25 +547,8 @@ static int read_packet(struct ccx_demuxer *demux, struct demuxer_data *data)
klv.key[12], klv.key[13], klv.key[14], klv.key[15],
klv.length);
// Check if this is an essence element key (first 12 bytes match)
if (IS_KLV_KEY(klv.key, mxf_essence_element_key) && !first_essence_logged)
{
debug("MXF: First essence element key: %02X%02X%02X%02X%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X%02X%02X\n",
klv.key[0], klv.key[1], klv.key[2], klv.key[3],
klv.key[4], klv.key[5], klv.key[6], klv.key[7],
klv.key[8], klv.key[9], klv.key[10], klv.key[11],
klv.key[12], klv.key[13], klv.key[14], klv.key[15]);
debug("MXF: cap_essence_key: %02X%02X%02X%02X%02X%02X%02X%02X.%02X%02X%02X%02X%02X%02X%02X%02X\n",
ctx->cap_essence_key[0], ctx->cap_essence_key[1], ctx->cap_essence_key[2], ctx->cap_essence_key[3],
ctx->cap_essence_key[4], ctx->cap_essence_key[5], ctx->cap_essence_key[6], ctx->cap_essence_key[7],
ctx->cap_essence_key[8], ctx->cap_essence_key[9], ctx->cap_essence_key[10], ctx->cap_essence_key[11],
ctx->cap_essence_key[12], ctx->cap_essence_key[13], ctx->cap_essence_key[14], ctx->cap_essence_key[15]);
first_essence_logged = 1;
}
if (IS_KLV_KEY(klv.key, ctx->cap_essence_key))
{
debug("MXF: Found ANC essence element, size=%" PRIu64 "\n", klv.length);
mxf_read_essence_element(demux, klv.length, data);
if (data->len > 0)
break;
@@ -628,15 +590,8 @@ int ccx_mxf_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
data->program_number = 1;
data->stream_pid = 1;
data->codec = CCX_CODEC_ATSC_CC;
// PTS is already calculated in 90kHz units by mxf_read_essence_element
data->tb.num = 1;
data->tb.den = 90000;
// Enable CEA-708 (DTVCC) decoder for MXF files with VANC captions
if (ctx->dec_global_setting && ctx->dec_global_setting->settings_dtvcc)
{
ctx->dec_global_setting->settings_dtvcc->enabled = 1;
}
data->tb.num = 1001;
data->tb.den = 30000;
}
else
{
@@ -645,11 +600,6 @@ int ccx_mxf_getmoredata(struct lib_ccx_ctx *ctx, struct demuxer_data **ppdata)
ret = read_packet(ctx->demux_ctx, data);
// Ensure timebase is 90kHz since PTS is calculated in 90kHz units
// CDP parsing may have set a frame-based timebase which would cause incorrect conversion
data->tb.num = 1;
data->tb.den = 90000;
return ret;
}

View File

@@ -3,31 +3,6 @@
#include "ccx_demuxer.h"
typedef uint8_t UID[16];
enum MXFCaptionType
{
MXF_CT_VBI,
MXF_CT_ANC,
};
typedef struct
{
int track_id;
uint8_t track_number[4];
} MXFTrack;
typedef struct MXFContext
{
enum MXFCaptionType type;
int cap_track_id;
UID cap_essence_key;
MXFTrack tracks[32];
int nb_tracks;
int cap_count;
struct ccx_rational edit_rate;
} MXFContext;
int ccx_probe_mxf(struct ccx_demuxer *ctx);
struct MXFContext *ccx_mxf_init(struct ccx_demuxer *demux);
#endif

View File

@@ -25,7 +25,7 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
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 + 2 > CCX_DTVCC_MAX_PACKET_LENGTH)
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");
@@ -51,7 +51,7 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
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 + 2 > CCX_DTVCC_MAX_PACKET_LENGTH)
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");
@@ -115,10 +115,10 @@ dtvcc_ctx *dtvcc_init(struct ccx_decoder_dtvcc_settings *opts)
dtvcc_service_decoder *decoder = &ctx->decoders[i];
decoder->cc_count = 0;
decoder->tv = (dtvcc_tv_screen *)malloc(sizeof(dtvcc_tv_screen));
if (!decoder->tv)
ccx_common_logging.fatal_ftn(EXIT_NOT_ENOUGH_MEMORY, "dtvcc_init");
decoder->tv->service_number = i + 1;
decoder->tv->cc_count = 0;
if (!decoder->tv)
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;

View File

@@ -10,14 +10,4 @@ void dtvcc_process_data(struct dtvcc_ctx *dtvcc,
dtvcc_ctx *dtvcc_init(ccx_decoder_dtvcc_settings *opts);
void dtvcc_free(dtvcc_ctx **);
#ifndef DISABLE_RUST
// Rust FFI functions for persistent CEA-708 decoder
extern void *ccxr_dtvcc_init(struct ccx_decoder_dtvcc_settings *settings_dtvcc);
extern void ccxr_dtvcc_free(void *dtvcc_rust);
extern void ccxr_dtvcc_process_data(void *dtvcc_rust, const unsigned char cc_valid,
const unsigned char cc_type, const unsigned char data1, const unsigned char data2);
extern int ccxr_dtvcc_is_active(void *dtvcc_rust);
extern void ccxr_dtvcc_set_active(void *dtvcc_rust, int active);
#endif
#endif // CCEXTRACTOR_CCX_DTVCC_H
#endif //CCEXTRACTOR_CCX_DTVCC_H

View File

@@ -30,7 +30,7 @@ ccx_encoders_transcript_format ccx_encoders_default_transcript_settings =
.useColors = 1,
.isFinal = 0};
// TODO sami header doesn't care about CRLF/LF option
// TODO sami header doesn't carry about CRLF/LF option
static const char *sami_header = // TODO: Revise the <!-- comments
"<SAMI>\n\
<HEAD>\n\
@@ -131,7 +131,7 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
switch (ctx->write_format)
{
case CCX_OF_SAMI:
snprintf((char *)str, sizeof(str), "</BODY></SAMI>\n");
sprintf((char *)str, "</BODY></SAMI>\n");
if (ctx->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -144,7 +144,7 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
}
break;
case CCX_OF_SMPTETT:
snprintf((char *)str, sizeof(str), " </div>\n </body>\n</tt>\n");
sprintf((char *)str, " </div>\n </body>\n</tt>\n");
if (ctx->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -160,7 +160,7 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
write_spumux_footer(out);
break;
case CCX_OF_SIMPLE_XML:
snprintf((char *)str, sizeof(str), "</captions>\n");
sprintf((char *)str, "</captions>\n");
if (ctx->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -176,14 +176,6 @@ int write_subtitle_file_footer(struct encoder_ctx *ctx, struct ccx_s_write *out)
case CCX_OF_CCD:
ret = write(out->fh, ctx->encoded_crlf, ctx->encoded_crlf_length);
break;
case CCX_OF_WEBVTT:
// Ensure WebVTT header is written even if no subtitles were found (issue #1743)
// This is required for HLS compatibility
if (!ctx->wrote_webvtt_header)
{
write_webvtt_header(ctx);
}
break;
default: // Nothing to do, no footer on this format
break;
}
@@ -201,7 +193,7 @@ static int write_bom(struct encoder_ctx *ctx, struct ccx_s_write *out)
ret = write(out->fh, UTF8_BOM, sizeof(UTF8_BOM));
if (ret < sizeof(UTF8_BOM))
{
mprint("WARNING: Unable to write UTF BOM\n");
mprint("WARNING: Unable tp write UTF BOM\n");
return -1;
}
}
@@ -675,13 +667,8 @@ static int init_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg)
if (cfg->cc_to_stdout)
{
#ifdef WIN32
ctx->dtvcc_writers[i].fd = -1;
ctx->dtvcc_writers[i].fhandle = GetStdHandle(STD_OUTPUT_HANDLE);
#else
ctx->dtvcc_writers[i].fd = STDOUT_FILENO;
ctx->dtvcc_writers[i].fhandle = NULL;
#endif
ctx->dtvcc_writers[i].charset = NULL;
ctx->dtvcc_writers[i].filename = NULL;
ctx->dtvcc_writers[i].cd = (iconv_t)-1;
@@ -727,9 +714,6 @@ void dinit_encoder(struct encoder_ctx **arg, LLONG current_fts)
write_subtitle_file_footer(ctx, ctx->out + i);
}
// Clean up teletext multi-page output files (issue #665)
dinit_teletext_outputs(ctx);
free_encoder_context(ctx->prev);
dinit_output_ctx(ctx);
freep(&ctx->subline);
@@ -783,7 +767,6 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt)
return NULL;
}
ctx->in_fileformat = opt->in_format;
ctx->is_pal = (opt->in_format == 2);
/** used in case of SUB_EOD_MARKER */
ctx->prev_start = -1;
@@ -849,19 +832,6 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt)
ctx->segment_pending = 0;
ctx->segment_last_key_frame = 0;
ctx->nospupngocr = opt->nospupngocr;
ctx->scc_framerate = opt->scc_framerate;
ctx->scc_accurate_timing = opt->scc_accurate_timing;
ctx->scc_last_transmission_end = 0;
ctx->scc_last_display_end = 0;
// Initialize teletext multi-page output arrays (issue #665)
ctx->tlt_out_count = 0;
for (int i = 0; i < MAX_TLT_PAGES_EXTRACT; i++)
{
ctx->tlt_out[i] = NULL;
ctx->tlt_out_pages[i] = 0;
ctx->tlt_srt_counter[i] = 0;
}
ctx->prev = NULL;
return ctx;
@@ -897,30 +867,8 @@ int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
int wrote_something = 0;
int ret = 0;
/* If there is no encoder context (e.g. -out=report), we must still free
any allocated subtitle data to avoid memory leaks. */
if (!context)
{
if (sub)
{
/* DVB subtitles store bitmap planes inside cc_bitmap */
if (sub->datatype == CC_DATATYPE_DVB)
{
struct cc_bitmap *bitmap = (struct cc_bitmap *)sub->data;
if (bitmap)
{
freep(&bitmap->data0);
freep(&bitmap->data1);
}
}
/* Free generic subtitle payload buffer */
freep(&sub->data);
sub->nb_data = 0;
}
return CCX_OK;
}
context = change_filename(context);
@@ -954,11 +902,6 @@ int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
// After adding delay, if start/end time is lower than 0, then continue with the next subtitle
if (data->start_time < 0 || data->end_time <= 0)
{
// Free XDS string if skipping to avoid memory leak
if (data->format == SFORMAT_XDS && data->xds_str)
{
freep(&data->xds_str);
}
continue;
}
@@ -1058,28 +1001,6 @@ int encode_sub(struct encoder_ctx *context, struct cc_subtitle *sub)
freep(&sub->data);
break;
case CC_BITMAP:;
// Apply subs_delay to bitmap subtitles (DVB, DVD, etc.)
// This is the same as what's done for CC_608 above
sub->start_time += context->subs_delay;
sub->end_time += context->subs_delay;
// After adding delay, if start/end time is lower than 0, skip this subtitle
if (sub->start_time < 0 || sub->end_time <= 0)
{
// Free bitmap data to avoid memory leak
if (sub->datatype == CC_DATATYPE_DVB)
{
struct cc_bitmap *bitmap_tmp = (struct cc_bitmap *)sub->data;
if (bitmap_tmp)
{
freep(&bitmap_tmp->data0);
freep(&bitmap_tmp->data1);
}
}
freep(&sub->data);
sub->nb_data = 0;
break;
}
#ifdef ENABLE_OCR
struct cc_bitmap *rect;
@@ -1260,7 +1181,7 @@ unsigned int get_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer, in
{
unsigned char *orig = buffer; // Keep for debugging
unsigned char *line = data->characters[line_num];
for (int i = 0; i < 32; i++)
for (int i = 0; i < 33; i++)
{
int bytes = 0;
switch (ctx->encoding)
@@ -1294,6 +1215,7 @@ unsigned int get_color_encoded(struct encoder_ctx *ctx, unsigned char *buffer, i
else
*buffer++ = 'E';
}
*buffer = 0;
return (unsigned)(buffer - orig); // Return length
}
unsigned int get_font_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data)
@@ -1330,7 +1252,7 @@ void switch_output_file(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, in
}
const char *ext = get_file_extension(ctx->write_format);
char suffix[32];
snprintf(suffix, sizeof(suffix), "_%d", track_id);
sprintf(suffix, "_%d", track_id);
char *basename = get_basename(enc_ctx->out->original_filename);
if (basename != NULL)
{
@@ -1345,168 +1267,3 @@ void switch_output_file(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, in
enc_ctx->cea_708_counter = 0;
enc_ctx->srt_counter = 0;
}
/**
* Get or create the output file for a specific teletext page (issue #665)
* Creates output files on-demand with suffix _pNNN (e.g., output_p891.srt)
* Returns NULL if we're in stdout mode or if too many pages are being extracted
*/
struct ccx_s_write *get_teletext_output(struct encoder_ctx *ctx, uint16_t teletext_page)
{
// If teletext_page is 0, use the default output
if (teletext_page == 0 || ctx->out == NULL)
return ctx->out;
// Check if we're sending to stdout - can't do multi-page in that case
if (ctx->out[0].fh == STDOUT_FILENO)
return ctx->out;
// Check if we already have an output file for this page
for (int i = 0; i < ctx->tlt_out_count; i++)
{
if (ctx->tlt_out_pages[i] == teletext_page)
return ctx->tlt_out[i];
}
// If we only have one teletext page requested, use the default output
// (no suffix needed for backward compatibility)
extern struct ccx_s_teletext_config tlt_config;
if (tlt_config.num_user_pages <= 1 && !tlt_config.extract_all_pages)
return ctx->out;
// Need to create a new output file for this page
if (ctx->tlt_out_count >= MAX_TLT_PAGES_EXTRACT)
{
mprint("Warning: Too many teletext pages to extract (max %d), using default output for page %03d\n",
MAX_TLT_PAGES_EXTRACT, teletext_page);
return ctx->out;
}
// Allocate the new write structure
struct ccx_s_write *new_out = (struct ccx_s_write *)malloc(sizeof(struct ccx_s_write));
if (!new_out)
{
mprint("Error: Memory allocation failed for teletext output\n");
return ctx->out;
}
memset(new_out, 0, sizeof(struct ccx_s_write));
// Create the filename with page suffix
const char *ext = get_file_extension(ctx->write_format);
char suffix[16];
snprintf(suffix, sizeof(suffix), "_p%03d", teletext_page);
char *basefilename = NULL;
if (ctx->out[0].filename != NULL)
{
basefilename = get_basename(ctx->out[0].filename);
}
else if (ctx->first_input_file != NULL)
{
basefilename = get_basename(ctx->first_input_file);
}
else
{
basefilename = strdup("untitled");
}
if (basefilename == NULL)
{
free(new_out);
return ctx->out;
}
char *filename = create_outfilename(basefilename, suffix, ext);
free(basefilename);
if (filename == NULL)
{
free(new_out);
return ctx->out;
}
// Open the file
new_out->filename = filename;
new_out->fh = open(filename, O_RDWR | O_CREAT | O_TRUNC | O_BINARY, S_IREAD | S_IWRITE);
if (new_out->fh == -1)
{
mprint("Error: Failed to open output file %s: %s\n", filename, strerror(errno));
free(filename);
free(new_out);
return ctx->out;
}
mprint("Creating teletext output file: %s\n", filename);
// Store in our array
int idx = ctx->tlt_out_count;
ctx->tlt_out[idx] = new_out;
ctx->tlt_out_pages[idx] = teletext_page;
ctx->tlt_srt_counter[idx] = 0;
ctx->tlt_out_count++;
// Write the subtitle file header
write_subtitle_file_header(ctx, new_out);
return new_out;
}
/**
* Get the SRT counter for a specific teletext page (issue #665)
* Returns pointer to the counter, or NULL if page not found
*/
unsigned int *get_teletext_srt_counter(struct encoder_ctx *ctx, uint16_t teletext_page)
{
// If teletext_page is 0, use the default counter
if (teletext_page == 0)
return &ctx->srt_counter;
// Check if we're using multi-page mode
extern struct ccx_s_teletext_config tlt_config;
if (tlt_config.num_user_pages <= 1 && !tlt_config.extract_all_pages)
return &ctx->srt_counter;
// Find the counter for this page
for (int i = 0; i < ctx->tlt_out_count; i++)
{
if (ctx->tlt_out_pages[i] == teletext_page)
return &ctx->tlt_srt_counter[i];
}
// Not found, use default counter
return &ctx->srt_counter;
}
/**
* Clean up all teletext output files (issue #665)
*/
void dinit_teletext_outputs(struct encoder_ctx *ctx)
{
if (!ctx)
return;
for (int i = 0; i < ctx->tlt_out_count; i++)
{
if (ctx->tlt_out[i] != NULL)
{
// Write footer
write_subtitle_file_footer(ctx, ctx->tlt_out[i]);
// Close file
if (ctx->tlt_out[i]->fh != -1)
{
close(ctx->tlt_out[i]->fh);
}
// Free filename
if (ctx->tlt_out[i]->filename != NULL)
{
free(ctx->tlt_out[i]->filename);
}
free(ctx->tlt_out[i]);
ctx->tlt_out[i] = NULL;
}
}
ctx->tlt_out_count = 0;
}

View File

@@ -2,13 +2,13 @@
#define _CC_ENCODER_COMMON_H
#ifdef WIN32
#if defined(__MINGW64__) || defined(__MINGW32__)
#include <iconv.h>
#if defined(__MINGW64__) || defined(__MINGW32__)
#include <iconv.h>
#else
#include "..\\thirdparty\\win_iconv\\iconv.h"
#endif
#else
#include "..\\thirdparty\\win_iconv\\iconv.h"
#endif
#else
#include "iconv.h"
#include "iconv.h"
#endif
#include "ccx_common_structs.h"
@@ -16,25 +16,14 @@
#include "ccx_encoders_structs.h"
#include "ccx_common_option.h"
// Maximum number of teletext pages to extract simultaneously (issue #665)
#ifndef MAX_TLT_PAGES_EXTRACT
#define MAX_TLT_PAGES_EXTRACT 8
#endif
#define REQUEST_BUFFER_CAPACITY(ctx, length) \
if (length > ctx->capacity) \
{ \
ctx->capacity = length * 2; \
ctx->buffer = (unsigned char *)realloc(ctx->buffer, ctx->capacity); \
if (ctx->buffer == NULL) \
{ \
fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for reallocating buffer, bailing out\n"); \
} \
}
#define REQUEST_BUFFER_CAPACITY(ctx,length) if (length>ctx->capacity) \
{ctx->capacity = length * 2; ctx->buffer = (unsigned char*)realloc(ctx->buffer, ctx->capacity); \
if (ctx->buffer == NULL) { fatal(EXIT_NOT_ENOUGH_MEMORY, "Not enough memory for reallocating buffer, bailing out\n"); } \
}
// CC page dimensions
#define ROWS 15
#define COLUMNS 32
#define ROWS 15
#define COLUMNS 32
typedef struct dtvcc_writer_ctx
{
@@ -57,11 +46,11 @@ typedef struct ccx_sbs_utf8_character
struct ccx_mcc_caption_time
{
unsigned int hour;
unsigned int minute;
unsigned int second;
unsigned int millisecond;
unsigned int frame;
unsigned int hour;
unsigned int minute;
unsigned int second;
unsigned int millisecond;
unsigned int frame;
};
/**
@@ -95,7 +84,7 @@ struct encoder_ctx
/* number of member in array of write out array */
int nb_out;
/* Input file format used in Teletext for exceptional output */
unsigned int in_fileformat; // 1 = Normal, 2 = Teletext
unsigned int in_fileformat; //1 = Normal, 2 = Teletext
/* Keep output file closed when not actually writing to it and start over each time (add headers, etc) */
unsigned int keep_output_closed;
/* Force a flush on the file buffer whenever content is written */
@@ -107,22 +96,22 @@ struct encoder_ctx
/* Flag saying BOM to be written in each output file */
enum ccx_encoding_type encoding;
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
enum ccx_output_format write_format; // 0=Raw, 1=srt, 2=SMI
int generates_file;
struct ccx_encoders_transcript_format *transcript_settings; // Keeps the settings for generating transcript output files.
int no_bom;
int sentence_cap; // FIX CASE? = Fix case?
int sentence_cap; // FIX CASE? = Fix case?
int filter_profanity;
int trim_subs; // " Remove spaces at sides? "
int autodash; // Add dashes (-) before each speaker automatically?
int trim_subs; // " Remove spaces at sides? "
int autodash; // Add dashes (-) before each speaker automatically?
int no_font_color;
int no_type_setting;
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
unsigned char *subline; // Temp storage for storing each line
int gui_mode_reports; // If 1, output in stderr progress updates so the GUI can grab them
unsigned char *subline; // Temp storage for storing each line
int extract;
int dtvcc_extract; // 1 or 0 depending if we have to handle dtvcc
int dtvcc_extract; // 1 or 0 depending if we have to handle dtvcc
dtvcc_writer_ctx dtvcc_writers[CCX_DTVCC_MAX_SERVICES];
/* Timing related variables*/
@@ -137,7 +126,7 @@ struct encoder_ctx
int startcredits_displayed;
char *start_credits_text;
char *end_credits_text;
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
struct ccx_boundary_time startcreditsnotbefore, startcreditsnotafter; // Where to insert start credits, if possible
struct ccx_boundary_time startcreditsforatleast, startcreditsforatmost; // How long to display them?
struct ccx_boundary_time endcreditsforatleast, endcreditsforatmost;
@@ -149,17 +138,9 @@ struct encoder_ctx
// MCC File
int header_printed_flag;
struct ccx_mcc_caption_time next_caption_time;
unsigned int cdp_hdr_seq;
int force_dropframe;
// SCC output framerate
int scc_framerate; // SCC output framerate: 0=29.97 (default), 1=24, 2=25, 3=30
// SCC accurate timing (issue #1120)
int scc_accurate_timing; // If 1, use bandwidth-aware timing for broadcast compliance
LLONG scc_last_transmission_end; // When last caption transmission ends (ms)
LLONG scc_last_display_end; // When last caption display ends (ms)
struct ccx_mcc_caption_time next_caption_time;
unsigned int cdp_hdr_seq;
int force_dropframe;
int new_sentence; // Capitalize next letter?
@@ -169,12 +150,12 @@ struct encoder_ctx
/* split-by-sentence stuff */
int sbs_enabled;
// for dvb subs
struct encoder_ctx *prev;
//for dvb subs
struct encoder_ctx* prev;
int write_previous;
// for dvb in .mkv
int is_mkv; // are we working with .mkv file
char *last_string; // last recognized DVB sub
//for dvb in .mkv
int is_mkv; //are we working with .mkv file
char* last_string; //last recognized DVB sub
// Segmenting
int segment_pending;
@@ -182,15 +163,9 @@ struct encoder_ctx
// OCR in SPUPNG
int nospupngocr;
int is_pal;
struct ccx_s_write *tlt_out[MAX_TLT_PAGES_EXTRACT]; // Output files per teletext page
uint16_t tlt_out_pages[MAX_TLT_PAGES_EXTRACT]; // Page numbers for each output slot
unsigned int tlt_srt_counter[MAX_TLT_PAGES_EXTRACT]; // SRT counter per page
int tlt_out_count; // Number of teletext output files
};
#define INITIAL_ENC_BUFFER_CAPACITY 2048
#define INITIAL_ENC_BUFFER_CAPACITY 2048
/**
* Inialize encoder context with output context
* allocate initial memory to buffer of context
@@ -211,7 +186,7 @@ struct encoder_ctx *init_encoder(struct encoder_cfg *opt);
* after deallocating user need to allocate encoder ctx again
*
* @oaram arg pointer to initialized encoder ctx using init_encoder
*
*
* @param current_fts to calculate window for end credits
*/
void dinit_encoder(struct encoder_ctx **arg, LLONG current_fts);
@@ -220,53 +195,52 @@ void dinit_encoder(struct encoder_ctx **arg, LLONG current_fts);
* @param ctx encoder context
* @param sub subtitle context returned by decoder
*/
int encode_sub(struct encoder_ctx *ctx, struct cc_subtitle *sub);
int encode_sub(struct encoder_ctx *ctx,struct cc_subtitle *sub);
int write_cc_buffer_as_ccd(const struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_scc(const struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_srt(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_ssa(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_webvtt(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_spupng(struct eia608_screen *data, struct encoder_ctx *context);
void write_cc_buffer_to_gui(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_ccd (const struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_scc (const struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_srt (struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_ssa (struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_webvtt (struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_sami (struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_smptett (struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_spupng (struct eia608_screen *data, struct encoder_ctx *context);
void write_cc_buffer_to_gui (struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_g608(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_g608 (struct eia608_screen *data, struct encoder_ctx *context);
int write_cc_buffer_as_transcript2 (struct eia608_screen *data, struct encoder_ctx *context);
void write_cc_line_as_transcript2(struct eia608_screen *data, struct encoder_ctx *context, int line_number);
void write_cc_line_as_transcript2 (struct eia608_screen *data, struct encoder_ctx *context, int line_number);
int write_cc_subtitle_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_ssa(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_srt (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_ssa (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_webvtt (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_sami (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_smptett (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_spupng (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_subtitle_as_transcript (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_stringz_as_srt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_stringz_as_ssa(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_stringz_as_webvtt(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_cc_bitmap_as_srt(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_ssa(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_webvtt(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_spupng(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_transcript(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_libcurl(struct cc_subtitle *sub, struct encoder_ctx *context);
int write_stringz_as_srt (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_stringz_as_ssa (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_stringz_as_webvtt (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_stringz_as_sami (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
void write_stringz_as_smptett (char *string, struct encoder_ctx *context, LLONG ms_start, LLONG ms_end);
int write_cc_bitmap_as_srt (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_ssa (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_webvtt (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_sami (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_smptett (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_spupng (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_transcript (struct cc_subtitle *sub, struct encoder_ctx *context);
int write_cc_bitmap_as_libcurl (struct cc_subtitle *sub, struct encoder_ctx *context);
void write_spumux_header(struct encoder_ctx *ctx, struct ccx_s_write *out);
void write_spumux_footer(struct ccx_s_write *out);
// WebVTT header writer (issue #1743 - ensures header is written even for empty files)
void write_webvtt_header(struct encoder_ctx *context);
struct cc_subtitle *reformat_cc_bitmap_through_sentence_buffer(struct cc_subtitle *sub, struct encoder_ctx *context);
struct cc_subtitle * reformat_cc_bitmap_through_sentence_buffer (struct cc_subtitle *sub, struct encoder_ctx *context);
void set_encoder_last_displayed_subs_ms(struct encoder_ctx *ctx, LLONG last_displayed_subs_ms);
void set_encoder_subs_delay(struct encoder_ctx *ctx, LLONG subs_delay);
@@ -277,7 +251,8 @@ int reset_output_ctx(struct encoder_ctx *ctx, struct encoder_cfg *cfg);
void find_limit_characters(const unsigned char *line, int *first_non_blank, int *last_non_blank, int max_len);
int get_str_basic(unsigned char *out_buffer, unsigned char *in_buffer, int trim_subs,
enum ccx_encoding_type in_enc, enum ccx_encoding_type out_enc, int max_len);
enum ccx_encoding_type in_enc, enum ccx_encoding_type out_enc, int max_len);
unsigned int get_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data);
unsigned int get_color_encoded(struct encoder_ctx *ctx, unsigned char *buffer, int line_num, struct eia608_screen *data);
@@ -285,9 +260,4 @@ unsigned int get_font_encoded(struct encoder_ctx *ctx, unsigned char *buffer, in
struct lib_ccx_ctx;
void switch_output_file(struct lib_ccx_ctx *ctx, struct encoder_ctx *enc_ctx, int track_id);
// Teletext multi-page output (issue #665)
struct ccx_s_write *get_teletext_output(struct encoder_ctx *ctx, uint16_t teletext_page);
unsigned int *get_teletext_srt_counter(struct encoder_ctx *ctx, uint16_t teletext_page);
void dinit_teletext_outputs(struct encoder_ctx *ctx);
#endif

View File

@@ -56,7 +56,7 @@ int write_cc_bitmap_as_libcurl(struct cc_subtitle *sub, struct encoder_ctx *cont
millis_to_time(ms_start, &h1, &m1, &s1, &ms1);
millis_to_time(ms_end - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
context->srt_counter++;
snprintf(timeline, sizeof(timeline), "group_id=ccextractordev&start_time=%" PRIu64 "&end_time=%" PRIu64 "&lang=en", ms_start, ms_end);
sprintf(timeline, "group_id=ccextractordev&start_time=%" PRIu64 "&end_time=%" PRIu64 "&lang=en", ms_start, ms_end);
char *curlline = NULL;
curlline = str_reallocncat(curlline, timeline);
curlline = str_reallocncat(curlline, "&payload=");
@@ -65,13 +65,9 @@ int write_cc_bitmap_as_libcurl(struct cc_subtitle *sub, struct encoder_ctx *cont
curl_free(urlencoded);
mprint("%s", curlline);
size_t result_size = strlen(ccx_options.curlposturl) + strlen("/frame/") + 1;
char *result = malloc(result_size);
if (!result)
{
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_cc_bitmap_as_curl: Out of memory allocating result.");
}
snprintf(result, result_size, "%s/frame/", ccx_options.curlposturl);
char *result = malloc(strlen(ccx_options.curlposturl) + strlen("/frame/") + 1);
strcpy(result, ccx_options.curlposturl);
strcat(result, "/frame/");
curl_easy_setopt(curl, CURLOPT_URL, result);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, curlline);
free(result);

View File

@@ -14,11 +14,11 @@ int write_cc_buffer_as_g608(struct eia608_screen *data, struct encoder_ctx *cont
millis_to_time(data->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
char timeline[128];
context->srt_counter++;
snprintf(timeline, sizeof(timeline), "%u%s", context->srt_counter, context->encoded_crlf);
sprintf(timeline, "%u%s", context->srt_counter, context->encoded_crlf);
used = encode_line(context, context->buffer, (unsigned char *)timeline);
write_wrapped(context->out->fh, context->buffer, used);
snprintf(timeline, sizeof(timeline), "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
sprintf(timeline, "%02u:%02u:%02u,%03u --> %02u:%02u:%02u,%03u%s",
h1, m1, s1, ms1, h2, m2, s2, ms2, context->encoded_crlf);
used = encode_line(context, context->buffer, (unsigned char *)timeline);
write_wrapped(context->out->fh, context->buffer, used);

View File

@@ -316,7 +316,7 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer
}
if (color_text[its_color][1][0]) // That means a <font> was added to the buffer
{
strncat(tagstack, "F", sizeof(tagstack) - strlen(tagstack) - 1);
strcat(tagstack, "F");
changed_font++;
}
color = its_color;
@@ -326,7 +326,7 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer
if (is_underlined && underlined == 0 && !ctx->no_type_setting) // Open underline
{
buffer += encode_line(ctx, buffer, (unsigned char *)"<u>");
strncat(tagstack, "U", sizeof(tagstack) - strlen(tagstack) - 1);
strcat(tagstack, "U");
underlined++;
}
if (is_underlined == 0 && underlined && !ctx->no_type_setting) // Close underline
@@ -338,7 +338,7 @@ unsigned get_decoder_line_encoded(struct encoder_ctx *ctx, unsigned char *buffer
if (has_ita && italics == 0 && !ctx->no_type_setting) // Open italics
{
buffer += encode_line(ctx, buffer, (unsigned char *)"<i>");
strncat(tagstack, "I", sizeof(tagstack) - strlen(tagstack) - 1);
strcat(tagstack, "I");
italics++;
}
if (has_ita == 0 && italics && !ctx->no_type_setting) // Close italics
@@ -410,13 +410,10 @@ int add_word(struct word_list *list, const char *word)
if (list->len == list->capacity)
{
list->capacity += 50;
char **tmp = realloc(list->words, list->capacity * sizeof(char *));
if (!tmp)
if ((list->words = realloc(list->words, list->capacity * sizeof(char *))) == NULL)
{
list->capacity -= 50; // Restore original capacity
return -1;
}
list->words = tmp;
}
size_t word_len = strlen(word);
@@ -425,7 +422,7 @@ int add_word(struct word_list *list, const char *word)
return -1;
}
memcpy(list->words[list->len++], word, word_len + 1);
strcpy(list->words[list->len++], word);
return word_len;
}
@@ -469,11 +466,6 @@ void shell_sort(void *base, int nb, size_t size, int (*compar)(const void *p1, c
{
unsigned char *lbase = (unsigned char *)base;
unsigned char *tmp = (unsigned char *)malloc(size);
if (!tmp)
{
// Cannot sort without temporary buffer, return silently
return;
}
for (int gap = nb / 2; gap > 0; gap = gap / 2)
{
int p, j;

View File

@@ -4,7 +4,6 @@
#include <stdarg.h>
#include "ccx_encoders_mcc.h"
#include "lib_ccx.h"
#include "utility.h"
#define MORE_DEBUG CCX_FALSE
@@ -143,16 +142,10 @@ boolean mcc_encode_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *de
caption_time.second, caption_time.frame, num_chars_needed);
#endif
size_t compressed_data_size = num_chars_needed + 13;
char *compressed_data_buffer = malloc(compressed_data_size);
if (!compressed_data_buffer)
{
free(w_boilerplate_buffer);
fatal(EXIT_NOT_ENOUGH_MEMORY, "In mcc_encode_cc_data: Out of memory allocating compressed_data_buffer.");
}
char *compressed_data_buffer = malloc(num_chars_needed + 13);
snprintf(compressed_data_buffer, compressed_data_size, "%02d:%02d:%02d:%02d\t", caption_time.hour, caption_time.minute,
caption_time.second, caption_time.frame);
sprintf(compressed_data_buffer, "%02d:%02d:%02d:%02d\t", caption_time.hour, caption_time.minute,
caption_time.second, caption_time.frame);
compress_data(w_boilerplate_buffer, w_boilerplate_buff_size, (uint8 *)&compressed_data_buffer[12]);
free(w_boilerplate_buffer);
@@ -162,12 +155,7 @@ boolean mcc_encode_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *de
caption_time.hour, caption_time.minute, caption_time.second, caption_time.frame);
#endif
size_t current_len = strlen(compressed_data_buffer);
if (current_len + 1 < compressed_data_size)
{
compressed_data_buffer[current_len] = '\n';
compressed_data_buffer[current_len + 1] = '\0';
}
strcat(compressed_data_buffer, "\n");
write_wrapped(enc_ctx->out->fh, compressed_data_buffer, strlen(compressed_data_buffer));
@@ -180,59 +168,58 @@ boolean mcc_encode_cc_data(struct encoder_ctx *enc_ctx, struct lib_cc_decode *de
static void generate_mcc_header(int fh, int fr_code, int dropframe_flag)
{
char uuid_str[50];
char date_str[64];
char time_str[32];
char tcr_str[32];
char date_str[50];
char time_str[30];
char tcr_str[25];
time_t t = time(NULL);
struct tm tm = *localtime(&t);
snprintf(uuid_str, sizeof(uuid_str), "UUID=");
sprintf(uuid_str, "UUID=");
uuid4(&uuid_str[5]);
uuid_str[41] = '\n';
uuid_str[42] = '\0';
ASSERT(tm.tm_wday < 7);
ASSERT(tm.tm_mon < 12);
snprintf(date_str, sizeof(date_str), "Creation Date=%s, %s %d, %d\n", DayOfWeekStr[tm.tm_wday], MonthStr[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
snprintf(time_str, sizeof(time_str), "Creation Time=%d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
sprintf(date_str, "Creation Date=%s, %s %d, %d\n", DayOfWeekStr[tm.tm_wday], MonthStr[tm.tm_mon], tm.tm_mday, tm.tm_year + 1900);
sprintf(time_str, "Creation Time=%d:%02d:%02d\n", tm.tm_hour, tm.tm_min, tm.tm_sec);
switch (fr_code)
{
case 1:
case 2:
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=24\n\n");
sprintf(tcr_str, "Time Code Rate=24\n\n");
break;
case 3:
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=25\n\n");
sprintf(tcr_str, "Time Code Rate=25\n\n");
break;
case 4:
case 5:
if (dropframe_flag == CCX_TRUE)
{
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=30DF\n\n");
sprintf(tcr_str, "Time Code Rate=30DF\n\n");
}
else
{
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=30\n\n");
sprintf(tcr_str, "Time Code Rate=30\n\n");
}
break;
case 6:
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=50\n\n");
sprintf(tcr_str, "Time Code Rate=50\n\n");
break;
case 7:
case 8:
if (dropframe_flag == CCX_TRUE)
{
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=60DF\n\n");
sprintf(tcr_str, "Time Code Rate=60DF\n\n");
}
else
{
snprintf(tcr_str, sizeof(tcr_str), "Time Code Rate=60\n\n");
sprintf(tcr_str, "Time Code Rate=60\n\n");
}
break;
default:
LOG("ERROR: Invalid Framerate Code: %d", fr_code);
tcr_str[0] = '\0';
break;
}
@@ -284,10 +271,6 @@ static uint8 *add_boilerplate(struct encoder_ctx *ctx, unsigned char *cc_data, i
uint8 data_size = cc_count * 3;
uint8 *buff_ptr = malloc(data_size + 16);
if (!buff_ptr)
{
fatal(EXIT_NOT_ENOUGH_MEMORY, "In add_boilerplate: Out of memory allocating buff_ptr.");
}
uint8 cdp_frame_rate = CDP_FRAME_RATE_FORBIDDEN;
switch (fr_code)
@@ -340,15 +323,15 @@ static uint8 *add_boilerplate(struct encoder_ctx *ctx, unsigned char *cc_data, i
buff_ptr[5] = data_size + 12;
buff_ptr[6] = ((cdp_frame_rate << 4) | 0x0F);
buff_ptr[7] = 0x43; // Timecode not Present; Service Info not Present; Captions Present
buff_ptr[8] = (uint8)((ctx->cdp_hdr_seq >> 8) & 0xFF);
buff_ptr[9] = (uint8)(ctx->cdp_hdr_seq & 0xFF);
buff_ptr[8] = (uint8)((ctx->cdp_hdr_seq & 0xF0) >> 8);
buff_ptr[9] = (uint8)(ctx->cdp_hdr_seq & 0x0F);
buff_ptr[10] = CC_DATA_ID;
buff_ptr[11] = cc_count | 0xE0;
memcpy(&buff_ptr[12], cc_data, data_size);
uint8 *data_ptr = &buff_ptr[data_size + 12];
data_ptr[0] = CDP_FOOTER_ID;
data_ptr[1] = (uint8)((ctx->cdp_hdr_seq >> 8) & 0xFF);
data_ptr[2] = (uint8)(ctx->cdp_hdr_seq & 0xFF);
data_ptr[1] = (uint8)((ctx->cdp_hdr_seq & 0xF0) >> 8);
data_ptr[2] = (uint8)(ctx->cdp_hdr_seq & 0x0F);
data_ptr[3] = 0;
for (int loop = 0; loop < (data_size + 15); loop++)
@@ -656,10 +639,9 @@ static void compress_data(uint8 *data_ptr, uint16 num_elements, uint8 *out_data_
static void random_chars(char buffer[], int len)
{
static const char hex_chars[] = "0123456789ABCDEF";
for (int i = 0; i < len; i++)
{
buffer[i] = hex_chars[rand() % 16];
sprintf(buffer + i, "%X", rand() % 16);
}
}
@@ -692,15 +674,14 @@ static void debug_log(char *file, int line, ...)
va_start(args, line);
char *fmt = va_arg(args, char *);
vsnprintf(message, sizeof(message), fmt, args);
vsprintf(message, fmt, args);
va_end(args);
char *basename = strrchr(file, '/');
basename = basename ? basename + 1 : file;
size_t msg_len = strlen(message);
if (msg_len > 0 && message[msg_len - 1] == '\n')
message[msg_len - 1] = '\0';
if (message[(strlen(message) - 1)] == '\n')
message[(strlen(message) - 1)] = '\0';
dbg_print(CCX_DMT_VERBOSE, "[%s:%d] - %s\n", basename, line, message);
} // debug_log()

View File

@@ -9,22 +9,22 @@
/*-- Constants --*/
/*----------------------------------------------------------------------------*/
#define ANC_DID_CLOSED_CAPTIONING 0x61
#define ANC_SDID_CEA_708 0x01
#define CDP_IDENTIFIER_VALUE_HIGH 0x96
#define CDP_IDENTIFIER_VALUE_LOW 0x69
#define CC_DATA_ID 0x72
#define CDP_FOOTER_ID 0x74
#define ANC_DID_CLOSED_CAPTIONING 0x61
#define ANC_SDID_CEA_708 0x01
#define CDP_IDENTIFIER_VALUE_HIGH 0x96
#define CDP_IDENTIFIER_VALUE_LOW 0x69
#define CC_DATA_ID 0x72
#define CDP_FOOTER_ID 0x74
#define CDP_FRAME_RATE_FORBIDDEN 0x00
#define CDP_FRAME_RATE_23_976 0x01
#define CDP_FRAME_RATE_24 0x02
#define CDP_FRAME_RATE_25 0x03
#define CDP_FRAME_RATE_29_97 0x04
#define CDP_FRAME_RATE_30 0x05
#define CDP_FRAME_RATE_50 0x06
#define CDP_FRAME_RATE_59_94 0x07
#define CDP_FRAME_RATE_60 0x08
#define CDP_FRAME_RATE_FORBIDDEN 0x00
#define CDP_FRAME_RATE_23_976 0x01
#define CDP_FRAME_RATE_24 0x02
#define CDP_FRAME_RATE_25 0x03
#define CDP_FRAME_RATE_29_97 0x04
#define CDP_FRAME_RATE_30 0x05
#define CDP_FRAME_RATE_50 0x06
#define CDP_FRAME_RATE_59_94 0x07
#define CDP_FRAME_RATE_60 0x08
/*----------------------------------------------------------------------------*/
/*-- Types --*/
@@ -46,9 +46,7 @@ typedef long long int64;
/*----------------------------------------------------------------------------*/
#define LOG(...) debug_log(__FILE__, __LINE__, __VA_ARGS__)
#define ASSERT(x) \
if (!(x)) \
debug_log(__FILE__, __LINE__, "ASSERT FAILED!")
#define ASSERT(x) if(!(x)) debug_log(__FILE__, __LINE__, "ASSERT FAILED!")
/*----------------------------------------------------------------------------*/
/*-- Exposed Variables --*/

View File

@@ -15,7 +15,7 @@ int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_st
unsigned char *el = NULL;
char str[1024];
snprintf(str, sizeof(str), "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)ms_start);
sprintf(str, "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)ms_start);
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -85,7 +85,7 @@ int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_st
begin += strlen((const char *)begin) + 1;
}
snprintf(str, sizeof(str), "</P></SYNC>\r\n");
sprintf((char *)str, "</P></SYNC>\r\n");
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -94,9 +94,9 @@ int write_stringz_as_sami(char *string, struct encoder_ctx *context, LLONG ms_st
ret = write(context->out->fh, context->buffer, used);
if (ret != used)
goto end;
snprintf(str, sizeof(str),
"<SYNC start=%llu><P class=\"UNKNOWNCC\">&nbsp;</P></SYNC>\r\n\r\n",
(unsigned long long)ms_end);
sprintf((char *)str,
"<SYNC start=%llu><P class=\"UNKNOWNCC\">&nbsp;</P></SYNC>\r\n\r\n",
(unsigned long long)ms_end);
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -127,8 +127,8 @@ int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context
if (sub->data != NULL) // then we should write the sub
{
snprintf(buf, context->capacity,
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)sub->start_time);
sprintf(buf,
"<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n", (unsigned long long)sub->start_time);
write_wrapped(context->out->fh, buf, strlen(buf));
for (int i = sub->nb_data - 1; i >= 0; i--)
{
@@ -137,7 +137,7 @@ int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context
if (context->prev_start != -1 || !(sub->flags & SUB_EOD_MARKER))
{
token = strtok(rect[i].ocr_text, "\r\n");
snprintf(buf, context->capacity, "%s", token);
sprintf(buf, "%s", token);
token = strtok(NULL, "\r\n");
write_wrapped(context->out->fh, buf, strlen(buf));
if (i != 0)
@@ -146,13 +146,13 @@ int write_cc_bitmap_as_sami(struct cc_subtitle *sub, struct encoder_ctx *context
}
}
}
snprintf(buf, context->capacity, "</P></SYNC>\r\n");
sprintf(buf, "</P></SYNC>\r\n");
write_wrapped(context->out->fh, buf, strlen(buf));
}
else // we write an empty subtitle to clear the old one
{
snprintf(buf, context->capacity,
"<SYNC start=%llu><P class=\"UNKNOWNCC\">&nbsp;</P></SYNC>\r\n\r\n", (unsigned long long)sub->start_time);
sprintf(buf,
"<SYNC start=%llu><P class=\"UNKNOWNCC\">&nbsp;</P></SYNC>\r\n\r\n", (unsigned long long)sub->start_time);
write_wrapped(context->out->fh, buf, strlen(buf));
}
#endif
@@ -194,8 +194,8 @@ int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *cont
int wrote_something = 0;
char str[1024];
snprintf(str, sizeof(str), "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n",
(unsigned long long)data->start_time);
sprintf(str, "<SYNC start=%llu><P class=\"UNKNOWNCC\">\r\n",
(unsigned long long)data->start_time);
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -219,16 +219,16 @@ int write_cc_buffer_as_sami(struct eia608_screen *data, struct encoder_ctx *cont
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
}
}
snprintf(str, sizeof(str), "</P></SYNC>\r\n");
sprintf((char *)str, "</P></SYNC>\r\n");
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
}
used = encode_line(context, context->buffer, (unsigned char *)str);
write_wrapped(context->out->fh, context->buffer, used);
snprintf(str, sizeof(str),
"<SYNC start=%llu><P class=\"UNKNOWNCC\">&nbsp;</P></SYNC>\r\n\r\n",
(unsigned long long)data->end_time - 1); // - 1 to prevent overlap
sprintf((char *)str,
"<SYNC start=%llu><P class=\"UNKNOWNCC\">&nbsp;</P></SYNC>\r\n\r\n",
(unsigned long long)data->end_time - 1); // - 1 to prevent overlap
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);

View File

@@ -10,171 +10,6 @@ unsigned char odd_parity(const unsigned char byte)
return byte | !(cc608_parity(byte) % 2) << 7;
}
/**
* SCC Accurate Timing Implementation (Issue #1120)
*
* EIA-608 bandwidth constraints:
* - 2 bytes per frame at 29.97 FPS (or configured frame rate)
* - Captions must be pre-loaded before display time
* - Each control code takes 2 bytes (sent twice for reliability = 4 bytes total)
* - Text characters take 1 byte each
*/
// Get frame rate value from scc_framerate setting
// 0=29.97 (default), 1=24, 2=25, 3=30
static float get_scc_fps_internal(int scc_framerate)
{
switch (scc_framerate)
{
case 1:
return 24.0f;
case 2:
return 25.0f;
case 3:
return 30.0f;
default:
return 29.97f;
}
}
/**
* Calculate total bytes needed to transmit a caption
*
* Byte costs:
* - Control code (RCL, EOC, ENM, EDM): 2 bytes x 2 (sent twice) = 4 bytes
* - Preamble code: 2 bytes x 2 = 4 bytes
* - Tab offset: 2 bytes x 2 = 4 bytes
* - Mid-row code (color/style): 2 bytes x 2 = 4 bytes
* - Text character: 1 byte each
* - Padding: 1 byte if odd number of text bytes
*/
static unsigned int calculate_caption_bytes(const struct eia608_screen *data)
{
unsigned int total_bytes = 0;
// RCL (Resume Caption Loading): 4 bytes
total_bytes += 4;
for (unsigned char row = 0; row < 15; ++row)
{
if (!data->row_used[row])
continue;
int first, last;
find_limit_characters(data->characters[row], &first, &last, CCX_DECODER_608_SCREEN_WIDTH);
if (first > last)
continue;
// Assume we need at least one preamble per row: 4 bytes
total_bytes += 4;
// Count characters on this row
unsigned int char_count = 0;
enum font_bits prev_font = FONT_REGULAR;
enum ccx_decoder_608_color_code prev_color = COL_WHITE;
int prev_col = -1;
for (int col = first; col <= last; ++col)
{
// Check if we need position codes
if (prev_col != col - 1 && prev_col != -1)
{
// Need preamble + possible tab offset: 4-8 bytes
total_bytes += 4;
if (col % 4 != 0)
total_bytes += 4; // Tab offset
}
// Check if we need mid-row style codes
if (data->fonts[row][col] != prev_font || data->colors[row][col] != prev_color)
{
total_bytes += 4; // Mid-row code
prev_font = data->fonts[row][col];
prev_color = data->colors[row][col];
}
// Text character
char_count++;
prev_col = col;
}
// Add text bytes (1 per character, rounded up to even)
total_bytes += char_count;
if (char_count % 2 == 1)
total_bytes++; // Padding
}
// EOC (End of Caption): 4 bytes
total_bytes += 4;
// ENM (Erase Non-displayed Memory): 4 bytes
total_bytes += 4;
return total_bytes;
}
/**
* Calculate the pre-roll start time for a caption
*
* @param display_time When the caption should appear on screen (ms)
* @param total_bytes Total bytes to transmit
* @param fps Frame rate
* @return Time to begin loading the caption (ms)
*/
static LLONG calculate_preroll_time(LLONG display_time, unsigned int total_bytes, float fps)
{
// Calculate transmission time in milliseconds
// 2 bytes per frame, so frames_needed = (total_bytes + 1) / 2
float ms_per_frame = 1000.0f / fps;
unsigned int frames_needed = (total_bytes + 1) / 2;
LLONG transmission_time_ms = (LLONG)(frames_needed * ms_per_frame);
// Add 1 frame for EOC to be sent before display
LLONG one_frame_ms = (LLONG)ms_per_frame;
LLONG preroll_start = display_time - transmission_time_ms - one_frame_ms;
// Don't go negative
if (preroll_start < 0)
preroll_start = 0;
return preroll_start;
}
/**
* Check for collision with previous caption transmission and resolve it
*
* @param context Encoder context with timing state
* @param preroll_start Proposed pre-roll start time (will be modified if collision)
* @param display_time Caption display time (may be adjusted)
* @param fps Frame rate
* @return true if timing was adjusted due to collision
*/
static bool resolve_collision(struct encoder_ctx *context, LLONG *preroll_start,
LLONG *display_time, float fps)
{
// Check if our preroll would start before previous caption finishes transmitting
// This prevents bandwidth collision but allows visual overlap (like scc_tools)
// Visual overlap is fine - the EOC command swaps buffers atomically
if (context->scc_last_transmission_end > 0 &&
*preroll_start < context->scc_last_transmission_end)
{
// Bandwidth collision detected - shift our caption forward
// Add 1 frame buffer to ensure no overlap
LLONG one_frame_ms = (LLONG)(1000.0f / fps);
LLONG new_preroll = context->scc_last_transmission_end + one_frame_ms;
LLONG shift = new_preroll - *preroll_start;
*preroll_start = new_preroll;
*display_time += shift;
return true;
}
return false;
}
struct control_code_info
{
unsigned int byte1_odd;
@@ -649,156 +484,14 @@ void write_control_code(const int fd, const unsigned char channel, const enum co
* @param row 0-14 (inclusive)
* @param column 0-31 (inclusive)
*
* Returns an indent-based preamble code (positions cursor at column with white color)
* //TODO: Preamble code need to take into account font as well
*
*/
enum control_code get_preamble_code(const unsigned char row, const unsigned char column)
{
return PREAMBLE_CC_START + 1 + (row * 8) + (column / 4);
}
/**
* Get byte2 value for a styled PAC (color/font at column 0)
* Returns 0x40-0x4F or 0x60-0x6F depending on the style
*
* @param color The color to use
* @param font The font style to use
* @param use_high_range If true, use 0x60-0x6F range instead of 0x40-0x4F
*
* PAC style encoding (byte2):
* 0x40/0x60: white, regular 0x41/0x61: white, underline
* 0x42/0x62: green, regular 0x43/0x63: green, underline
* 0x44/0x64: blue, regular 0x45/0x65: blue, underline
* 0x46/0x66: cyan, regular 0x47/0x67: cyan, underline
* 0x48/0x68: red, regular 0x49/0x69: red, underline
* 0x4a/0x6a: yellow, regular 0x4b/0x6b: yellow, underline
* 0x4c/0x6c: magenta, regular 0x4d/0x6d: magenta, underline
* 0x4e/0x6e: white, italics 0x4f/0x6f: white, italic underline
*/
static unsigned char get_styled_pac_byte2(enum ccx_decoder_608_color_code color, enum font_bits font, bool use_high_range)
{
unsigned char base = use_high_range ? 0x60 : 0x40;
unsigned char style_offset;
// Handle italics specially - they're always white
if (font == FONT_ITALICS)
return base + 0x0e;
if (font == FONT_UNDERLINED_ITALICS)
return base + 0x0f;
// Map color to base offset (0, 2, 4, 6, 8, 10, 12)
switch (color)
{
case COL_WHITE:
style_offset = 0x00;
break;
case COL_GREEN:
style_offset = 0x02;
break;
case COL_BLUE:
style_offset = 0x04;
break;
case COL_CYAN:
style_offset = 0x06;
break;
case COL_RED:
style_offset = 0x08;
break;
case COL_YELLOW:
style_offset = 0x0a;
break;
case COL_MAGENTA:
style_offset = 0x0c;
break;
default:
// For unsupported colors (black, transparent, userdefined), fall back to white
style_offset = 0x00;
break;
}
// Add 1 for underlined
if (font == FONT_UNDERLINED)
style_offset += 1;
return base + style_offset;
}
/**
* Check if the row uses high range (0x60-0x6F) or low range (0x40-0x4F) for styled PACs
* Rows that have byte2 in 0x70-0x7F range for indents use 0x60-0x6F for styles
*/
static bool row_uses_high_range(unsigned char row)
{
// Based on the preamble code table:
// Rows 2, 4, 6, 8, 10, 13, 15 use the "high" range (byte2 0x70-0x7F for indents)
// which corresponds to 0x60-0x6F for styled PACs
return (row == 1 || row == 3 || row == 5 || row == 7 || row == 9 || row == 12 || row == 14);
}
/**
* Write a styled PAC code (color/font at column 0) directly
* This is more efficient than using indent PAC + mid-row code when at column 0
*
* @param fd File descriptor
* @param channel Caption channel (1-4)
* @param row Row number (0-14)
* @param color Color to set
* @param font Font style to set
* @param disassemble If true, output assembly format
* @param bytes_written Pointer to byte counter
*/
static void write_styled_preamble(const int fd, const unsigned char channel, const unsigned char row,
enum ccx_decoder_608_color_code color, enum font_bits font,
const bool disassemble, unsigned int *bytes_written)
{
// Get the preamble code for column 0 to obtain byte1
enum control_code base_preamble = get_preamble_code(row, 0);
unsigned char byte1 = odd_parity(get_first_byte(channel, base_preamble));
// Get styled byte2
bool use_high_range = row_uses_high_range(row);
unsigned char byte2 = odd_parity(get_styled_pac_byte2(color, font, use_high_range));
check_padding(fd, disassemble, bytes_written);
if (disassemble)
{
// Output assembly format like {0100Gr} for row 1, green
const char *color_names[] = {"Wh", "Gr", "Bl", "Cy", "R", "Y", "Ma", "Wh", "Bk", "Wh"};
const char *font_suffix = "";
if (font == FONT_UNDERLINED)
font_suffix = "U";
else if (font == FONT_ITALICS)
font_suffix = "I";
else if (font == FONT_UNDERLINED_ITALICS)
font_suffix = "IU";
fdprintf(fd, "{%02d00%s%s}", row + 1, color_names[color], font_suffix);
}
else
{
if (*bytes_written % 2 == 0)
write_wrapped(fd, " ", 1);
fdprintf(fd, "%02x%02x", byte1, byte2);
}
*bytes_written += 2;
}
/**
* Check if a styled PAC can be used (when color/font differs from white/regular and column is 0)
*/
static bool can_use_styled_pac(enum ccx_decoder_608_color_code color, enum font_bits font, unsigned char column)
{
// Styled PACs can only be used at column 0
if (column != 0)
return false;
// If style is already white/regular, no need for styled PAC
if (color == COL_WHITE && font == FONT_REGULAR)
return false;
return true;
}
enum control_code get_tab_offset_code(const unsigned char column)
{
int offset = column % 4;
@@ -826,23 +519,6 @@ enum control_code get_font_code(enum font_bits font, enum ccx_decoder_608_color_
}
}
// Get frame rate value from scc_framerate setting
// 0=29.97 (default), 1=24, 2=25, 3=30
static float get_scc_fps(int scc_framerate)
{
switch (scc_framerate)
{
case 1:
return 24.0f;
case 2:
return 25.0f;
case 3:
return 30.0f;
default:
return 29.97f;
}
}
void add_timestamp(const struct encoder_ctx *context, LLONG time, const bool disassemble)
{
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
@@ -852,15 +528,9 @@ void add_timestamp(const struct encoder_ctx *context, LLONG time, const bool dis
unsigned hour, minute, second, milli;
millis_to_time(time, &hour, &minute, &second, &milli);
// SMPTE format - use configurable frame rate (issue #1191)
float fps = get_scc_fps(context->scc_framerate);
// Calculate frame number from milliseconds, ensuring it stays in valid range 0 to fps-1
// Use floor to avoid rounding up to fps (e.g., 29.97 -> 30 is invalid)
int max_frames = (int)fps;
int frame = (int)(milli * fps / 1000.0f);
if (frame >= max_frames)
frame = max_frames - 1; // Cap at max valid frame (e.g., 29 for 29.97fps)
fdprintf(context->out->fh, "%02u:%02u:%02u:%02d\t", hour, minute, second, frame);
// SMPTE format
float frame = milli * 29.97 / 1000;
fdprintf(context->out->fh, "%02u:%02u:%02u:%02.f\t", hour, minute, second, frame);
}
void clear_screen(const struct encoder_ctx *context, LLONG end_time, const unsigned char channel, const bool disassemble)
@@ -875,56 +545,11 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
unsigned int bytes_written = 0;
enum font_bits current_font = FONT_REGULAR;
enum ccx_decoder_608_color_code current_color = COL_WHITE;
// Initialize to impossible values to ensure the first character always
// triggers a position code write (fixes issue #1776)
unsigned char current_row = UINT8_MAX;
unsigned char current_column = UINT8_MAX;
// Timing variables for accurate timing mode (issue #1120)
LLONG actual_start_time = data->start_time; // When caption should display
LLONG actual_end_time = data->end_time; // When caption should clear
LLONG preroll_start = data->start_time; // When to start loading (default: same as display)
float fps = get_scc_fps_internal(context->scc_framerate);
bool use_separate_display_time = false; // Whether to write EOC at separate timestamp
// If accurate timing is enabled, calculate pre-roll and handle collisions
if (context->scc_accurate_timing)
{
// Calculate total bytes needed for this caption
unsigned int total_bytes = calculate_caption_bytes(data);
// Calculate when we need to start loading
preroll_start = calculate_preroll_time(actual_start_time, total_bytes, fps);
// Check for collisions with previous caption and resolve
if (resolve_collision(context, &preroll_start, &actual_start_time, fps))
{
// Timing was adjusted due to collision
// Also adjust end time by the same amount
LLONG shift = actual_start_time - data->start_time;
actual_end_time = data->end_time + shift;
}
// Update timing state for next caption
float ms_per_frame = 1000.0f / fps;
unsigned int frames_needed = (total_bytes + 1) / 2;
LLONG transmission_time_ms = (LLONG)(frames_needed * ms_per_frame);
context->scc_last_transmission_end = preroll_start + transmission_time_ms;
context->scc_last_display_end = actual_end_time;
// Enable separate display timing (like scc_tools)
use_separate_display_time = true;
// 1. Load the caption at pre-roll time
add_timestamp(context, preroll_start, disassemble);
}
else
{
// Legacy mode: use original timing
// 1. Load the caption
add_timestamp(context, data->start_time, disassemble);
}
unsigned char current_row = 14;
unsigned char current_column = 0;
// 1. Load the caption
add_timestamp(context, data->start_time, disassemble);
write_control_code(context->out->fh, data->channel, RCL, disassemble, &bytes_written);
for (uint8_t row = 0; row < 15; ++row)
{
@@ -951,23 +576,6 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
{
if (switch_font || switch_color)
{
// Optimization (issue #1191): Use styled PAC when at column 0 with non-default style
// This avoids needing a separate mid-row code
if (column == 0 && can_use_styled_pac(data->colors[row][column], data->fonts[row][column], 0))
{
write_styled_preamble(context->out->fh, data->channel, row,
data->colors[row][column], data->fonts[row][column],
disassemble, &bytes_written);
current_row = row;
current_column = 0;
current_font = data->fonts[row][column];
current_color = data->colors[row][column];
// Write the character and continue
write_character(context->out->fh, data->characters[row][column], disassemble, &bytes_written);
++current_column;
continue;
}
if (data->characters[row][column] == ' ')
{
// The MID-ROW code is going to move the cursor to the
@@ -1007,26 +615,12 @@ int write_cc_buffer_as_scenarist(const struct eia608_screen *data, struct encode
check_padding(context->out->fh, disassemble, &bytes_written);
}
// 2. Show the caption (EOC = End of Caption, makes it visible)
if (use_separate_display_time)
{
// For accurate timing: write display command at actual display time
// This matches scc_tools behavior where load and display are separate
add_timestamp(context, actual_start_time, disassemble);
}
// 2. Show the caption
write_control_code(context->out->fh, data->channel, EOC, disassemble, &bytes_written);
write_control_code(context->out->fh, data->channel, ENM, disassemble, &bytes_written);
// 3. Clear the caption at the end time
// In accurate timing mode, skip clear - the next caption's EOC will handle the transition
// This matches scc_tools behavior which doesn't write EDM between consecutive captions
if (!use_separate_display_time)
{
// Legacy mode: always write clear
clear_screen(context, actual_end_time, data->channel, disassemble);
}
// In accurate timing mode, scc_last_display_end is still tracked for reference
// but we don't write the clear command to avoid out-of-order timestamps
// 3. Clear the caption
clear_screen(context, data->end_time, data->channel, disassemble);
return 1;
}

View File

@@ -36,22 +36,18 @@ void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG m
unsigned h2, m2, s2, ms2;
int len = strlen(string);
unsigned char *unescaped = (unsigned char *)malloc(len + 1);
if (!unescaped)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_smptett() - not enough memory for unescaped buffer.\n");
unsigned char *el = (unsigned char *)malloc(len * 3 + 1); // Be generous
if (!el)
{
free(unescaped);
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_smptett() - not enough memory for el buffer.\n");
}
int pos_r = 0;
int pos_w = 0;
char str[1024];
if (el == NULL || unescaped == NULL)
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_stringz_as_smptett() - not enough memory.\n");
millis_to_time(ms_start, &h1, &m1, &s1, &ms1);
millis_to_time(ms_end - 1, &h2, &m2, &s2, &ms2);
snprintf(str, sizeof(str), "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\r\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
sprintf((char *)str, "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\r\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -91,7 +87,7 @@ void write_stringz_as_smptett(char *string, struct encoder_ctx *context, LLONG m
begin += strlen((const char *)begin) + 1;
}
snprintf(str, sizeof(str), "</p>\n");
sprintf((char *)str, "</p>\n");
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -130,15 +126,13 @@ int write_cc_bitmap_as_smptett(struct cc_subtitle *sub, struct encoder_ctx *cont
unsigned h2, m2, s2, ms2;
millis_to_time(sub->start_time, &h1, &m1, &s1, &ms1);
millis_to_time(sub->end_time - 1, &h2, &m2, &s2, &ms2); // -1 To prevent overlapping with next line.
int written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
write_wrapped(context->out->fh, buf, written);
sprintf((char *)context->buffer, "<p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\">\n", h1, m1, s1, ms1, h2, m2, s2, ms2);
write_wrapped(context->out->fh, buf, strlen(buf));
len = strlen(rect[i].ocr_text);
write_wrapped(context->out->fh, rect[i].ocr_text, len);
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
written = snprintf(buf, INITIAL_ENC_BUFFER_CAPACITY, "</p>\n");
if (written > 0 && (size_t)written < INITIAL_ENC_BUFFER_CAPACITY)
write_wrapped(context->out->fh, buf, written);
sprintf(buf, "</p>\n");
write_wrapped(context->out->fh, buf, strlen(buf));
}
}
}
@@ -224,7 +218,7 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
{
wrote_something = 1;
snprintf(str, sizeof(str), " <p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\" tts:origin=\"%1.3f%% %1.3f%%\">\n <span>", h1, m1, s1, ms1, h2, m2, s2, ms2, col1, row1);
sprintf(str, " <p begin=\"%02u:%02u:%02u.%03u\" end=\"%02u:%02u:%02u.%03u\" tts:origin=\"%1.3f%% %1.3f%%\">\n <span>", h1, m1, s1, ms1, h2, m2, s2, ms2, col1, row1);
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);
@@ -242,16 +236,8 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
get_decoder_line_encoded(context, context->subline, row, data);
size_t subline_len = strlen((const char *)(context->subline));
size_t buf_size = subline_len + 1000; // Being overly generous? :P
char *final = malloc(buf_size);
char *temp = malloc(buf_size);
if (!final || !temp)
{
freep(&final);
freep(&temp);
fatal(EXIT_NOT_ENOUGH_MEMORY, "In write_cc_buffer_as_smptett() - not enough memory.\n");
}
char *final = malloc(strlen((const char *)(context->subline)) + 1000); // Being overly generous? :P
char *temp = malloc(strlen((const char *)(context->subline)) + 1000);
*final = 0;
*temp = 0;
/*
@@ -311,56 +297,37 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
if (end == NULL)
{
// Incorrect styling, writing as it is
snprintf(final, buf_size, "%s", (const char *)(context->subline));
strcpy(final, (const char *)(context->subline));
}
else
{
size_t final_len = 0;
int start_index = start - (char *)(context->subline);
int end_index = end - (char *)(context->subline);
// copying content before opening tag e.g. <i>
if (start_index > 0 && (size_t)start_index < buf_size - 1)
{
memcpy(final, (const char *)(context->subline), start_index);
final[start_index] = '\0';
final_len = start_index;
}
strncat(final, (const char *)(context->subline), start_index); // copying content before opening tag e.g. <i>
// adding <span> : replacement of <i>
size_t remaining = buf_size - final_len;
int written = snprintf(final + final_len, remaining, "<span>");
if (written > 0 && (size_t)written < remaining)
final_len += written;
strcat(final, "<span>"); // adding <span> : replacement of <i>
// The content in italics is between <i> and </i>, i.e. between (start_index + 3) and end_index.
int content_len = end_index - start_index - 3;
if (content_len > 0)
{
remaining = buf_size - final_len;
if ((size_t)content_len < remaining - 1)
{
memcpy(final + final_len, (const char *)(context->subline) + start_index + 3, content_len);
final_len += content_len;
final[final_len] = '\0';
}
}
// adding appropriate style tag
remaining = buf_size - final_len;
strncat(temp, (const char *)(context->subline) + start_index + 3, end_index - start_index - 3); // the content in italics
strcat(final, temp); // attaching to final sentence.
if (style == 1)
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontStyle=\"italic\"/> </span>");
strcpy(temp, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontStyle=\"italic\"/> </span>");
else if (style == 2)
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontWeight=\"bold\"/> </span>");
strcpy(temp, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:fontWeight=\"bold\"/> </span>");
else
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:textDecoration=\"underline\"/> </span>");
strcpy(temp, "<style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\" tts:textDecoration=\"underline\"/> </span>");
if (written > 0 && (size_t)written < remaining)
final_len += written;
strcat(final, temp); // adding appropriate style tag.
// finding remaining sentence and adding it
remaining = buf_size - final_len;
snprintf(final + final_len, remaining, "%s", (const char *)(context->subline) + end_index + 4);
sprintf(temp, "%s", (const char *)(context->subline) + end_index + 4); // finding remaining sentence.
strcat(final, temp); // adding remaining sentence.
}
}
else // No style or Font Color
@@ -373,75 +340,44 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
if (end == NULL)
{
// Incorrect styling, writing as it is
snprintf(final, buf_size, "%s", (const char *)(context->subline));
strcpy(final, (const char *)(context->subline));
}
else
{
size_t final_len = 0;
int start_index = start - (char *)(context->subline);
int end_index = end - (char *)(context->subline);
// copying content before opening tag e.g. <font ..>
if (start_index > 0 && (size_t)start_index < buf_size - 1)
{
memcpy(final, (const char *)(context->subline), start_index);
final[start_index] = '\0';
final_len = start_index;
}
strncat(final, (const char *)(context->subline), start_index); // copying content before opening tag e.g. <font ..>
// adding <span> : replacement of <font ..>
size_t remaining = buf_size - final_len;
int written = snprintf(final + final_len, remaining, "<span>");
if (written > 0 && (size_t)written < remaining)
final_len += written;
strcat(final, "<span>"); // adding <span> : replacement of <font ..>
char *temp_pointer = strchr((const char *)(context->subline), '#'); // locating color code
char color_code[8];
if (temp_pointer)
{
snprintf(color_code, sizeof(color_code), "%.6s", temp_pointer + 1); // obtained color code
}
else
{
color_code[0] = '\0';
}
char color_code[7];
strncpy(color_code, temp_pointer + 1, 6); // obtained color code
color_code[6] = '\0';
temp_pointer = strchr((const char *)(context->subline), '>'); // The content is in between <font ..> and </font>
if (temp_pointer)
{
// Copy the content between <font ..> and </font>
int content_len = end_index - (temp_pointer - (char *)(context->subline) + 1);
if (content_len > 0)
{
remaining = buf_size - final_len;
if ((size_t)content_len < remaining - 1)
{
memcpy(final + final_len, temp_pointer + 1, content_len);
final_len += content_len;
final[final_len] = '\0';
}
}
}
strncat(temp, temp_pointer + 1, end_index - (temp_pointer - (char *)(context->subline) + 1));
// adding font color tag
remaining = buf_size - final_len;
written = snprintf(final + final_len, remaining, "<style tts:backgroundColor=\"#FFFF00FF\" tts:color=\"%s\" tts:fontSize=\"18px\"/></span>", color_code);
if (written > 0 && (size_t)written < remaining)
final_len += written;
strcat(final, temp); // attaching to final sentence.
// finding remaining sentence and adding it
remaining = buf_size - final_len;
snprintf(final + final_len, remaining, "%s", (const char *)(context->subline) + end_index + 7);
sprintf(temp, "<style tts:backgroundColor=\"#FFFF00FF\" tts:color=\"%s\" tts:fontSize=\"18px\"/></span>", color_code);
strcat(final, temp); // adding font color tag
sprintf(temp, "%s", (const char *)(context->subline) + end_index + 7); // finding remaining sentence.
strcat(final, temp); // adding remaining sentence
}
}
else
{
// NO styling, writing as it is
snprintf(final, buf_size, "%s", (const char *)(context->subline));
strcpy(final, (const char *)(context->subline));
}
}
@@ -450,7 +386,7 @@ int write_cc_buffer_as_smptett(struct eia608_screen *data, struct encoder_ctx *c
write_wrapped(context->out->fh, context->encoded_crlf, context->encoded_crlf_length);
context->trim_subs = old_trim_subs;
snprintf(str, sizeof(str), " <style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\"/></span>\n </p>\n");
sprintf(str, " <style tts:backgroundColor=\"#000000FF\" tts:fontSize=\"18px\"/></span>\n </p>\n");
if (context->encoding != CCX_ENC_UNICODE)
{
dbg_print(CCX_DMT_DECODER_608, "\r%s\n", str);

Some files were not shown because too many files have changed in this diff Show More