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
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.
This commit is contained in:
@@ -35,8 +35,12 @@ pf-capture = { path = "../pf-capture" }
|
||||
openh264 = "0.9"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
# libavcodec (NVENC libav + VAAPI backends). `ffmpeg-sys-next` auto-detects the FFmpeg version.
|
||||
ffmpeg-next = "8"
|
||||
# libavcodec (NVENC libav + VAAPI backends). `ffmpeg-sys-next` auto-detects the FFmpeg version, so
|
||||
# this pin tracks the crate's own major (which shadows FFmpeg's): 9 = FFmpeg 9 (libavcodec 63,
|
||||
# libavutil 61). Arch shipped FFmpeg 9 on 2026-08-08 and every soname moved with it; the packaged
|
||||
# host must be BUILT against the FFmpeg it will run on, and packaging/arch/PKGBUILD now derives a
|
||||
# soname dep from that link so pacman can no longer walk an install across the break.
|
||||
ffmpeg-next = "9"
|
||||
libc = "0.2"
|
||||
# Vulkan bindings for the raw Vulkan-Video encode + PyroWave compute backends (feature-gated below;
|
||||
# the dep stays unconditional to mirror the host's Linux target — unused-but-declared is harmless).
|
||||
@@ -53,7 +57,7 @@ pyrowave-sys = { path = "../pyrowave-sys", optional = true }
|
||||
# NVENC (direct SDK, D3D11 input) + the shared D3D11/DXGI vocabulary via pf-frame.
|
||||
nvidia-video-codec-sdk = { version = "0.4", features = ["ci-check"], optional = true }
|
||||
# AMD (AMF) + Intel (QSV) hardware encode via libavcodec (behind `amf-qsv`; link-imports FFmpeg).
|
||||
ffmpeg-next = { version = "8", optional = true }
|
||||
ffmpeg-next = { version = "9", optional = true }
|
||||
# `libnvidia-encode`/`nvEncodeAPI64.dll` resolved at runtime; the NVENC status→cause table dlopen.
|
||||
libloading = "0.8"
|
||||
# Native Intel QSV (VPL): vendored static MIT dispatcher + bindgen'd C API, only under `qsv`.
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
//! NVENC encoder via `ffmpeg-next` (binds the system FFmpeg — `ffmpeg-sys-next` auto-detects the
|
||||
//! installed version, so this builds against FFmpeg 7.x/libavcodec 61 *or* 8.x/libavcodec 62;
|
||||
//! validated live on Ubuntu 26.04 (FFmpeg 8) and Bazzite F43 (FFmpeg 7.1)).
|
||||
//! installed version and emits a per-version cfg, so one source tree spans FFmpeg 7.x/libavcodec 61,
|
||||
//! 8.x/62 and 9.x/63; validated live on Ubuntu 26.04 (FFmpeg 8), Bazzite F43 (7.1) and CachyOS
|
||||
//! (FFmpeg 9). The `ffmpeg-next` MAJOR is a ceiling, not a target: 8.x refused anything past
|
||||
//! libavcodec 62, which is why Arch's FFmpeg 9 needed the crate bump and not just a rebuild.
|
||||
//! What a given package links is decided by the BUILDER's FFmpeg, so the soname bound that keeps an
|
||||
//! install honest is generated at package time — see packaging/arch/PKGBUILD.
|
||||
//!
|
||||
//! Input is a packed RGB/BGR CPU frame; `*_nvenc` accepts `rgb0`/`bgr0`/`rgba`/`bgra`
|
||||
//! directly and does the RGB→YUV conversion on the GPU, so the host stays off the
|
||||
|
||||
@@ -79,6 +79,13 @@ struct AVD3D11VADeviceContext {
|
||||
lock: *mut c_void, // void (*)(void*)
|
||||
unlock: *mut c_void, // void (*)(void*)
|
||||
lock_ctx: *mut c_void,
|
||||
// DELIBERATELY TRUNCATED: FFmpeg >=8 appends `UINT BindFlags; UINT MiscFlags;` here, FFmpeg 7.1
|
||||
// does not, and we build against both (Windows links the BtbN n7.1 tree, Linux the distro's 8 or
|
||||
// 9). Mirroring only the common prefix is what makes one definition correct for all three —
|
||||
// libav owns the allocation (av_hwdevice_ctx_alloc sizes it), and we only ever WRITE `device` at
|
||||
// offset 0, so a short mirror can never read or write past what libav allocated. Adding the two
|
||||
// flags to match 8/9 would silently mis-describe the 7.1 build we actually ship on Windows.
|
||||
// The per-pool AVD3D11VAFramesContext.BindFlags below — which we DO set — exists in all three.
|
||||
}
|
||||
|
||||
/// `AVD3D11VAFramesContext` (libavutil/hwcontext_d3d11va.h) — mirrored. `BindFlags`/`MiscFlags`
|
||||
@@ -95,10 +102,17 @@ struct AVD3D11VAFramesContext {
|
||||
// Hand-written mirrors of libav's `AVD3D11VADeviceContext` / `AVD3D11VAFramesContext`
|
||||
// (hwcontext_d3d11va.h) — `ffmpeg-sys-next` binds neither, and we WRITE `device` / `bind_flags`
|
||||
// through them, so a wrong offset is silent corruption of libav's context rather than a compile
|
||||
// error. ⚠ These two structs are duplicated in the other crate that talks to the same libav
|
||||
// contexts (pf-encode's `ffmpeg_win.rs` and pf-client-core's `video_d3d11.rs`); they must agree
|
||||
// with libav AND with each other, and these assertions are what makes a drift in either a build
|
||||
// failure instead of a runtime mystery.
|
||||
// error.
|
||||
//
|
||||
// ⚠ KNOW WHAT THESE ASSERTIONS DO AND DO NOT BUY YOU. They pin OUR layout, not libav's, so they
|
||||
// turn an accidental edit to the structs above into a build failure — but nothing here reads
|
||||
// hwcontext_d3d11va.h, so a field libav inserts upstream still sails straight through, and a green
|
||||
// build is not evidence. Both layouts were therefore re-checked BY HAND against FFmpeg 7.1, 8.1.2
|
||||
// and 9.0 during the 8 -> 9 bump (2026-08-08): `AVD3D11VAFramesContext` is byte-identical in all
|
||||
// three, and `AVD3D11VADeviceContext` gained two trailing UINTs in 8 that 7.1 lacks — which is
|
||||
// exactly why the struct above stops at the common prefix. Re-check by hand on the next FFmpeg
|
||||
// major. (An older note here claimed these were duplicated in pf-client-core's `video_d3d11.rs`;
|
||||
// that copy went away with the client's FFmpeg in M10, so this is now the only definition.)
|
||||
const _: () = {
|
||||
use std::mem::{offset_of, size_of};
|
||||
type P = *mut c_void;
|
||||
|
||||
Reference in New Issue
Block a user