chromium-fourier r2 + firefox-fourier 150.0.1 + KWIN_PIVOT.md
build and publish packages / distcc-avahi-aarch64 (push) Successful in 46s
build and publish packages / lmcp-any (push) Successful in 9s
build and publish packages / lmcp-debian (push) Successful in 4s
build and publish packages / claude-his-any (push) Successful in 7s
build and publish packages / ffmpeg-v4l2-request-aarch64 (push) Successful in 12m8s
build and publish packages / claude-his-debian (push) Successful in 5s

chromium-fourier:
- patch 3/3 nv12-external-oes-on-modifier-external-only.patch — adds
  NativePixmapEGLBinding::ModifierRequiresExternalOES helper, extends
  OzoneImageGLTexturesHolder::GetBinding to honor EGL external_only
  flag for NV12 dmabufs on panfrost / panthor. Validated on ohm
  (RK3566 hantro mainline 6.19.10): bbb_1080p30_h264.mp4 plays at
  34.7 % combined CPU vs ~131 % pre-patch baseline (~3.8x).
- PKGBUILD pkgrel 1->2, source array + sha256sums + prepare() hook for
  patch 4, patch numbering 1/2,2/2 -> 1/3,2/3,3/3.
- NEXT.md appended with 2026-04-28 section: patch 4 design, validation
  log, KWin GL_ALPHA bug pinpoint (preexisting since 2026-03-06,
  affects every wayland video client; unrelated to chromium-fourier),
  device-renumbering note (/dev/video1 = encoder post-reboot).
- KWIN_PIVOT.md: 4-phase plan to identify and patch KWin's
  glTexImage2D(internalFormat=GL_ALPHA) site, ohm-only test plan,
  scope discipline.
- patches/ now tracked (compiler-rt-adjust-paths, enable-v4l2,
  wayland-allow-direct-egl-gles2, nv12-external-oes); the dead-end
  chromeos-pipeline-bypass.patch removed.

firefox-fourier:
- 4 patches (gfxinfo v4l2 stateless fourccs, libwrapper hwdevice ctx,
  ffmpegvideo v4l2-request route, prefs v4l2-request default).
- PKGBUILD bumped to firefox 150.0.1, Arch toolchain glue patches
  layered in, mozconfig with --without-wasm-sandboxed-libraries for
  ALARM, package() launcher fix (rm -f symlink before cat > to avoid
  ENOENT through the dangling /usr/local symlink mach install drops).
- 150.0.1-1-aarch64.pkg.tar.zst built on boltzmann (95 MB), pending
  fresnel power-on for V4L2 stateless validation on RK3399.
