Arch could upgrade FFmpeg out from under the host and brick it — and the host now builds against FFmpeg 9 #108
Merged
enricobuehler
merged 2 commits from 2026-08-08 00:41:36 +00:00
worktree-ffmpeg9-support into main
2
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
deeb8b6700 |
feat(pf-encode): build against FFmpeg 9
apple / swift (pull_request) Successful in 1m53s
apple / screenshots (pull_request) Skipped
windows / build (aarch64-pc-windows-msvc) (pull_request) Successful in 2m34s
ci / web (pull_request) Successful in 2m32s
ci / docs-site (pull_request) Successful in 1m25s
ci / bun-nix (pull_request) Successful in 26s
windows / build (x86_64-pc-windows-msvc) (pull_request) Successful in 3m23s
android / android (pull_request) Successful in 6m47s
ci / rust-arm64 (pull_request) Successful in 8m49s
nix / flake (pull_request) Failing after 16m7s
ci / rust (pull_request) Successful in 23m39s
ffmpeg-next 8.1.0 could not accept FFmpeg 9 at all: ffmpeg-sys-next's version probe
covered avcodec majors 56..62 (the range is exclusive of its end), so libavcodec 63 fell
outside what it knew how to bind. 9.0.0 widens that to 56..63, which is what actually
unblocks Arch. Bump both pins — the unconditional Linux dep and the optional Windows
amf-qsv one — and the lock with them.
No API drift to fix. The crate major is a CEILING, not a target: one source tree still
spans FFmpeg 7.x/libavcodec 61, 8.x/62 and 9.x/63 via per-version cfgs, and every wrapper
symbol the NVENC-libav, VAAPI and amf-qsv backends name survives 8.1.0 -> 9.0.0
unchanged. The three hand-written #[repr(C)] hwcontext mirrors are the parts no compiler
checks, so they were re-read against the real headers rather than trusted:
AVCUDADeviceContext and AVD3D11VAFramesContext are byte-identical across 7.1/8/9, and
AVD3D11VADeviceContext gained two trailing UINTs in 8 that 7.1 lacks — which is why that
mirror deliberately stops at the common prefix, and why its assertions now say what they
do and do not buy you. They pin our layout, not libav's; a green build is not evidence.
The CI image is the step that makes this reach users. arch.yml deliberately runs no -Syu
("the image's snapshot IS the build environment"), so the builder stayed frozen on ffmpeg
8 no matter what Arch shipped, and a canary built from that snapshot could not satisfy the
soname dep the PKGBUILD now derives. Re-keying ci/ rebuilds it against ffmpeg 9.
Ubuntu and Windows deliberately stay put: the noble .deb bundles its own FFmpeg 8 behind
an rpath and strips the libav sonames from its Depends, and Windows bundles BtbN DLLs into
the signed installer — neither is exposed to the break, BtbN publishes no FFmpeg 9 build,
and moving either would re-qualify an encode stack to buy nothing.
Verified end to end on 192.168.1.21 (CachyOS, system ffmpeg 2:9.0-5, RTX 5070 Ti): host
builds clean and links libavcodec.so.63/libavutil.so.61/libavfilter.so.12/libswscale.so.10
with no unresolved sonames; the ffmpeg-8 compat shim is gone and the service runs with
NRestarts=0 and answers 401 on :47990; pf-encode's 67 tests pass; and a live synthetic
encode drives real NVENC hardware through FFmpeg 9's libavcodec to a decodable 1080p HEVC
stream (180/180 frames, FEC loopback 0 mismatches) with libavcodec.so.63 and
libnvidia-encode both mapped into the encoding process.
|
||
|
|
b1e0525872 |
fix(packaging/arch): pacman could upgrade FFmpeg out from under the host and brick it
`depends=('ffmpeg' ...)` carried no version bound, and pacman is the only one of our
packaging formats that does not derive dependencies from ELF DT_NEEDED — rpm
auto-generates `libavcodec.so.62()(64bit)`, dpkg-shlibdeps emits `libavcodec62`, nix
pins the closure. So when Arch shipped ffmpeg 2:9.0-5 on 2026-08-08 and every soname
moved (libavutil .60->.61, libavcodec .62->.63, libavfilter .11->.12, libavdevice
.62->.63, libswscale .9->.10), a plain `pacman -Syu` walked every Arch/CachyOS install
straight across the break. The result is not a crash we can log: the dynamic loader
cannot start the binary at all, so it is exit 127 *before* main() in a systemd restart
loop, and because punktfunk-web is a separate bun service with no libav linkage it keeps
serving happily while :47990 has nothing listening — which reads as "the mgmt API is
broken" rather than "the host is not running". `ldd /usr/bin/punktfunk-host | grep
"not found"` is the one-line diagnosis.
Depend on the sonames instead of the package. Arch's ffmpeg declares the matching
`provides=(libavcodec.so=63-64 ...)`, and makepkg rewrites each bare `libfoo.so` listed
in depends into `libfoo.so=<soname>-<arch>` by reading the built binary's DT_NEEDED, so
the bound tracks whatever FFmpeg the builder linked against with nothing to hand-maintain
across the next bump. pacman now refuses the ffmpeg upgrade rather than bricking the
install. A hand-written `ffmpeg<2:9` would have gone stale on the very next major; not
bundling FFmpeg the way the .deb does, because that exists only because Ubuntu 24.04 LTS
is frozen on 6.1 and can never satisfy the dep, while rolling Arch always ships a current
one.
Verified on a real ffmpeg-9 box (192.168.1.21): the built package records
libavcodec.so=63-64, libavutil.so=61-64, libavfilter.so=12-64, libavdevice.so=63-64 and
libswscale.so=10-64, exactly matching DT_NEEDED, with the two libs --as-needed drops left
bare and satisfied by any ffmpeg.
The new arch.yml step asserts that expansion actually happened. If it ever stops — Arch
dropping the soname provides, someone tidying the entries out of depends — the dep
silently degrades to an unversioned name that any ffmpeg satisfies, which is exactly the
state that caused this, and it is invisible in a green build until a box bricks weeks later.
|