1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
# CI for ez-ffmpeg.
#
# Every job runs 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). First build per lane compiles FFmpeg
# (~10-20 min); caches make subsequent runs fast.
#
# 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 FFmpeg lane. Two lanes run once, not per FFmpeg:
#
# * `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 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).
#
# All jobs are enforced gates.
name: CI
on:
push:
branches:
pull_request:
branches:
env:
CARGO_TERM_COLOR: always
RUST_BACKTRACE: 1
jobs:
build:
name: Build & Clippy (FFmpeg ${{ matrix.ffmpeg }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- ffmpeg: "8.1"
ffmpeg-features: "--features ffmpeg-sys-next/build"
- ffmpeg: "7.1"
ffmpeg-features: ""
steps:
- uses: actions/checkout@v4
- 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)"
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- name: Cache FFmpeg 7.1 install
if: matrix.ffmpeg == '7.1'
uses: actions/cache@v4
with:
path: ~/ffmpeg71
key: ${{ runner.os }}-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 registry and target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-ffmpeg${{ matrix.ffmpeg }}-${{ 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 --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
# 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
test:
name: Test (FFmpeg ${{ matrix.ffmpeg }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- ffmpeg: "8.1"
ffmpeg-features: "--features ffmpeg-sys-next/build"
- 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@v4
- 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@stable
- name: Cache FFmpeg 7.1 install
if: matrix.ffmpeg == '7.1'
uses: actions/cache@v4
with:
path: ~/ffmpeg71
key: ${{ runner.os }}-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 registry and target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-ffmpeg${{ matrix.ffmpeg }}-${{ hashFiles('**/Cargo.toml') }}
- name: Test
run: cargo test --lib --features async,rtmp,flv,subtitle,wgpu ${{ matrix.ffmpeg-features }}
docs:
name: Docs (docsrs cfg compile)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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@stable
- name: Cache cargo registry and target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-docs-${{ 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
asan:
name: ASAN (UAF, FFmpeg 8.1)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- 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@nightly
- name: Cache cargo registry and target
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-asan-${{ 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. GPU features are omitted here (the test job
# runs wgpu's self-skipping suite; opengl needs a display either way) —
# the lifecycle/teardown suites 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 \
--test bsf --test mux_teardown --test lifecycle \
--test start_failure_join --test start_panic_unwind \
--test shortest --test frame_pipeline_hardening