# CI for ez-ffmpeg.
#
# Linux build and test jobs run on both native GitHub-hosted architectures
# (x64 and ARM64) in a two-lane FFmpeg matrix, because the crate supports
# linking either major and the two lanes are provisioned differently:
#
# * ffmpeg 8.1 lane — `--features ffmpeg-sys-next/build`: the sys crate's
# build script git-clones FFmpeg `release/8.1` (the branch is hardcoded to
# the crate's own major.minor) and compiles/statically links it. This is
# the same mechanism the pre-8 CI used, which then produced FFmpeg 7.1 —
# after the dependency bump the SAME flag silently produces 8.1, which is
# exactly why the 7.x lane below must NOT use it.
#
# * ffmpeg 7.1 lane — explicit from-source build of FFmpeg `release/7.1`
# (shared libs, default components, no external deps) installed under
# $HOME/ffmpeg71 and linked via pkg-config. This proves the bump keeps
# FFmpeg 7.x users working: ffmpeg-sys-next 8.1 probes the installed
# headers and only enables cfg flags up to ffmpeg_7_1.
#
# Ubuntu's apt FFmpeg (6.1 on 24.04) matches neither lane, so it is never
# installed; the apt packages below are just the FFmpeg build toolchain
# (nasm/yasm/clang/pkg-config/zlib). The 7.1 install is cached; the 8.1 lane
# rebuilds FFmpeg per job because its source-build helper tunes for the current
# runner CPU.
#
# Cargo caches deliberately keep only registry and git sources, never `target`.
# ffmpeg-sys-next's native source build adds `-march=native -mtune=native`, and
# GitHub can schedule the same runner architecture on different CPU generations;
# restoring those compiled artifacts can otherwise terminate tests with SIGILL.
#
# Native platform coverage outside Linux runs both FFmpeg lanes on every
# GitHub-hosted architecture: Apple Silicon macOS, Windows x64, and Windows
# ARM64. macOS builds FFmpeg 8.1 from source and uses Homebrew's versioned
# FFmpeg 7 formula; Windows checks out pinned vcpkg revisions whose ports carry
# FFmpeg 8.1.2 and 7.1.2. These jobs build every functional non-static feature,
# compile the corresponding test trees, and run the CPU-only library suite.
#
# GPU-dependent code: the `opengl` frame filter needs a real GPU/display and
# its tests fail headless ("Failed to create Surfman connection"), so the
# `test` job omits that feature; the `build` job still compiles it (via
# `--all-features`) so it stays type-checked. The `wgpu` feature runs in the
# test job: its GPU oracles self-skip without an adapter while its CPU-only
# gates (offline naga shader validation, layout pins, builders) run for real.
# The macOS-only `videotoolbox` scheduler tests are
# `#[cfg(target_os = "macos")]`, so they do not run on Linux.
#
# The `build` job (all-features build + a default-features build + clippy) and
# the `test` job run per Linux architecture and FFmpeg lane. The x64/7.1 build
# lane additionally checks MSRV 1.80 with the `http-input` feature (that lane
# links a cached shared FFmpeg via pkg-config, so the old-toolchain check
# compiles Rust only instead of rebuilding FFmpeg from source). Four
# specialized jobs run once on Linux x64:
#
# * `docs` — compiles the crate under `--cfg docsrs` (`DOCS_RS=1`), which stubs
# ez-ffmpeg's own FFI call sites; the gate catches docsrs-stub compile gaps.
# ffmpeg-sys-next still needs a real FFmpeg for its bindings, so this job builds
# one via `ffmpeg-sys-next/build` (not a literal docs.rs run — docs.rs has no
# network to clone FFmpeg — but it exercises the same `--cfg docsrs` paths).
# * `asan` — AddressSanitizer over the library unit tests and the
# lifecycle/teardown integration tests, guarding the use-after-free fixes.
# FFmpeg is statically linked via ffmpeg-sys-next/build; ASan's interceptors
# see its alloc/free, so a UAF where instrumented Rust touches FFmpeg-freed
# memory is caught (one entirely inside un-instrumented FFmpeg C is not
# reliably covered — those regressions are Rust-side here).
# * `lgpl-contract` — Ubuntu x64, FFmpeg n8.1.2 shared libraries built with
# `--disable-gpl --disable-nonfree --disable-version3 --disable-autodetect`.
# Hard-asserts the documented LGPL-minimum matrix (no libx264 / cropdetect,
# native AAC/MJPEG/MPEG-4 paths). Contract tests are `#[ignore]` in the
# crate and only this job runs them with `--ignored`.
# * `audit` — dependency advisory gate. Cargo.lock is intentionally
# untracked, so the job resolves the graph fresh (`cargo generate-lockfile`)
# and audits the resolved versions against the RUSTSEC advisory database;
# vulnerabilities fail the job, informational advisories only warn
# (cargo-audit's default verdict).
#
# One specialized job runs on the arm64 runner:
#
# * `armhf` — 32-bit ARM build + test inside an arm32v7 Debian container,
# executing natively as AArch32 EL0 code (no QEMU). The only lane where
# `target_pointer_width = "32"` code paths actually run. See the job's
# own comment for the fleet caveat.
#
# All jobs are enforced gates.
name: CI
on:
push:
branches: [ main ]
pull_request:
branches: [ main ]
# Every job only reads the repository; nothing writes back through the token.
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
build:
name: Build & Clippy (${{ matrix.arch }}, FFmpeg ${{ matrix.ffmpeg }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
arch: x64
ffmpeg: "8.1"
ffmpeg-features: "--features ffmpeg-sys-next/build"
- runner: ubuntu-24.04
arch: x64
ffmpeg: "7.1"
ffmpeg-features: ""
- runner: ubuntu-24.04-arm
arch: arm64
ffmpeg: "8.1"
ffmpeg-features: "--features ffmpeg-sys-next/build"
- runner: ubuntu-24.04-arm
arch: arm64
ffmpeg: "7.1"
ffmpeg-features: ""
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
# The 8.1 lane git-clones and statically builds FFmpeg inside target/
# alongside the crate's own build tree, and the sum has exhausted the
# stock image's free disk. Drop preinstalled toolchains this job never
# touches (~25 GiB) before the first build.
- name: Free disk space (from-source FFmpeg lane)
if: matrix.ffmpeg == '8.1'
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force >/dev/null 2>&1 || true
df -h /
- name: Install FFmpeg build toolchain
run: |
sudo apt-get update
sudo apt-get install -y \
nasm yasm clang pkg-config make git zlib1g-dev
echo "nasm: $(nasm --version | head -1)"
# Pinned to a commit, so the ref no longer names the toolchain; the
# explicit `toolchain` input preserves the original selection (same for
# every rust-toolchain step below).
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
components: clippy
- name: Cache FFmpeg 7.1 install
if: matrix.ffmpeg == '7.1'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/ffmpeg71
key: ${{ runner.os }}-${{ runner.arch }}-ffmpeg71-shared-v1
- name: Build FFmpeg 7.1 from source (if not cached)
if: matrix.ffmpeg == '7.1'
run: |
if [ ! -f "$HOME/ffmpeg71/lib/pkgconfig/libavcodec.pc" ]; then
git clone --depth=1 -b release/7.1 https://github.com/FFmpeg/FFmpeg ffmpeg-71-src
cd ffmpeg-71-src
./configure --prefix="$HOME/ffmpeg71" \
--disable-programs --disable-doc \
--disable-static --enable-shared
make -j"$(nproc)"
make install
fi
- name: Point pkg-config at FFmpeg 7.1
if: matrix.ffmpeg == '7.1'
run: |
echo "PKG_CONFIG_PATH=$HOME/ffmpeg71/lib/pkgconfig" >> "$GITHUB_ENV"
echo "LD_LIBRARY_PATH=$HOME/ffmpeg71/lib" >> "$GITHUB_ENV"
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
- name: Build
run: cargo build --all-features ${{ matrix.ffmpeg-features }} --verbose
# Compiles every #[cfg(test)] tree under --all-features WITHOUT running:
# the opengl/wgpu suites need a GPU/display to RUN, but their tests carry
# compile-time signature pins (e.g. the opengl typed-error fn-pointer
# bindings) that only a test build type-checks. `--no-run` is headless-safe.
- name: Build tests (all features, no run)
run: cargo test --lib --tests --all-features ${{ matrix.ffmpeg-features }} --no-run
# The crate's default feature set is empty (`default = []`), yet every job
# here builds `--all-features`; this proves the no-feature build and its cfg
# gates still compile. The ffmpeg-features flag only controls how FFmpeg is
# provisioned, so "default features" still carries it.
- name: Build (default features)
run: cargo build ${{ matrix.ffmpeg-features }} --verbose
# Design §1.3: the empty default feature set must not activate the
# http-input stack (reqwest/tokio/QUIC). Cargo.lock is untracked, so
# this is a denylist rather than a golden tree.
- name: Default tree excludes http-input crates
run: |
tree="$(cargo tree --no-default-features --edges normal --prefix none --format '{p}')"
if printf '%s\n' "$tree" | grep -E '^(reqwest|tokio|quinn|quinn-proto|quinn-udp|hyper-rustls) '; then
echo "default feature tree must not activate http-input crates" >&2
printf '%s\n' "$tree" | grep -E '^(reqwest|tokio|quinn|quinn-proto|quinn-udp|hyper-rustls) ' >&2
exit 1
fi
# Safety-hygiene lints surface here. Not `-D warnings` yet: the crate has a
# pre-existing unused-import / undocumented-unsafe backlog to clear first.
- name: Clippy
run: cargo clippy --all-features ${{ matrix.ffmpeg-features }} --lib
# MSRV gate: the dependency pins must keep MSRV 1.80 real, and
# `http-input` is the feature whose dependency tree pulled in the
# Edition-2024-risk crates. One lane suffices. Pinned to x64/7.1
# because that lane links the cached shared FFmpeg via pkg-config —
# the 1.80 check compiles Rust only; on an 8.1 lane the
# `ffmpeg-sys-next/build` build script would rebuild FFmpeg from
# source a second time under the old toolchain. Runs last in the job:
# the toolchain action switches the rustup default, and the explicit
# `+1.80.0` keeps the invocation unambiguous either way.
- name: Install Rust 1.80.0 (MSRV)
if: matrix.arch == 'x64' && matrix.ffmpeg == '7.1'
uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # 1.80.0
with:
toolchain: 1.80.0
- name: MSRV check (1.80, http-input)
if: matrix.arch == 'x64' && matrix.ffmpeg == '7.1'
run: cargo +1.80.0 check --no-default-features --features http-input
test:
name: Test (${{ matrix.arch }}, FFmpeg ${{ matrix.ffmpeg }})
runs-on: ${{ matrix.runner }}
strategy:
fail-fast: false
matrix:
include:
- runner: ubuntu-24.04
arch: x64
ffmpeg: "8.1"
ffmpeg-features: "--features ffmpeg-sys-next/build"
- runner: ubuntu-24.04
arch: x64
ffmpeg: "7.1"
ffmpeg-features: ""
- runner: ubuntu-24.04-arm
arch: arm64
ffmpeg: "8.1"
ffmpeg-features: "--features ffmpeg-sys-next/build"
- runner: ubuntu-24.04-arm
arch: arm64
ffmpeg: "7.1"
ffmpeg-features: ""
# Runs the full library suite MINUS the opengl feature, whose tests need a
# GPU/display (surfman connection creation fails headless). The wgpu
# feature IS included: its GPU oracles self-skip when no adapter exists,
# while its CPU-only gates — offline naga shader validation, uniform
# layout pins, builder checks — run for real. Media assets are committed
# as test.mp4.
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
# The 8.1 lane git-clones and statically builds FFmpeg inside target/
# alongside the crate's own build tree, and the sum has exhausted the
# stock image's free disk. Drop preinstalled toolchains this job never
# touches (~25 GiB) before the first build.
- name: Free disk space (from-source FFmpeg lane)
if: matrix.ffmpeg == '8.1'
run: |
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force >/dev/null 2>&1 || true
df -h /
- name: Install FFmpeg build toolchain
run: |
sudo apt-get update
sudo apt-get install -y \
nasm yasm clang pkg-config make git zlib1g-dev
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- name: Cache FFmpeg 7.1 install
if: matrix.ffmpeg == '7.1'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/ffmpeg71
key: ${{ runner.os }}-${{ runner.arch }}-ffmpeg71-shared-v1
- name: Build FFmpeg 7.1 from source (if not cached)
if: matrix.ffmpeg == '7.1'
run: |
if [ ! -f "$HOME/ffmpeg71/lib/pkgconfig/libavcodec.pc" ]; then
git clone --depth=1 -b release/7.1 https://github.com/FFmpeg/FFmpeg ffmpeg-71-src
cd ffmpeg-71-src
./configure --prefix="$HOME/ffmpeg71" \
--disable-programs --disable-doc \
--disable-static --enable-shared
make -j"$(nproc)"
make install
fi
- name: Point pkg-config at FFmpeg 7.1
if: matrix.ffmpeg == '7.1'
run: |
echo "PKG_CONFIG_PATH=$HOME/ffmpeg71/lib/pkgconfig" >> "$GITHUB_ENV"
echo "LD_LIBRARY_PATH=$HOME/ffmpeg71/lib" >> "$GITHUB_ENV"
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
- name: Test
run: cargo test --lib --test avoption_warnings --test build_probe --test builder_option_validation --test frame_export_test --test frame_export_uniform_test --test frame_export_audio_test --test hls_fmp4 --test video_writer --test rawvideo_seek_gating --test per_output_video_filter --test per_output_audio_filter --test codec_tag --test packet_sink --test packet_sink_negative --test packet_sink_teardown --test packet_sink_unwind --test cli_emit_hardening --test hdr_to_sdr_detection --test logger_reentrancy --test packet_sink_job_failure --test http_input --test crop_detection --test custom_io_hardening --test default_tree --features async,rtmp,flv,subtitle,wgpu,cli,http-input ${{ matrix.ffmpeg-features }}
# Crop parity smoke: native Rust Analysis vs optional GPL ffmpeg
# `-vf cropdetect`. Own step so the main Test line never grows
# `--ignored` (that would run every ignored test). Linux FFmpeg here
# is often program-less or LGPL-ish; the test then records the
# rust-only half and skips the executable half instead of failing.
# Not added to lgpl-contract `--ignored` tests: those assert absence
# of GPL pieces; native crop already runs via `--test crop_detection`.
- name: Crop parity bench (ignored 1080p smoke)
run: cargo test --release --test crop_parity_bench --features async,rtmp,flv,subtitle,wgpu,cli,http-input ${{ matrix.ffmpeg-features }} -- --ignored --exact rust_on_off_letterbox_1080_smoke
# The packet-sink examples are API surface: keep them compiling on the
# runtime lane (native-AAC cases in the suites above do real work even
# without libx264; x264 cases self-skip at runtime).
- name: Check packet-sink examples
run: cargo check --example packet_sink_avc --example packet_sink_aac_channel --example packet_sink_av_transport --example hdr_to_sdr --example http_input --features http-input ${{ matrix.ffmpeg-features }}
- name: Doctests
run: cargo test --doc --features async,rtmp,flv,subtitle,wgpu,cli,http-input ${{ matrix.ffmpeg-features }}
# aarch64 second-stage confirmation for the NAL start-code scan
# refactor (perf/nal-stride3-scan, C2). Its x86-64 win is established
# (paired same-round benchmark: 19 corpora, medians 1.01-3.3x, no pair
# below parity); ARM throughput was the one open item in the
# SHIP-TWO-STAGE disposition. This runs the in-repo #[ignore]
# micro-benchmark on the arm64 runner, prints the full same-round
# paired table (parent byte scan vs shipped stride scan) to the CI log
# for review via `gh run view --log`, then applies a lenient
# net-regression gate: nal_scan_parity_gate.py requires each corpus's
# pooled median paired ratio >= 0.85 on both walks. That floor sits
# below the x86 win (near-parity 1-2 byte-NAL corpora hover around 1.0
# even unpinned) so it will not flake, yet a breach means the stride
# scan is a material per-corpus loss on ARM and the aarch64 byte-scan
# fallback (#[cfg(target_arch = "aarch64")] over the parent byte
# finder) should be gated in before release tagging.
#
# Pinned to the 7.1 arm64 lane only: 7.1 links a cached shared FFmpeg,
# so the release build recompiles Rust only (8.1 rebuilds FFmpeg
# statically in release); the scanner is core and feature-independent,
# so no extra cargo features are needed; and x64 needs no run (the x86
# evidence is complete on the branch). Three invocations pool fifteen
# same-round rounds per corpus so the median stays stable on a shared
# runner (the near-parity tiny-NAL corpora are the noise-sensitive
# ones and this keeps their pooled median clear of the floor).
- name: NAL start-code scan aarch64 parity benchmark
if: matrix.arch == 'arm64' && matrix.ffmpeg == '7.1'
run: |
set -o pipefail
: > nal_scan_bench.log
for i in 1 2 3; do
cargo test --release --lib bench_nal_startcode_scan ${{ matrix.ffmpeg-features }} \
-- --ignored --nocapture | tee -a nal_scan_bench.log
done
python3 .github/scripts/nal_scan_parity_gate.py nal_scan_bench.log
cli-goldens:
# Strict semantic-golden lane for the cli feature: builds a
# version-matched FFmpeg WITH programs and libx264 (GPL is fine for CI
# testing; nothing GPL ships in the crate) and runs the golden suite with
# EZ_FFMPEG_CLI pointing at that binary, so a skip IS a failure. The
# verified-profile table only lists lines whose lane has passed here:
# 7.1 today. An 8.1 row is a matrix addition (same recipe, release/8.1
# branch and an ffmpeg81full cache key) once the build matrix carries it.
name: CLI goldens (FFmpeg ${{ matrix.ffmpeg }}, strict)
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- ffmpeg: "7.1"
branch: release/7.1
prefix: ffmpeg71full
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Install FFmpeg build toolchain
run: |
sudo apt-get update
sudo apt-get install -y \
nasm yasm clang pkg-config make git zlib1g-dev libx264-dev
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- name: Cache FFmpeg ${{ matrix.ffmpeg }} full install
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/${{ matrix.prefix }}
key: ${{ runner.os }}-${{ runner.arch }}-${{ matrix.prefix }}-shared-v1
- name: Build FFmpeg ${{ matrix.ffmpeg }} with programs (if not cached)
run: |
if [ ! -x "$HOME/${{ matrix.prefix }}/bin/ffmpeg" ]; then
git clone --depth=1 -b ${{ matrix.branch }} https://github.com/FFmpeg/FFmpeg ffmpeg-golden-src
cd ffmpeg-golden-src
./configure --prefix="$HOME/${{ matrix.prefix }}" \
--disable-doc \
--disable-static --enable-shared \
--enable-gpl --enable-libx264
make -j"$(nproc)"
make install
fi
- name: Point pkg-config at the golden FFmpeg
run: |
echo "PKG_CONFIG_PATH=$HOME/${{ matrix.prefix }}/lib/pkgconfig" >> "$GITHUB_ENV"
echo "LD_LIBRARY_PATH=$HOME/${{ matrix.prefix }}/lib" >> "$GITHUB_ENV"
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
- name: Build the pinned emitted examples
run: cargo build --features cli --examples
- name: Run the cli suite with strict goldens
run: |
EZ_FFMPEG_CLI="$HOME/${{ matrix.prefix }}/bin/ffmpeg" cargo test --features cli --lib cli::
# The `cli::` filter above cannot reach the frame-export byte-parity
# golden (`core::frame_export::color_goldens::default_tier_matches_cli_bytes`),
# whose gate compares libswscale builds and treats a skip as a failure
# under EZ_FFMPEG_CLI. The crate links the same install the strict
# binary comes from, so the parity comparison actually executes here.
- name: Run the frame-export goldens with the strict CLI
run: |
EZ_FFMPEG_CLI="$HOME/${{ matrix.prefix }}/bin/ffmpeg" cargo test --features cli --lib core::frame_export::color_goldens
- name: Emit-hardening integration tests
run: cargo test --features cli --test cli_emit_hardening
# RTMP loopback suite: real-TCP rml_rtmp watchers against the embedded
# RTMP server (late-joiner GOP replay, EOF status delivery, StreamBuilder
# port release, AVCC parser hardening). The publish leg re-encodes
# test.mp4 to H.264 because create_rtmp_input pins h264/aac, so it needs
# an FFmpeg with a working H.264 encoder — this lane's libx264 build is
# the only one in the matrix that has one: the from-source and vcpkg
# lanes carry no H.264 encoder, and the runners expose no V4L2 device or
# VideoToolbox session for the hardware fallbacks. The load-harness
# measurements in the same file stay #[ignore]d.
- name: RTMP loopback tests
run: cargo test --features rtmp --test rtmp_loopback
platform:
name: Platform (${{ matrix.platform }}, FFmpeg ${{ matrix.ffmpeg }})
runs-on: ${{ matrix.runner }}
timeout-minutes: 120
strategy:
fail-fast: false
matrix:
include:
- runner: macos-15
platform: macos-15-arm64
os: macos
target: aarch64-apple-darwin
ffmpeg: "8.1"
build_features: ffmpeg-sys-next/build-videotoolbox,async,opengl,wgpu,rtmp,flv,subtitle,cli,http-input
test_features: ffmpeg-sys-next/build-videotoolbox,async,rtmp,flv,subtitle,wgpu,cli,http-input
default_ffmpeg_features: --features ffmpeg-sys-next/build-videotoolbox
test_args: >-
--
--skip core::scheduler::ffmpeg_scheduler::tests::test_hwaccel
--skip core::scheduler::ffmpeg_scheduler::tests::test_to_stdout
- runner: macos-15
platform: macos-15-arm64
os: macos
target: aarch64-apple-darwin
ffmpeg: "7.1"
build_features: async,opengl,wgpu,rtmp,flv,subtitle,cli,http-input
test_features: async,rtmp,flv,subtitle,wgpu,cli,http-input
default_ffmpeg_features: ""
test_args: >-
--
--skip core::scheduler::ffmpeg_scheduler::tests::test_hwaccel
--skip core::scheduler::ffmpeg_scheduler::tests::test_to_stdout
- runner: windows-2025
platform: windows-2025-x64
os: windows
target: x86_64-pc-windows-msvc
ffmpeg: "8.1"
vcpkg_triplet: x64-windows
vcpkg_ref: b5229343b4b80264ed51e89c6a7dcd0cbe85e9cc
vcpkg_cache: b5229343-ffmpeg-8.1.2-v1
build_features: async,opengl,wgpu,rtmp,flv,subtitle,cli,http-input
test_features: async,rtmp,flv,subtitle,wgpu,cli,http-input
default_ffmpeg_features: ""
test_args: ""
- runner: windows-2025
platform: windows-2025-x64
os: windows
target: x86_64-pc-windows-msvc
ffmpeg: "7.1"
vcpkg_triplet: x64-windows
vcpkg_ref: 6070d5cb2f818921c10ccdf6f35f3415a2503369
vcpkg_cache: 6070d5cb-ffmpeg-7.1.2-p5-v1
build_features: async,opengl,wgpu,rtmp,flv,subtitle,cli,http-input
test_features: async,rtmp,flv,subtitle,wgpu,cli,http-input
default_ffmpeg_features: ""
test_args: ""
- runner: windows-11-arm
platform: windows-11-arm64
os: windows
target: aarch64-pc-windows-msvc
ffmpeg: "8.1"
vcpkg_triplet: arm64-windows
vcpkg_ref: b5229343b4b80264ed51e89c6a7dcd0cbe85e9cc
vcpkg_cache: b5229343-ffmpeg-8.1.2-v1
build_features: async,opengl,wgpu,rtmp,flv,subtitle,cli,http-input
test_features: async,rtmp,flv,subtitle,wgpu,cli,http-input
default_ffmpeg_features: ""
test_args: -- --test-threads=1
- runner: windows-11-arm
platform: windows-11-arm64
os: windows
target: aarch64-pc-windows-msvc
ffmpeg: "7.1"
vcpkg_triplet: arm64-windows
vcpkg_ref: 6070d5cb2f818921c10ccdf6f35f3415a2503369
vcpkg_cache: 6070d5cb-ffmpeg-7.1.2-p5-v1
build_features: async,opengl,wgpu,rtmp,flv,subtitle,cli,http-input
test_features: async,rtmp,flv,subtitle,wgpu,cli,http-input
default_ffmpeg_features: ""
test_args: -- --test-threads=1
env:
CARGO_BUILD_JOBS: 2
CARGO_INCREMENTAL: 0
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- name: Verify native Rust target
shell: bash
run: |
rustc -vV
test "$(rustc -vV | sed -n 's/^host: //p')" = "${{ matrix.target }}"
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
# ffmpeg@7 is keg-only and uses absolute Homebrew install names, so its
# shared libraries remain discoverable without DYLD_LIBRARY_PATH. The
# versioned formula tracks the latest 7.1 patch release.
- name: Install FFmpeg 7.1 with Homebrew
if: matrix.os == 'macos' && matrix.ffmpeg == '7.1'
shell: bash
env:
HOMEBREW_NO_AUTO_UPDATE: "1"
HOMEBREW_NO_INSTALL_CLEANUP: "1"
run: |
# GitHub's image may carry an unrelated untrusted aws/tap. Remove it
# before Homebrew runs diagnostics so the job stays annotation-free.
brew untap aws/tap >/dev/null 2>&1 || true
brew install ffmpeg@7
prefix="$(brew --prefix ffmpeg@7)"
version="$("$prefix/bin/ffmpeg" -version | sed -n 's/^ffmpeg version \([^ ]*\).*/\1/p')"
case "$version" in
7.1*) ;;
*) echo "Expected FFmpeg 7.1.x, found $version" >&2; exit 1 ;;
esac
echo "PKG_CONFIG_PATH=$prefix/lib/pkgconfig" >> "$GITHUB_ENV"
echo "$prefix/bin" >> "$GITHUB_PATH"
PKG_CONFIG_PATH="$prefix/lib/pkgconfig" pkg-config --modversion libavcodec
- name: Configure vcpkg
if: matrix.os == 'windows'
shell: pwsh
run: |
$root = $env:VCPKG_INSTALLATION_ROOT
$cache = Join-Path $env:GITHUB_WORKSPACE "vcpkg-bincache"
New-Item -ItemType Directory -Force -Path $cache | Out-Null
"VCPKG_ROOT=$root" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"VCPKG_DEFAULT_TRIPLET=${{ matrix.vcpkg_triplet }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"VCPKGRS_TRIPLET=${{ matrix.vcpkg_triplet }}" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"VCPKGRS_DYNAMIC=1" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"VCPKG_DEFAULT_BINARY_CACHE=$cache" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append
"$root\installed\${{ matrix.vcpkg_triplet }}\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: Cache vcpkg binary packages
if: matrix.os == 'windows'
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: vcpkg-bincache
key: ${{ runner.os }}-${{ runner.arch }}-vcpkg-${{ matrix.vcpkg_cache }}
- name: Install FFmpeg ${{ matrix.ffmpeg }} with vcpkg
if: matrix.os == 'windows'
shell: pwsh
run: |
git config --global --add safe.directory $env:VCPKG_ROOT
if ($LASTEXITCODE -ne 0) { throw "git config exited with code $LASTEXITCODE" }
git -C $env:VCPKG_ROOT fetch --depth=1 origin ${{ matrix.vcpkg_ref }}
if ($LASTEXITCODE -ne 0) { throw "git fetch exited with code $LASTEXITCODE" }
git -C $env:VCPKG_ROOT checkout --force FETCH_HEAD
if ($LASTEXITCODE -ne 0) { throw "git checkout exited with code $LASTEXITCODE" }
& "$env:VCPKG_ROOT\bootstrap-vcpkg.bat" -disableMetrics
if ($LASTEXITCODE -ne 0) { throw "bootstrap-vcpkg exited with code $LASTEXITCODE" }
& "$env:VCPKG_ROOT\vcpkg.exe" install "ffmpeg:${{ matrix.vcpkg_triplet }}" --clean-after-build
if ($LASTEXITCODE -ne 0) { throw "vcpkg install exited with code $LASTEXITCODE" }
$ffmpeg = & "$env:VCPKG_ROOT\vcpkg.exe" list | Select-String "^ffmpeg:"
if ($LASTEXITCODE -ne 0) { throw "vcpkg list exited with code $LASTEXITCODE" }
if (-not $ffmpeg) { throw "The FFmpeg vcpkg package was not installed" }
$expectedVersion = [regex]::Escape("${{ matrix.ffmpeg }}")
if ("$ffmpeg" -notmatch "\s$expectedVersion\.") {
throw "Expected FFmpeg ${{ matrix.ffmpeg }}.x, found: $ffmpeg"
}
$ffmpeg
# The crate-level `static` feature is intentionally omitted on Windows:
# these vcpkg triplets provide shared FFmpeg libraries. macOS uses the
# dependency's source-build feature for 8.1 and Homebrew's shared 7.1
# libraries for the compatibility lane. Every functional optional feature
# is type-checked on each native platform.
- name: Build all non-static features
run: cargo build --features "${{ matrix.build_features }}" --verbose
- name: Build all non-static feature tests
run: cargo test --lib --tests --features "${{ matrix.build_features }}" --no-run
# This keeps the crate's own default feature set empty while carrying only
# the dependency feature needed to provision source-built FFmpeg 8.1 on
# macOS. The system/vcpkg lanes pass an empty string here.
- name: Build default crate features
run: cargo build ${{ matrix.default_ffmpeg_features }} --verbose
# GPU-dependent features are compile-only above. GitHub's macOS runner
# also cannot create a VideoToolbox compression session, so its two
# hardware-encoder tests stay compile-only. The remaining suite exercises
# the platform scheduler, device, socket, RTMP, subtitle, and async paths.
# The cli suite is profile-aware: on linked 8.1 its execution tests
# assert the typed UnverifiedRuntimeProfile refusal, on the vcpkg 7.1
# lanes the semantic goldens skip deterministically (today no
# version-matched ffmpeg binary is on PATH, and even if one ever lands
# there the goldens' lenient gate skips every shape whose canonical
# command names an encoder this libx264-less link lacks; the
# in-process gates accept EncoderUnavailable on that link), and on the
# Homebrew 7.1 lane they run in full against the matching CLI. The
# emit-hardening tests type-check emitted programs with
# `rustc --emit=metadata`, needing no native linking.
#
# xnu regrows an unread TCP receive window past an explicitly pinned
# SO_RCVBUF (observed on these runners: pinned to 16384 at accept,
# grown back past 500k by the packet-sink wedge test's park — the
# test's own drain probes feed the autotuner), so a never-reading peer
# cannot hold a sender blocked under the 4 MiB default ceiling. Cap
# the ceilings to a bound the stream saturates within the test's
# first second. The bound must stay comfortably above the loopback
# MSS (16 KiB): at 16384 a drained window cannot re-announce itself
# and the wedge stages starve waiting for a refill that never comes.
- name: Cap TCP buffer autotuning (macOS)
if: matrix.os == 'macos'
run: |
sudo sysctl -w net.inet.tcp.autorcvbufmax=131072
sudo sysctl -w net.inet.tcp.autosndbufmax=131072
- name: Run CPU-only library tests
run: cargo test --lib --test avoption_warnings --test build_probe --test builder_option_validation --test frame_export_test --test frame_export_uniform_test --test frame_export_audio_test --test hls_fmp4 --test video_writer --test rawvideo_seek_gating --test per_output_video_filter --test per_output_audio_filter --test codec_tag --test packet_sink --test packet_sink_negative --test packet_sink_teardown --test packet_sink_unwind --test cli_emit_hardening --test hdr_to_sdr_detection --test logger_reentrancy --test packet_sink_job_failure --test http_input --test crop_detection --test custom_io_hardening --test default_tree --features "${{ matrix.test_features }}" ${{ matrix.test_args }}
# Homebrew ffmpeg@7 is GPL and typically ships cropdetect. Gate the
# ignored 1080p smoke here only (not Windows, not macOS 8.1 source
# builds) so the main CPU-only suite stays free of `--ignored`.
- name: Crop parity bench (ignored 1080p smoke)
if: matrix.os == 'macos' && matrix.ffmpeg == '7.1'
run: cargo test --release --test crop_parity_bench --features "${{ matrix.test_features }}" -- --ignored --exact rust_on_off_letterbox_1080_smoke
# Homebrew ffmpeg links libzimg, so this is the one hosted lane where
# the zscale tone-map E2E is guaranteed runnable. Escalate its skip to
# a failure here so the historical-default HDR backend cannot lose its
# only fleet-wide runtime coverage silently (the main suite above
# already ran these tests; this re-run only enforces the no-skip gate).
- name: HDR zscale tone-map must run (macOS Homebrew)
if: matrix.os == 'macos' && matrix.ffmpeg == '7.1'
env:
EZ_FFMPEG_ZSCALE_MUST_RUN: 1
run: cargo test --test hdr_to_sdr_detection --features "${{ matrix.test_features }}" zscale
- name: Check packet-sink examples
run: cargo check --example packet_sink_avc --example packet_sink_aac_channel --example packet_sink_av_transport --example hdr_to_sdr --example http_input --features "${{ matrix.test_features }}"
# 32-bit ARM (armhf) lane. GitHub offers no 32-bit hosted runner, but the
# arm64 runners' Neoverse N2 (Cobalt 100) cores execute AArch32 at EL0, so
# an armhf container runs natively — real 32-bit words, not QEMU. This is
# the only lane where target_pointer_width = "32" code actually executes:
# the RTMP poller-token generation split (u16 halves) was once inert on
# 32-bit precisely because nothing ran it. Trixie's armhf FFmpeg is 7.1.x,
# matching the 7.1 matrix lane, and its GPL libavcodec carries libx264,
# which lets the RTMP loopback suite publish for real.
#
# Fleet caveat: AArch32 EL0 is a property of the current Cobalt 100 fleet,
# not a documented GitHub guarantee. If the fleet ever moves to cores
# without AArch32 (e.g. Neoverse V2+), the container fails to start or the
# in-container canary trips — either way this job goes red loudly rather
# than silently testing the wrong width, and the lane then needs a QEMU
# fallback decision.
armhf:
name: Build & Test (armhf 32-bit, FFmpeg 7.1)
runs-on: ubuntu-24.04-arm
timeout-minutes: 90
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
# Host-side canary: a kernel built without 32-bit compat support — the
# likely shape of a fleet move to AArch32-less cores — refuses the
# PER_LINUX32 personality, so this turns an opaque container crash into
# a named diagnosis. lscpu's op-mode line ("32-bit, 64-bit") is the
# CPU-level signal. The authoritative ELF32 proof is the in-container
# canary at the top of armhf_lane.sh.
- name: Verify 32-bit compat on the host
run: |
lscpu | grep -i 'op-mode' || true
if ! setarch linux32 true 2>/dev/null; then
echo "Host kernel refuses 32-bit personality (fleet change?)" >&2
exit 1
fi
# Same key as every other lane: registry sources are architecture-
# independent, so the armhf container can reuse what the arm64 lanes
# populated. Mounted into the container below.
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
# The whole build+test body runs inside an armhf Debian container.
# Docker on the arm64 host runs arm32v7 images natively via AArch32.
# --platform pins the pull so a manifest-list mixup cannot hand us an
# arm64 image (the in-script getconf/dpkg canary double-checks).
- name: Build and test in an armhf container
run: |
mkdir -p "$HOME/.cargo/registry" "$HOME/.cargo/git"
docker run --rm --platform linux/arm/v7 \
-v "$GITHUB_WORKSPACE:/work" -w /work \
-v "$HOME/.cargo/registry:/root/.cargo/registry" \
-v "$HOME/.cargo/git:/root/.cargo/git" \
arm32v7/debian:trixie bash .github/scripts/armhf_lane.sh
docs:
name: Docs (docsrs cfg compile)
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Install FFmpeg build toolchain
run: |
sudo apt-get update
sudo apt-get install -y \
nasm yasm clang pkg-config make git zlib1g-dev
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
# `DOCS_RS=1` makes ez-ffmpeg's build.rs pass `--cfg docsrs`, compiling its FFI
# call sites as stubs; this gate catches a docsrs-stub compile gap (a fn that
# takes a `#[cfg(not(docsrs))]` type but is itself ungated). ffmpeg-sys-next's
# build.rs does NOT honor DOCS_RS, so it still needs a real FFmpeg for its
# bindings; `ffmpeg-sys-next/build` compiles one from source (the runner has
# the network docs.rs lacks), keeping the job self-contained. The features
# otherwise match `[package.metadata.docs.rs]`. Not `-D warnings`: docs.rs does
# not, and the crate has a pre-existing intra-doc-link backlog (a separate
# task); this lane is the stub-compile gate, not doc-link cleanup.
- name: cargo doc (docsrs cfg)
env:
DOCS_RS: "1"
run: cargo doc --no-deps --features ffmpeg-sys-next/build,async,opengl,rtmp,flv,subtitle,wgpu,cli,http-input
asan:
name: ASAN (UAF, FFmpeg 8.1)
runs-on: ubuntu-24.04
timeout-minutes: 30
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Install FFmpeg build toolchain
run: |
sudo apt-get update
sudo apt-get install -y \
nasm yasm clang pkg-config make git zlib1g-dev
- uses: dtolnay/rust-toolchain@7c8d7d138f5c09cef361f8214cf96882cd029cdb # nightly
with:
toolchain: nightly
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
# `-Zsanitizer=address` instruments the target crates' Rust code. The explicit
# `--target` scopes the sanitizer to those crates and off the host build
# scripts / proc-macros; std is reused un-instrumented from the sysroot (so no
# `-Zbuild-std`), which suffices because the UAF regressions here are detected
# on the Rust side. `detect_leaks=0` because FFmpeg's one-time global
# allocations are not leaks. `--lib` runs the library unit tests under the
# same instrumentation as the lifecycle/teardown integration suites. GPU
# features are omitted here (the test job runs wgpu's self-skipping suite;
# opengl needs a display either way) — the tests under ASAN do not need
# them.
- name: ASAN UAF tests
env:
RUSTFLAGS: "-Zsanitizer=address"
RUSTDOCFLAGS: "-Zsanitizer=address"
ASAN_OPTIONS: "detect_leaks=0"
run: |
cargo +nightly test --target x86_64-unknown-linux-gnu \
--features ffmpeg-sys-next/build,async,rtmp,flv,subtitle \
--lib \
--test bsf --test mux_teardown --test lifecycle \
--test start_failure_join --test start_panic_unwind \
--test shortest --test frame_pipeline_hardening \
--test build_probe --test frame_export_test \
--test frame_export_uniform_test --test frame_export_audio_test \
--test video_writer --test packet_sink_teardown --test packet_sink_unwind \
--test logger_reentrancy --test custom_io_hardening
# Explicit LGPL-minimum contract. Existing Linux 8.1/7.1 lanes already build
# FFmpeg without --enable-gpl, but they skip x264-shaped tests instead of
# asserting the absence. This job pins n8.1.2, uses an exact cache key (no
# restore-keys), and runs #[ignore] contract binaries that must not skip.
lgpl-contract:
name: LGPL contract (Ubuntu x64, FFmpeg 8.1.2)
runs-on: ubuntu-24.04
timeout-minutes: 90
env:
FFMPEG_PREFIX: /home/runner/ffmpeg-lgpl-8.1.2
FFMPEG_SHA: 38b88335f99e76ed89ff3c93f877fdefce736c13
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- name: Install FFmpeg build toolchain
run: |
sudo apt-get update
sudo apt-get install -y \
gcc nasm yasm clang pkg-config make git fonts-dejavu-core
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- name: Define the LGPL profile
id: lgpl-profile
shell: bash
run: |
printf '%s\n' \
--disable-gpl \
--disable-nonfree \
--disable-version3 \
--disable-autodetect \
--disable-doc \
--disable-debug \
--disable-static \
--enable-shared \
--enable-pic \
--enable-pthreads \
--disable-ffplay \
--disable-ffprobe \
> "$RUNNER_TEMP/ffmpeg-lgpl.flags"
echo "flags_hash=$(sha256sum "$RUNNER_TEMP/ffmpeg-lgpl.flags" | awk '{print $1}')" >> "$GITHUB_OUTPUT"
echo "compiler_major=$(gcc -dumpversion | cut -d. -f1)" >> "$GITHUB_OUTPUT"
- name: Cache the LGPL FFmpeg install
id: cache-ffmpeg-lgpl
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ${{ env.FFMPEG_PREFIX }}
key: ${{ runner.os }}-${{ runner.arch }}-ubuntu-24.04-ffmpeg-lgpl-8.1.2-38b88335-${{ steps.lgpl-profile.outputs.flags_hash }}-gcc${{ steps.lgpl-profile.outputs.compiler_major }}-v1
- name: Build FFmpeg 8.1.2 shared LGPL libraries
if: steps.cache-ffmpeg-lgpl.outputs.cache-hit != 'true'
shell: bash
run: |
git init "$RUNNER_TEMP/ffmpeg-src"
git -C "$RUNNER_TEMP/ffmpeg-src" remote add origin \
https://github.com/FFmpeg/FFmpeg.git
git -C "$RUNNER_TEMP/ffmpeg-src" fetch --depth=1 origin \
refs/tags/n8.1.2
git -C "$RUNNER_TEMP/ffmpeg-src" checkout --detach FETCH_HEAD
test "$(git -C "$RUNNER_TEMP/ffmpeg-src" rev-parse HEAD)" = "$FFMPEG_SHA"
mapfile -t configure_flags < "$RUNNER_TEMP/ffmpeg-lgpl.flags"
cd "$RUNNER_TEMP/ffmpeg-src"
./configure --prefix="$FFMPEG_PREFIX" "${configure_flags[@]}"
make -j"$(nproc)"
make install
{
echo "ffmpeg_sha=$FFMPEG_SHA"
echo "flags_hash=${{ steps.lgpl-profile.outputs.flags_hash }}"
echo "compiler_major=${{ steps.lgpl-profile.outputs.compiler_major }}"
} > "$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
LD_LIBRARY_PATH="$FFMPEG_PREFIX/lib" \
"$FFMPEG_PREFIX/bin/ffmpeg" -hide_banner -buildconf \
>> "$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest" 2>&1
- name: Validate the cached build manifest
shell: bash
run: |
test -f "$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
grep -Fx "ffmpeg_sha=$FFMPEG_SHA" \
"$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
grep -Fx \
"flags_hash=${{ steps.lgpl-profile.outputs.flags_hash }}" \
"$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
grep -Fx \
"compiler_major=${{ steps.lgpl-profile.outputs.compiler_major }}" \
"$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
grep -F -- "--disable-gpl" \
"$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
grep -F -- "--disable-nonfree" \
"$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
grep -F -- "--disable-version3" \
"$FFMPEG_PREFIX/ez-ffmpeg-lgpl-manifest"
- name: Link against the LGPL profile
run: |
echo "PKG_CONFIG_PATH=$FFMPEG_PREFIX/lib/pkgconfig" >> "$GITHUB_ENV"
echo "LD_LIBRARY_PATH=$FFMPEG_PREFIX/lib" >> "$GITHUB_ENV"
echo "$FFMPEG_PREFIX/bin" >> "$GITHUB_PATH"
- name: Record the linked FFmpeg configuration
run: |
ffmpeg -hide_banner -buildconf
ldd "$FFMPEG_PREFIX/bin/ffmpeg"
- name: Cache cargo sources
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: |
~/.cargo/registry
~/.cargo/git
key: ${{ runner.os }}-cargo-sources-v1-${{ hashFiles('**/Cargo.toml') }}
- name: Run LGPL-compatible existing suites
env:
EZ_FFMPEG_SWSCALE_PERCEPTUAL_MUST_RUN: "1"
EZ_FFMPEG_HDR_PIXEL_ORACLE_MUST_RUN: "1"
run: >-
cargo test
--lib
--test builder_option_validation
--test frame_export_test
--test frame_export_uniform_test
--test frame_export_audio_test
--test hls_fmp4
--test video_writer
--test subtitle_chain
--test packet_sink
--test packet_sink_negative
--test packet_sink_teardown
--test packet_sink_unwind
--test packet_sink_job_failure
--test crop_detection
--test hdr_to_sdr_detection
--test http_input
--test custom_io_hardening
--test default_tree
--features async,rtmp,flv,subtitle,cli,http-input
- name: Run the explicit LGPL contract
run: >-
cargo test
--test lgpl_capabilities
--test lgpl_expected_failures
--test lgpl_media_paths
--features async,rtmp,flv,subtitle,cli
-- --ignored --nocapture
# Dependency advisory gate. Cargo.lock is intentionally untracked, so every
# run resolves the dependency graph fresh and audits those resolved versions
# against the RUSTSEC advisory database. cargo-audit's default verdict is
# the gate: vulnerabilities fail, informational advisories (unmaintained /
# unsound / yanked) only warn. The binary is version-pinned, built from
# crates.io source with its own committed lockfile (--locked), and cached
# keyed on that version, so no additional third-party actions are involved.
# Lockfile resolution runs no build scripts, so the job needs no FFmpeg.
audit:
name: Audit (dependency advisories)
runs-on: ubuntu-24.04
timeout-minutes: 30
env:
CARGO_AUDIT_VERSION: 0.22.2
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
- uses: dtolnay/rust-toolchain@4360b52568e2003a75bf9bc1d59f33a8e3fc893c # stable
with:
toolchain: stable
- name: Cache cargo-audit binary
id: cargo-audit-bin
uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6
with:
path: ~/.cargo/bin/cargo-audit
key: ${{ runner.os }}-${{ runner.arch }}-cargo-audit-${{ env.CARGO_AUDIT_VERSION }}
- name: Install cargo-audit
if: steps.cargo-audit-bin.outputs.cache-hit != 'true'
run: cargo install cargo-audit --locked --version "$CARGO_AUDIT_VERSION"
- name: Audit dependency advisories
run: |
cargo generate-lockfile
cargo audit