This commit is contained in:
2026-04-28 12:02:18 +00:00
parent 7bb2fbeca9
commit 8756ce38be
15 changed files with 1711 additions and 60 deletions
+163
View File
@@ -0,0 +1,163 @@
# Maintainer: Markus Fritsche <mfritsche@reauktion.de>
#
# Firefox with V4L2 stateless (request API) hardware video decode
# unlocked for mainline-Linux Rockchip (RK3399 rkvdec, RK3566/RK3588
# hantro multiplanar, RK3588 rkvdec2). Sibling to chromium-fourier;
# same niche. No vendor MPP, no Mali blob, no panfork, no 5.10 BSP.
#
# Patch series adds 4 thin shims around upstream firefox (~+169 lines,
# zero deletions). Architecture: stateless decode rides libavcodec's
# v4l2_request hwaccel (AV_HWDEVICE_TYPE_DRM); no separate Mozilla V4L2
# decoder gets written. See ../../arch/firefox-fourier/PLAN.md for
# the full diagnosis. Mozilla bug 1969297.
pkgname=firefox-fourier
pkgver=150.0.1
pkgrel=1
pkgdesc='Firefox with V4L2 stateless HW video decode unlocked for mainline Linux Rockchip'
arch=('aarch64' 'x86_64')
url='https://www.mozilla.org/firefox'
license=('MPL-2.0')
depends=(
alsa-lib
at-spi2-core
cairo
dbus
ffmpeg
fontconfig
freetype2
gcc-libs
gdk-pixbuf2
glib2
glibc
gtk3
hicolor-icon-theme
libdrm
libpulse
libva
libxcb
libxkbcommon
mesa
nspr
nss
pango
pciutils
ttf-liberation
v4l-utils
)
makedepends=(
cbindgen
clang
imagemagick
inetutils
lld
llvm
mesa
nasm
nodejs
python
rust
unzip
wasi-compiler-rt
wasi-libc
yasm
zip
)
optdepends=(
'hunspell-en_us: spell checking, American English'
'libnotify: send notifications when downloads complete'
'pulseaudio: audio support'
)
provides=(firefox)
conflicts=(firefox)
options=('!emptydirs' '!strip')
source=(
"https://archive.mozilla.org/pub/firefox/releases/${pkgver}/source/firefox-${pkgver}.source.tar.xz"
'mozconfig'
# Arch's official firefox patches — toolchain glue for clang 22 +
# glibc 2.43 + Rust 1.95+. Picked up verbatim because we hit the same
# walls. arch-0001 (install-under-remoting) skipped — our launcher
# ships under /usr/bin/firefox-fourier with our own wrapper.
# https://gitlab.archlinux.org/archlinux/packaging/packages/firefox
'arch-0002-Bug-2033279-Make-enable-rust-simd-work-with-Rust-1.9.patch'
'arch-0003-Patch-glsl-optimizer-to-build-with-glibc-2.43.patch'
'arch-0004-Bug-2023597-Use-wasm32-wasip1-target-for-clang-22.1-.patch'
# firefox-fourier patches — V4L2 stateless decode unlock.
'0001-gfxinfo-v4l2-stateless-fourccs.patch'
'0002-libwrapper-hwdevice-ctx-create.patch'
'0003-ffmpegvideo-v4l2-request-route.patch'
'0004-prefs-v4l2-request.patch'
)
sha256sums=('SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP' 'SKIP')
prepare() {
cd "${srcdir}/firefox-${pkgver}"
# Toolchain glue (Arch upstream) — apply BEFORE the fourier patches.
patch -Np1 -i "${srcdir}/arch-0002-Bug-2033279-Make-enable-rust-simd-work-with-Rust-1.9.patch"
patch -Np1 -i "${srcdir}/arch-0003-Patch-glsl-optimizer-to-build-with-glibc-2.43.patch"
patch -Np1 -i "${srcdir}/arch-0004-Bug-2023597-Use-wasm32-wasip1-target-for-clang-22.1-.patch"
# Fourier patches — order matters; see ../PLAN.md for rationale.
patch -Np1 -i "${srcdir}/0001-gfxinfo-v4l2-stateless-fourccs.patch"
patch -Np1 -i "${srcdir}/0002-libwrapper-hwdevice-ctx-create.patch"
patch -Np1 -i "${srcdir}/0003-ffmpegvideo-v4l2-request-route.patch"
patch -Np1 -i "${srcdir}/0004-prefs-v4l2-request.patch"
cp "${srcdir}/mozconfig" .mozconfig
}
build() {
cd "${srcdir}/firefox-${pkgver}"
# Arch's makepkg.conf injects -fexceptions into CFLAGS/CXXFLAGS by
# default for hardening. Mozilla's STL wrappers refuse to compile
# with exceptions enabled (#error "STL code can only be used with
# -fno-exceptions"). Strip the offender before mach configure picks
# up the env. Same trick the upstream Arch firefox PKGBUILD uses.
CFLAGS="${CFLAGS//-fexceptions/}"
CXXFLAGS="${CXXFLAGS//-fexceptions/}"
export CFLAGS CXXFLAGS
export MOZ_NOSPAM=1
export MOZ_API_KEY_UNUSED=1
export MOZ_TELEMETRY_REPORTING=
export MOZ_REQUIRE_SIGNING=
export MACH_BUILD_PYTHON_NATIVE_PACKAGE_SOURCE=system
export PYTHON=/usr/bin/python
./mach configure
./mach build
}
package() {
cd "${srcdir}/firefox-${pkgver}"
DESTDIR="${pkgdir}" ./mach install
# Move mach's default /usr/local/* layout to /usr/* so we conflict
# with `firefox` cleanly and `provides=firefox` actually works.
# `cp -r` preserves the bin symlink (target lives in /usr/local) —
# delete it before staging the launcher so `cat >` doesn't follow a
# dangling symlink and ENOENT.
if [ -d "${pkgdir}/usr/local" ]; then
cp -r "${pkgdir}/usr/local/." "${pkgdir}/usr/"
rm -rf "${pkgdir}/usr/local"
fi
rm -f "${pkgdir}/usr/bin/firefox-fourier"
# Launcher script. mach's install drops the binary at
# /usr/lib/firefox-fourier/firefox-fourier (a small bash launcher) plus
# firefox-fourier-bin alongside; we exec the launcher.
cat > "${pkgdir}/usr/bin/firefox-fourier" <<'LAUNCHER'
#!/bin/bash
# firefox-fourier launcher — V4L2 stateless HW decode path defaults.
# Patch 4/4 already defaults media.ffmpeg.v4l2-request.enabled=true on
# Linux; the env vars below cover the platform-detection bits firefox
# still consults at startup.
export MOZ_ENABLE_WAYLAND="${MOZ_ENABLE_WAYLAND:-1}"
export MOZ_X11_EGL="${MOZ_X11_EGL:-1}"
exec /usr/lib/firefox-fourier/firefox-fourier "$@"
LAUNCHER
chmod 0755 "${pkgdir}/usr/bin/firefox-fourier"
}
+210
View File
@@ -0,0 +1,210 @@
# firefox-fourier — V4L2 Stateless Decoder Patch Plan
Plan to extend Firefox 149's V4L2 hardware decode path to cover Rockchip
mainline kernel boards (RK3399 rkvdec, RK3566/RK3588 hantro, RK3588
rkvdec2) by routing stateless `S264`/`S265`/`VP9F` fourccs through
libavcodec's `v4l2_request` hwaccel, which mainline FFmpeg surfaces via
`AV_HWDEVICE_TYPE_DRM` (no dedicated `_V4L2REQUEST` enum exists upstream
— confirmed against `libavutil/hwcontext.h`).
## 1. Files touched (in order)
| # | Path | Change | Lines |
|---|------|--------|-------|
| 1 | `widget/gtk/GfxInfo.cpp` | `V4L2ProbeDevice` (~L10301110): add `S264`/`S265`/`VP9F` matches alongside existing `H264`/`HEVC`/`VP90`. Set `mIsV4L2Supported = Some(true)` and OR the same `CODEC_HW_DEC_*` bits. Tag a new bool `mV4L2IsStateless` so downstream can branch. | +35 / -2 |
| 2 | `dom/media/platforms/ffmpeg/FFmpegLibWrapper.h` | Add wrappers for `av_hwdevice_ctx_create` (currently only `_alloc`/`_init` per L173174) and `av_hwdevice_find_type_by_name`. Needed because stateless wants the *device-path-aware* `_create` form to bind `/dev/dri/renderD128`. | +4 / 0 |
| 3 | `dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp` | `dlsym` the two new pointers; gate behind `LIBAVUTIL_VERSION_MAJOR >= 56`. | +6 / 0 |
| 4 | `dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h` | Add `AVBufferRef* mDRMDeviceContext = nullptr;` and `bool mUsingV4L2Request = false;` next to existing `mUsingV4L2`. | +3 / 0 |
| 5 | `dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp` | New `CreateV4L2RequestDeviceContext()` modelled on `CreateVAAPIDeviceContext` (current uses `av_hwdevice_ctx_alloc(AV_HWDEVICE_TYPE_VAAPI)`). Wire it in alongside the existing V4L2 init branch (the one feeding `ChooseV4L2PixelFormat` at L~258). `FindVideoHardwareAVCodec` call at L708 stays unchanged for stateless — we want the *generic* `h264`/`hevc`/`vp9` decoder + a DRM hw_device_ctx, not a `_v4l2m2m` codec. | +90 / -5 |
| 6 | `modules/libpref/init/StaticPrefList.yaml` | New pref `media.ffmpeg.v4l2-request.enabled` mirroring `media.ffmpeg.vaapi.enabled` (default `@IS_LINUX@`). | +6 / 0 |
| 7 | `dom/media/ipc/RDDProcessHost.cpp` + sandbox policy file | Whitelist `/dev/media*`, `/dev/dri/renderD*`, `/dev/video*` for the RDD process (already partly done for VAAPI — verify `policy/linux/SandboxBrokerPolicyFactory.cpp`). | +6 / 0 |
Total: roughly **+150 / -10** across 7 files.
## 2. Probe extension — `GfxInfo.cpp::V4L2ProbeDevice`
Existing pattern (~L1075):
```cpp
if (outFormats.Contains("H264")) { mIsV4L2Supported = Some(true);
mV4L2SupportedCodecs |= CODEC_HW_DEC_H264; ... }
```
Add three siblings, each setting an additional `mV4L2IsStateless = true`:
```cpp
if (outFormats.Contains("S264")) { /* CODEC_HW_DEC_H264 | stateless */ }
if (outFormats.Contains("S265")) { /* CODEC_HW_DEC_HEVC | stateless */ }
if (outFormats.Contains("VP9F")) { /* CODEC_HW_DEC_VP9 | stateless */ }
```
Decision: do **not** introduce a separate `mIsV4L2StatelessSupported`.
Collapse under `mIsV4L2Supported` so the existing feature-gate plumbing
(`MediaCodecsSupport`, `gfxFeature::HW_DECODE_VIDEO`) flips identically
— only `mV4L2IsStateless` distinguishes the routing in step 3. Stateful
+ stateless on the same SoC (rare, but RK3588 has both rkvdec2 + hantro
VEPU) gracefully degrades to whichever codec wins enumeration order.
The capture-format gate (`YV12`/`NV12`) needs widening: stateless
decoders frequently expose only `NV12` or `NV15` (10-bit, RK3588 HEVC).
Add `NV15` and `NM12` (multiplanar NV12, hantro). Without this the
prober rejects an otherwise-good device.
## 3. Decoder routing — `FFmpegVideoDecoder.cpp`
Codec selection happens at **L651** (`AV_HWDEVICE_TYPE_VAAPI`
`h264_vaapi`) and **L708** (the V4L2 fallback path → `h264_v4l2m2m` via
`FindVideoHardwareAVCodec(mLib, mCodecID)` resolving by suffix). The
stateless route diverges from both: the *codec* must remain the generic
`h264`/`hevc`/`vp9` decoder (libavcodec auto-binds `v4l2_request` from
its `hw_configs` when a DRM hw_device_ctx is attached). Pseudo-patch:
```cpp
if (gfxInfo.mUsingV4L2 && gfxInfo.mV4L2IsStateless) {
AVCodec* codec = mLib->avcodec_find_decoder(mCodecID); // generic
mCodecContext = mLib->avcodec_alloc_context3(codec);
if (!CreateV4L2RequestDeviceContext()) return false;
mCodecContext->get_format = ChooseV4L2PixelFormat; // already returns DRM_PRIME
mUsingV4L2Request = true;
}
```
`CreateV4L2RequestDeviceContext()` body:
```cpp
const char* drm = "/dev/dri/renderD128";
if (mLib->av_hwdevice_ctx_create(&mDRMDeviceContext,
AV_HWDEVICE_TYPE_DRM, drm, nullptr, 0) < 0) return false;
mCodecContext->hw_device_ctx = mLib->av_buffer_ref(mDRMDeviceContext);
```
`av_hwdevice_ctx_create` — not currently wrapped — is the entry point.
The codec's internal hwaccel selector then walks
`avcodec_get_hw_config()` and picks the entry whose `device_type ==
AV_HWDEVICE_TYPE_DRM` and `pix_fmt == AV_PIX_FMT_DRM_PRIME`, which is
the v4l2_request hwaccel registered in `libavcodec/v4l2_request_*.c`.
No `av_hwdevice_find_type_by_name("v4l2_request")` needed — stays an
internal libavcodec name.
## 4. Dmabuf / DRM_PRIME reuse
`ChooseV4L2PixelFormat` at L~258270 already returns
`AV_PIX_FMT_DRM_PRIME` and is the *only* format the v4l2_request
hwaccel produces. The downstream consumer (DMABufSurfaceYUV import in
`FFmpegVideoFramePool.cpp`) is already DRM_PRIME-aware for the
stateful path — same code reads `AVDRMFrameDescriptor` from
`frame->data[0]`. **No new output handling required for NV12/YV12.**
10-bit caveat: RK3588 HEVC outputs `DRM_FORMAT_NV15` / `NV20`
(Mali-tile). Existing `WaylandDMABufSurface::CreateYUVSurface`
modifier list does not include `DRM_FORMAT_MOD_ARM_AFBC` or NV15
fourcc. Either reject 10-bit at probe (capture format gate above) or
extend `gfx/layers/DMABUFSurfaceImage.cpp` — out of scope for v1; gate
to NV12 only.
SAND format pollution Turner mentioned in bug 1969297 c#3 is
**Pi5-specific**; rkvdec/hantro do not produce SAND. Safe to ignore for
the Rockchip target.
## 5. Configuration
New pref:
```yaml
- name: media.ffmpeg.v4l2-request.enabled
type: RelaxedAtomicBool
value: @IS_LINUX@
mirror: always
```
No new env var. No `MOZ_X11_EGL`-style kludge. The existing
`MOZ_LOG=PlatformDecoderModule:5` covers diagnostics. Default-on
matches `media.ffmpeg.vaapi.enabled` shape; users get fallback to
software via existing failure paths if `av_hwdevice_ctx_create` fails
(e.g., missing `/dev/media0`).
## 6. Test plan (fresnel — RK3399, KDE Wayland)
1. `ls /dev/video* /dev/media*` — confirm `/dev/video0` (rkvdec
output) and `/dev/media0` exist.
2. `v4l2-ctl -d /dev/video0 --list-formats-out` — expect
`S264`/`S265`/`VP9F`.
3. Start: `MOZ_LOG="PlatformDecoderModule:5,FFmpegVideo:5"
firefox-fourier 2>&1 | tee fx.log`.
4. Open `https://test-videos.co.uk/bigbuckbunny/mp4-h264` 1080p clip.
5. Success markers in `fx.log`:
- `V4L2ProbeDevice: /dev/video0 supports S264 (stateless)`
- `Choosing FFmpeg pixel format for V4L2 video decoding.`
- `Requesting pixel format DRM PRIME`
- `av_hwdevice_ctx_create(DRM, /dev/dri/renderD128) ok`
- **No** `Using preferred software codec h264`.
6. `cat /sys/kernel/debug/clk/clk_summary | grep vdec` — clock should
be active during playback.
7. `top` — CPU < 40% on a single A72 core for 1080p H.264 (stock =
100% on all 6 cores).
## 7. Build + ship — `firefox-fourier` PKGBUILD
Mirror `chromium-fourier` shape exactly (sibling).
```bash
pkgname=firefox-fourier
pkgver=149.0
arch=('aarch64' 'x86_64')
makedepends=(rust clang lld nodejs python cbindgen nasm yasm wasi-libc-bin
gtk3 mesa libva ffmpeg) # ffmpeg only for headers via system libs
source=(
"https://archive.mozilla.org/pub/firefox/releases/${pkgver}/source/firefox-${pkgver}.source.tar.xz"
patches/0001-gfxinfo-v4l2-stateless-fourccs.patch
patches/0002-libwrapper-hwdevice-ctx-create.patch
patches/0003-ffmpegvideo-v4l2-request-route.patch
patches/0004-prefs-v4l2-request.patch
mozconfig
)
```
`prepare()`: `cd firefox-${pkgver}` → apply patches with `patch -Np1`.
`build()`: `MOZ_NOSPAM=1 ./mach build`. `package()`: `./mach install
DESTDIR=${pkgdir}`. `mozconfig` enables
`--enable-default-toolkit=cairo-gtk3-wayland`, `--with-system-ffmpeg`,
`ac_add_options --disable-tests`. **No** `--enable-media-gpu-process`
— let it default. Tarball is the official Mozilla source release (not
gecko-dev).
Extra makedepends vs. stock firefox PKGBUILD: none — this only
modifies existing C++.
## 8. Risk register (ranked)
1. **libavcodec ABI mismatch.** ALARM ships ffmpeg 7.x; Firefox dlopens
whatever's at `libavcodec.so.61`. If the v4l2_request hwaccel was
compiled out (Arch's ffmpeg has it; ALARM rebuild may not),
`av_hwdevice_ctx_create(DRM, ...)` succeeds but no codec binds —
silent fallback. Mitigation: `ffmpeg -hwaccels` should list `drm`.
2. **Renderer-process sandbox** blocks `/dev/dri/renderD128` open.
VAAPI already brokered this for RDD process; verify
`SandboxBrokerPolicyFactory.cpp` covers `/dev/media*` too — likely
doesn't.
3. **glxtest probe runs in stripped env.** `v4l2test` (the
FireTestProcess child) needs `cap_sys_admin` for
`VIDIOC_S_EXT_CTRLS` request API ioctls? No — request API just
needs `O_RDWR` on `/dev/media*`. Should be fine.
4. **Regression of stateful path.** Adding new fourccs is additive;
the routing branch is gated on `mV4L2IsStateless`. Stateful boards
(Pi4) untouched.
5. **NV15/10-bit on RK3588** — explicitly out-of-scope v1;
gate-rejected.
6. **rkvdec2 driver maturity.** Linux 6.12 mainline rkvdec2 H.264
works; HEVC/VP9 still upstream-pending on some boards. Probe will
skip what kernel doesn't expose.
7. **DMA-BUF modifier negotiation** with panfrost/panthor on Wayland
— already shaken out by chromium-fourier on RK3566; same code
path.
## 9. Upstream path (bug 1969297)
Split into 4 reviewable commits matching files 1, 2+3, 5, 6 from the
table. Add a gtest exercising `V4L2ProbeDevice` against a synthetic
v4l2test stdout containing `S264` (no kernel needed). Reach out to
skyevg (D252119 author) for review continuity. r? jya for the
FFmpegVideoDecoder change. The `av_hwdevice_ctx_create` wrapper
addition is a self-contained 6-liner that should land independently.
The 10-bit/SAND concerns Turner raised remain valid for Pi5 —
explicitly scope this series to **stateless DRM_PRIME NV12 only**,
leaving SAND for a follow-up bug.
+36
View File
@@ -0,0 +1,36 @@
# firefox-fourier mozconfig — minimal, Wayland + system ffmpeg.
ac_add_options --enable-application=browser
ac_add_options --enable-default-toolkit=cairo-gtk3-wayland
ac_add_options --enable-release
ac_add_options --enable-optimize
ac_add_options --enable-rust-simd
ac_add_options --enable-linker=lld
# Arch's 0004 patch updates the wasm32-wasip1 target string but ALARM's
# wasi-libc package doesn't expose the headers at the path Mozilla's
# probe looks for. Disable the wasm sandbox — hardens font/graphics
# parsers only, no impact on V4L2 decode. Revisit when ALARM's
# wasi-libc catches up to Arch x86_64's layout.
ac_add_options --without-wasm-sandboxed-libraries
# Firefox dlopens libavcodec.so at runtime regardless of build flags;
# the v4l2_request hwaccel routing happens via the system libavcodec
# loaded at startup, controlled by media.ffmpeg.enabled (default true).
# No configure-time hook needed.
ac_add_options --disable-tests
ac_add_options --disable-debug
ac_add_options --disable-debug-symbols
ac_add_options --disable-crashreporter
ac_add_options --disable-updater
ac_add_options --disable-default-browser-agent
# Mozilla branding requires a separate signed-build-tooling agreement
# we don't have; ship with the unbranded "firefox-fourier" identity.
ac_add_options --with-app-name=firefox-fourier
ac_add_options --with-app-basename=Firefox
ac_add_options --with-distribution-id=de.reauktion.fourier
# Reduce build memory pressure on aarch64 — parallel link is heavy.
mk_add_options MOZ_PARALLEL_BUILD=8
@@ -0,0 +1,75 @@
From: Markus Fritsche <mfritsche@reauktion.de>
Subject: [PATCH 1/4] widget/gtk: recognize V4L2 stateless fourccs in
GfxInfo prober (S264 / S265 / VP9F)
Date: 2026-04-27
Background
----------
Firefox's V4L2 prober in `widget/gtk/GfxInfo.cpp::V4L2ProbeDevice`
parses `v4l2test`'s `V4L2_OUTPUT_FMTS` line and matches against the
fourccs of stateful V4L2-M2M decoders (`H264`, `VP80`, `VP90`, `HEVC`).
That's correct for Pi4 / Mediatek / vendor-MPP stateful decoders but
silently skips every mainline-Linux Rockchip board: RK3399 `rkvdec`,
RK3566 `hantro` (multiplanar), RK3588 `hantro` and `rkvdec2` all
expose stateless fourccs only — `S264`, `S265`, `VP9F`. The probe
binary itself enumerates these correctly (verified end-to-end on
fresnel / Pinebook Pro / RK3399 with `v4l2test --device /dev/video1`
showing `V4L2_OUTPUT_FMTS: S265 S264 VP9F` and
`V4L2_SUPPORTED: TRUE`); the gap is purely in this string table.
This patch adds the three sibling blocks for the stateless fourccs,
each identical in shape to the existing stateful blocks except for
setting a new `mV4L2IsStateless` member. The follow-up patches in
this series (2/4, 3/4, 4/4) consume that member to route through the
libavcodec v4l2_request hwaccel (`AV_HWDEVICE_TYPE_DRM`) instead of
the v4l2m2m codec wrapper used for stateful boards.
Bug 1969297.
diff --git a/widget/gtk/GfxInfo.h b/widget/gtk/GfxInfo.h
--- a/widget/gtk/GfxInfo.h
+++ b/widget/gtk/GfxInfo.h
@@ -127,6 +127,10 @@
mozilla::Maybe<bool> mIsVAAPISupported;
int mVAAPISupportedCodecs = 0;
mozilla::Maybe<bool> mIsV4L2Supported;
+ // firefox-fourier: true when probe matched at least one stateless
+ // V4L2 fourcc (S264 / S265 / VP9F). Drives libavcodec v4l2_request
+ // hwaccel routing in FFmpegVideoDecoder.cpp.
+ bool mV4L2IsStateless = false;
int mV4L2SupportedCodecs = 0;
static int sGLXTestPipe;
diff --git a/widget/gtk/GfxInfo.cpp b/widget/gtk/GfxInfo.cpp
--- a/widget/gtk/GfxInfo.cpp
+++ b/widget/gtk/GfxInfo.cpp
@@ -852,6 +852,29 @@ void GfxInfo::V4L2ProbeDevice(nsCString& dev) {
media::MCSInfo::AddSupport(media::MediaCodecsSupport::HEVCHardwareDecode);
mV4L2SupportedCodecs |= CODEC_HW_DEC_HEVC;
}
+ // firefox-fourier: V4L2 stateless (request API) fourccs. Mainline
+ // Rockchip rkvdec / hantro / rkvdec2 expose these instead of the
+ // V4L2-M2M-stateful fourccs above. Decoding routes through
+ // libavcodec's v4l2_request hwaccel (AV_HWDEVICE_TYPE_DRM) rather
+ // than the *_v4l2m2m codec wrappers — see FFmpegVideoDecoder.cpp.
+ if (outFormats.Contains("S264")) {
+ mIsV4L2Supported = Some(true);
+ mV4L2IsStateless = true;
+ media::MCSInfo::AddSupport(media::MediaCodecsSupport::H264HardwareDecode);
+ mV4L2SupportedCodecs |= CODEC_HW_DEC_H264;
+ }
+ if (outFormats.Contains("S265")) {
+ mIsV4L2Supported = Some(true);
+ mV4L2IsStateless = true;
+ media::MCSInfo::AddSupport(media::MediaCodecsSupport::HEVCHardwareDecode);
+ mV4L2SupportedCodecs |= CODEC_HW_DEC_HEVC;
+ }
+ if (outFormats.Contains("VP9F")) {
+ mIsV4L2Supported = Some(true);
+ mV4L2IsStateless = true;
+ media::MCSInfo::AddSupport(media::MediaCodecsSupport::VP9HardwareDecode);
+ mV4L2SupportedCodecs |= CODEC_HW_DEC_VP9;
+ }
}
const nsTArray<RefPtr<GfxDriverInfo>>& GfxInfo::GetGfxDriverInfo() {
@@ -0,0 +1,52 @@
From: Markus Fritsche <mfritsche@reauktion.de>
Subject: [PATCH 2/4] dom/media/platforms/ffmpeg: wrap
av_hwdevice_ctx_create
Date: 2026-04-27
Background
----------
`FFmpegLibWrapper` already wraps `av_hwdevice_ctx_alloc` (no device
path) and `av_hwdevice_ctx_init`, used by the VAAPI codepath which
discovers the DRM device implicitly. The v4l2_request hwaccel needs
the *path-aware* constructor `av_hwdevice_ctx_create`, which lets the
caller pass `"/dev/dri/renderD128"` (or similar) directly when
creating an `AV_HWDEVICE_TYPE_DRM` context. libavcodec then binds the
v4l2_request hwaccel internally based on the codec's `hw_configs`.
This patch adds the function pointer + the `AV_FUNC_OPTION_SILENT`
registration. Same versioning as the other `av_hwdevice_ctx_*`
wrappers (libavutil 5862). No callers yet — patch 3/4
(FFmpegVideoDecoder routing) consumes it.
Bug 1969297.
diff --git a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
@@ -177,6 +177,11 @@
// libavutil >= 58
AVBufferRef* (*av_hwdevice_ctx_alloc)(int);
int (*av_hwdevice_ctx_init)(AVBufferRef* ref);
+ // firefox-fourier: device-path-aware constructor needed to bind a
+ // DRM hwdevice (AV_HWDEVICE_TYPE_DRM) to /dev/dri/renderD* for the
+ // libavcodec v4l2_request hwaccel.
+ int (*av_hwdevice_ctx_create)(AVBufferRef** device_ctx, int type,
+ const char* device, void* opts, int flags);
AVBufferRef* (*av_hwframe_ctx_alloc)(AVBufferRef* device_ctx);
int (*av_hwframe_ctx_init)(AVBufferRef* ref);
AVBufferRef* (*av_buffer_ref)(AVBufferRef* buf);
diff --git a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
--- a/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
@@ -293,6 +293,11 @@ FFmpegLibWrapper::LinkResult FFmpegLibWrapper::Link() {
AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59 |
AV_FUNC_AVUTIL_60 | AV_FUNC_AVUTIL_61 |
AV_FUNC_AVUTIL_62)
+ // firefox-fourier: see comment in FFmpegLibWrapper.h
+ AV_FUNC_OPTION_SILENT(av_hwdevice_ctx_create,
+ AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59 |
+ AV_FUNC_AVUTIL_60 | AV_FUNC_AVUTIL_61 |
+ AV_FUNC_AVUTIL_62)
AV_FUNC_OPTION_SILENT(
av_buffer_ref, AV_FUNC_AVUTIL_58 | AV_FUNC_AVUTIL_59 | AV_FUNC_AVUTIL_60 |
AV_FUNC_AVUTIL_61 | AV_FUNC_AVUTIL_62)
@@ -0,0 +1,208 @@
From: Markus Fritsche <mfritsche@reauktion.de>
Subject: [PATCH 3/4] dom/media/platforms/ffmpeg: route through libavcodec
v4l2_request hwaccel for V4L2 stateless boards
Date: 2026-04-27
Background
----------
Firefox's existing V4L2 init (`InitV4L2Decoder`) finds the codec by
suffix lookup (`FindVideoHardwareAVCodec(mLib, mCodecID)`), which on
Linux resolves to the **stateful** V4L2-M2M wrapper codec
(`h264_v4l2m2m` etc). Mainline-Linux Rockchip boards (RK3399 rkvdec,
RK3566/RK3588 hantro, RK3588 rkvdec2) only expose **stateless**
V4L2 fourccs (`S264`, `S265`, `VP9F`); the stateful wrapper codec
fails to open, Firefox falls all the way through to software.
This patch adds a sibling init path, `InitV4L2RequestDecoder`, that:
* uses the **generic** codec (e.g. plain `h264`, returned by
`avcodec_find_decoder(AV_CODEC_ID_H264)`) rather than the stateful
wrapper;
* sanity-checks the codec's `hw_configs` for an `AV_HWDEVICE_TYPE_DRM`
entry — that's how libavcodec surfaces the v4l2_request hwaccel
upstream (no dedicated `_V4L2REQUEST` device type exists);
* creates an `AV_HWDEVICE_TYPE_DRM` hwdevice context bound to
`/dev/dri/renderD128` via the new `av_hwdevice_ctx_create` wrapper
(patch 2/4) and attaches it to the codec context;
* reuses the existing `ChooseV4L2PixelFormat` get-format callback
(already returns `AV_PIX_FMT_DRM_PRIME`) and the existing
`apply_cropping = 0` constraint.
`InitV4L2RequestDecoder` is invoked **before** `InitV4L2Decoder` in
`InitHWDecoderIfAllowed`. On Rockchip mainline it succeeds. On Pi4 /
Mediatek / vendor-MPP-stateful boards the codec's `hw_configs` lacks
a DRM entry (the V4L2-M2M codecs don't register one), the sanity
check fails, and the existing stateful `InitV4L2Decoder` runs as
before. No regression of stateful boards.
`mDRMDeviceContext` is unconditionally `av_buffer_unref`'d in
`ProcessShutdown` (no-op when null). Gated behind
`media.ffmpeg.v4l2-request.enabled` from patch 4/4.
Bug 1969297.
diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h 2026-03-18 19:22:14.000000000 +0000
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h 2026-04-27 20:43:39.347992674 +0000
@@ -225,7 +225,12 @@
bool IsLinuxHDR() const;
MediaResult InitVAAPIDecoder();
MediaResult InitV4L2Decoder();
+ // firefox-fourier: V4L2 stateless (request API) decode path. Uses
+ // libavcodec's v4l2_request hwaccel, which it surfaces via
+ // AV_HWDEVICE_TYPE_DRM rather than a dedicated _V4L2REQUEST type.
+ MediaResult InitV4L2RequestDecoder();
bool CreateVAAPIDeviceContext();
+ bool CreateV4L2RequestDeviceContext();
bool GetVAAPISurfaceDescriptor(VADRMPRIMESurfaceDescriptor* aVaDesc);
void AddAcceleratedFormats(nsTArray<AVCodecID>& aCodecList,
AVCodecID aCodecID, AVVAAPIHWConfig* hwconfig);
@@ -239,7 +244,10 @@
void AdjustHWDecodeLogging();
AVBufferRef* mVAAPIDeviceContext = nullptr;
+ // firefox-fourier: DRM hwdevice ctx for the v4l2_request hwaccel.
+ AVBufferRef* mDRMDeviceContext = nullptr;
bool mUsingV4L2 = false;
+ bool mUsingV4L2Request = false;
// If video overlay is used we want to upload SW decoded frames to
// DMABuf and present it as a external texture to rendering pipeline.
bool mUploadSWDecodeToDMABuf = false;
diff --git a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp 2026-03-18 19:22:14.000000000 +0000
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp 2026-04-27 20:44:33.280766228 +0000
@@ -406,6 +406,105 @@
return NS_OK;
}
+// firefox-fourier: V4L2 stateless (request API) DRM hwdevice context.
+// libavcodec's v4l2_request hwaccel binds via AV_HWDEVICE_TYPE_DRM —
+// no dedicated _V4L2REQUEST type exists upstream.
+bool FFmpegVideoDecoder<LIBAV_VER>::CreateV4L2RequestDeviceContext() {
+ if (!mLib->av_hwdevice_ctx_create) {
+ FFMPEG_LOG(" av_hwdevice_ctx_create not available (libavutil too old)");
+ return false;
+ }
+ const char* drmDevice = "/dev/dri/renderD128";
+ if (mLib->av_hwdevice_ctx_create(&mDRMDeviceContext,
+ AV_HWDEVICE_TYPE_DRM, drmDevice,
+ nullptr, 0) < 0) {
+ FFMPEG_LOG(" av_hwdevice_ctx_create(DRM, %s) failed", drmDevice);
+ return false;
+ }
+ mCodecContext->hw_device_ctx = mLib->av_buffer_ref(mDRMDeviceContext);
+ FFMPEG_LOG(" DRM hwdevice ctx created on %s", drmDevice);
+ return true;
+}
+
+// firefox-fourier: try V4L2 stateless decode via libavcodec's
+// v4l2_request hwaccel. Distinct from InitV4L2Decoder which uses the
+// stateful h264_v4l2m2m wrapper codec. On Rockchip mainline boards
+// (rkvdec / hantro / rkvdec2) only the stateless path exists.
+MediaResult FFmpegVideoDecoder<LIBAV_VER>::InitV4L2RequestDecoder() {
+ FFMPEG_LOG("Initialising V4L2 stateless (request API) FFmpeg decoder");
+
+ StaticMutexAutoLock mon(sMutex);
+
+ // Use the GENERIC codec (not the _v4l2m2m wrapper). libavcodec picks
+ // the v4l2_request hwaccel internally by walking the codec's
+ // hw_configs once an AV_HWDEVICE_TYPE_DRM ctx is attached.
+ AVCodec* codec = mLib->avcodec_find_decoder(mCodecID);
+ if (!codec) {
+ FFMPEG_LOG(" generic codec for ID %d not found", mCodecID);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ // Sanity-check: refuse when libavcodec was built without the
+ // v4l2_request hwaccel (no DRM hwaccel registered against this
+ // codec). Distro packagers occasionally drop --enable-v4l2-request.
+ bool hasDrmHwaccel = false;
+ for (int i = 0;; i++) {
+ const AVCodecHWConfig* cfg = mLib->avcodec_get_hw_config(codec, i);
+ if (!cfg) break;
+ if (cfg->device_type == AV_HWDEVICE_TYPE_DRM) {
+ hasDrmHwaccel = true;
+ break;
+ }
+ }
+ if (!hasDrmHwaccel) {
+ FFMPEG_LOG(" codec %s has no DRM hwaccel — libavcodec built "
+ "without --enable-v4l2-request?", codec->name);
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ FFMPEG_LOG(" V4L2 stateless: codec %s : %s", codec->name, codec->long_name);
+
+ if (!(mCodecContext = mLib->avcodec_alloc_context3(codec))) {
+ FFMPEG_LOG(" couldn't init HW ffmpeg context");
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ mCodecContext->opaque = this;
+
+ // Reuse the existing V4L2 init helpers: pixel-format selector returns
+ // AV_PIX_FMT_DRM_PRIME, cropping disabled (FFmpeg can't crop opaque
+ // DRM buffers). Same constraints as the stateful V4L2 path.
+ InitHWCodecContext(ContextType::V4L2);
+ mCodecContext->apply_cropping = 0;
+
+ auto releaseDecoder = MakeScopeExit(
+ [&]() MOZ_NO_THREAD_SAFETY_ANALYSIS { ReleaseCodecContext(); });
+
+ if (!CreateV4L2RequestDeviceContext()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ MediaResult ret = AllocateExtraData();
+ if (NS_FAILED(ret)) {
+ return ret;
+ }
+
+ if (mLib->avcodec_open2(mCodecContext, codec, nullptr) < 0) {
+ FFMPEG_LOG(" Couldn't open V4L2 stateless decoder");
+ return NS_ERROR_DOM_MEDIA_FATAL_ERR;
+ }
+
+ if (mAcceleratedFormats.IsEmpty()) {
+ mAcceleratedFormats.AppendElement(mCodecID);
+ }
+
+ AdjustHWDecodeLogging();
+
+ FFMPEG_LOG(" V4L2 stateless FFmpeg init successful");
+ mUsingV4L2 = true;
+ mUsingV4L2Request = true;
+ releaseDecoder.release();
+ return NS_OK;
+}
+
MediaResult FFmpegVideoDecoder<LIBAV_VER>::InitV4L2Decoder() {
FFMPEG_LOG("Initialising V4L2-DRM FFmpeg decoder");
@@ -659,6 +758,16 @@
# endif // MOZ_ENABLE_VAAPI
# ifdef MOZ_ENABLE_V4L2
+ // firefox-fourier: try V4L2 stateless (request API) first. On
+ // mainline-Linux Rockchip boards (RK3399 rkvdec, RK3566/RK3588
+ // hantro, RK3588 rkvdec2) the kernel exposes only stateless
+ // fourccs, so the stateful path below would fail anyway. On
+ // stateful boards (Pi4 / vendor MPP) this gracefully falls
+ // through (no DRM hwaccel registered for the codec).
+ if (StaticPrefs::media_ffmpeg_v4l2_request_enabled() &&
+ NS_SUCCEEDED(InitV4L2RequestDecoder())) {
+ return;
+ }
// VAAPI didn't work or is disabled, so try V4L2 with DRM
if (NS_SUCCEEDED(InitV4L2Decoder())) {
return;
@@ -2046,6 +2155,11 @@
if (IsHardwareAccelerated()) {
mLib->av_buffer_unref(&mVAAPIDeviceContext);
}
+ // firefox-fourier: release the DRM hwdevice ctx for the v4l2_request
+ // hwaccel. Always safe — av_buffer_unref no-ops on a null pointer.
+ if (mDRMDeviceContext) {
+ mLib->av_buffer_unref(&mDRMDeviceContext);
+ }
#endif
#ifdef MOZ_ENABLE_D3D11VA
if (IsHardwareAccelerated()) {
@@ -0,0 +1,34 @@
From: Markus Fritsche <mfritsche@reauktion.de>
Subject: [PATCH 4/4] modules/libpref: add media.ffmpeg.v4l2-request.enabled
Date: 2026-04-27
Background
----------
Toggle for the V4L2 stateless (request API) decode path introduced
in patch 3/4. Defaults on for Linux, mirroring the
`media.ffmpeg.vaapi.enabled`-style shape. Users can flip to false to
force the existing stateful `InitV4L2Decoder` (or VAAPI / software
fallbacks) without rebuilding.
Bug 1969297.
diff --git a/modules/libpref/init/StaticPrefList.yaml b/modules/libpref/init/StaticPrefList.yaml
--- a/modules/libpref/init/StaticPrefList.yaml
+++ b/modules/libpref/init/StaticPrefList.yaml
@@ -12159,6 +12159,16 @@
type: uint32_t
value: 2
mirror: once
+
+# firefox-fourier: route V4L2 stateless (request API) decode through
+# libavcodec's v4l2_request hwaccel (AV_HWDEVICE_TYPE_DRM). Required
+# for mainline-Linux Rockchip rkvdec / hantro / rkvdec2. On stateful
+# boards (Pi4 / vendor MPP) the codec's hw_configs lacks a DRM entry
+# and the path silently falls back to InitV4L2Decoder.
+- name: media.ffmpeg.v4l2-request.enabled
+ type: RelaxedAtomicBool
+ value: true
+ mirror: always
#endif # MOZ_WIDGET_GTK
# Set to true in marionette tests to disable the sanity test