Merge pull request 'noether/daedalus-v4l2-packages' (#31) from claude-noether/marfrit-packages:noether/daedalus-v4l2-packages into main
build and publish packages / distcc-avahi-aarch64 (push) Successful in 55s
build and publish packages / lmcp-any (push) Successful in 9s
build and publish packages / lmcp-debian (push) Successful in 6s
build and publish packages / claude-his-any (push) Successful in 9s
build and publish packages / claude-his-debian (push) Successful in 25s
build and publish packages / ffmpeg-v4l2-request-aarch64 (push) Failing after 16m36s
build and publish packages / libva-v4l2-request-fourier-aarch64 (push) Has been cancelled
build and publish packages / mpv-fourier-aarch64 (push) Has been cancelled

Reviewed-on: #31
This commit was merged in pull request #31.
This commit is contained in:
2026-05-18 19:31:00 +00:00
13 changed files with 1430 additions and 0 deletions
@@ -0,0 +1,166 @@
--- a/libavutil/hwcontext_v4l2request.c
+++ b/libavutil/hwcontext_v4l2request.c
@@ -19,12 +19,13 @@
#include "config.h"
#include <fcntl.h>
#include <linux/dma-buf.h>
#include <linux/media.h>
#include <sys/ioctl.h>
+#include <sys/stat.h>
#include <sys/mman.h>
#include <unistd.h>
#include <drm_fourcc.h>
#include <libudev.h>
@@ -690,12 +691,125 @@
}
udev_enumerate_unref(enumerate);
return ret;
}
+/*
+ * Brute-force fallback used when libudev's scan fails (e.g. inside firefox's
+ * RDD sandbox where Mozilla's broker rejects fd-relative openat used by
+ * systemd's chase() symlink resolver). Iterates /dev/video[0..63], picks the
+ * one whose major/minor matches the requested devnum.
+ */
+static char *v4l2request_devnum_to_video_path_brute(dev_t devnum)
+{
+ char path[32];
+ struct stat st;
+ for (int i = 0; i < 64; i++) {
+ snprintf(path, sizeof(path), "/dev/video%d", i);
+ if (stat(path, &st) < 0)
+ continue;
+ if (st.st_rdev == devnum)
+ return av_strdup(path);
+ }
+ return NULL;
+}
+
+/* Brute-force version of v4l2request_probe_video_devices: replaces the
+ * udev_device_new_from_devnum + udev_device_get_devnode flow with
+ * stat()-based major/minor matching against /dev/video[0..63]. */
+static int v4l2request_probe_video_devices_brute(AVHWFramesContext *hwfc,
+ uint32_t pixelformat,
+ uint32_t buffersize)
+{
+ AVV4L2RequestFramesContext *fctx = hwfc->hwctx;
+ AVV4L2RequestFramesContextInternal *fctxi = fctx->internal;
+ struct media_device_info device_info;
+ struct media_v2_topology topology = {0};
+ struct media_v2_interface *interfaces;
+ char *path;
+ dev_t devnum;
+ int ret;
+
+ if (ioctl(fctxi->media_fd, MEDIA_IOC_DEVICE_INFO, &device_info) < 0)
+ return AVERROR(errno);
+
+ if (ioctl(fctxi->media_fd, MEDIA_IOC_G_TOPOLOGY, &topology) < 0)
+ return AVERROR(errno);
+
+ if (!topology.num_interfaces)
+ return AVERROR(ENOENT);
+
+ interfaces = av_calloc(topology.num_interfaces,
+ sizeof(struct media_v2_interface));
+ if (!interfaces)
+ return AVERROR(ENOMEM);
+
+ topology.ptr_interfaces = (__u64)(uintptr_t)interfaces;
+ if (ioctl(fctxi->media_fd, MEDIA_IOC_G_TOPOLOGY, &topology) < 0) {
+ ret = AVERROR(errno);
+ goto fail;
+ }
+
+ ret = AVERROR(ENOENT);
+ for (unsigned i = 0; i < topology.num_interfaces; i++) {
+ if (interfaces[i].intf_type != MEDIA_INTF_T_V4L_VIDEO)
+ continue;
+
+ devnum = makedev(interfaces[i].devnode.major,
+ interfaces[i].devnode.minor);
+ path = v4l2request_devnum_to_video_path_brute(devnum);
+ if (!path)
+ continue;
+
+ ret = v4l2request_probe_video_device(hwfc, path, pixelformat, buffersize);
+ if (!ret) {
+ av_log(hwfc, AV_LOG_INFO,
+ "Using V4L2 media driver %s (brute-force) for %s\n",
+ device_info.driver, av_fourcc2str(pixelformat));
+ av_free(path);
+ break;
+ }
+ av_free(path);
+ }
+
+fail:
+ av_free(interfaces);
+ return ret;
+}
+
+/* Brute-force fallback for v4l2request_probe_media_devices(). Iterates
+ * /dev/media[0..15], opens each, probes via topology+stat. */
+static int v4l2request_probe_media_devices_brute(AVHWFramesContext *hwfc,
+ uint32_t pixelformat,
+ uint32_t buffersize)
+{
+ AVV4L2RequestFramesContext *fctx = hwfc->hwctx;
+ AVV4L2RequestFramesContextInternal *fctxi = fctx->internal;
+ char path[32];
+ int ret = AVERROR(ENOENT);
+
+ for (int i = 0; i < 16; i++) {
+ snprintf(path, sizeof(path), "/dev/media%d", i);
+
+ fctxi->media_fd = open(path, O_RDWR);
+ if (fctxi->media_fd < 0)
+ continue;
+
+ ret = v4l2request_probe_video_devices_brute(hwfc, pixelformat,
+ buffersize);
+ if (!ret)
+ return 0;
+
+ close(fctxi->media_fd);
+ fctxi->media_fd = -1;
+ }
+
+ return ret;
+}
+
static int v4l2request_open_decoder(AVHWFramesContext *hwfc)
{
AVV4L2RequestFramesContext *fctx = hwfc->hwctx;
uint32_t buffersize;
struct udev *udev;
int ret;
@@ -712,12 +826,23 @@
buffersize = FFMAX(hwfc->width * hwfc->height * 3 / 2, 256 * 1024);
// Probe all media devices (auto-detection)
ret = v4l2request_probe_media_devices(hwfc, udev, fctx->pixelformat, buffersize);
+ // Brute-force fallback when libudev fails. Firefox-fourier hits this
+ // because Mozilla's RDD sandbox blocks fd-relative openat used by
+ // systemd's chase() symlink resolver inside udev_enumerate_scan_devices.
+ if (ret < 0) {
+ av_log(hwfc, AV_LOG_INFO,
+ "libudev probe failed (%d), falling back to brute-force /dev/media*\n",
+ ret);
+ ret = v4l2request_probe_media_devices_brute(hwfc, fctx->pixelformat,
+ buffersize);
+ }
+
udev_unref(udev);
return ret;
}
static AVBufferRef *v4l2request_v4l2_buffer_alloc(AVHWFramesContext *hwfc,
struct v4l2_format *format)
@@ -0,0 +1,178 @@
From 0cd6e669735e453ec8772f111065bbb2f70a5bc6 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <mfritsche@reauktion.de>
Date: Mon, 18 May 2026 07:27:10 +0000
Subject: [PATCH] avutil/hwcontext_v4l2request: unpack NV15 to P010 in
transfer_data_from
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
V4L2_PIX_FMT_NV15 (RK3399/RK3588 rkvdec 10-bit 4:2:0 capture) is mapped to
sw_format = AV_PIX_FMT_YUV420P10 in v4l2request_capture_pixelformats[]. The
existing transfer_get_formats explicitly blanked the format list for that
sw_format, so 'ffmpeg -hwaccel v4l2request -vf hwdownload,format=p010le' on
a Hi10P / Main10 input failed at filter init with EINVAL before reaching
the actual decode (which itself succeeds — 2 frames decoded cleanly).
Expose AV_PIX_FMT_P010 as the transfer target for NV15-backed surfaces and
unpack the packed 10-bit samples into the standard high-bits-of-16 layout
inside transfer_data_from. Luma and chroma share the same packing format
(5 bytes per 4 samples, little endian); chroma plane is W × H/2 samples
for 4:2:0.
The other 'needs custom unpack' sw_formats (YUV420P / Allwinner NV12_32L32
tiled and YUV422P10 / rkvdec NV20) keep the original ENOSYS path because
they need different unpack code that isn't covered by this patch.
Closes marfrit/marfrit-packages#21.
---
libavutil/hwcontext_v4l2request.c | 111 +++++++++++++++++++++++++++++-
1 file changed, 110 insertions(+), 1 deletion(-)
diff --git a/libavutil/hwcontext_v4l2request.c b/libavutil/hwcontext_v4l2request.c
index b6633d9081..3842160dfb 100644
--- a/libavutil/hwcontext_v4l2request.c
+++ b/libavutil/hwcontext_v4l2request.c
@@ -1073,6 +1073,56 @@ fail:
return ret;
}
+/*
+ * Unpack one NV15-packed 10-bit plane (5 bytes per 4 samples, little endian)
+ * into a P010-style plane (10 bits in the high bits of a 16-bit container).
+ * `dst_stride` is in bytes; `src_stride` is bytes per row of NV15 data.
+ */
+static void v4l2request_nv15_unpack_plane_to_p010(const uint8_t *src,
+ uint16_t *dst,
+ unsigned width,
+ unsigned height,
+ unsigned src_stride,
+ unsigned dst_stride)
+{
+ for (unsigned y = 0; y < height; y++) {
+ const uint8_t *s = src + y * src_stride;
+ uint16_t *d = (uint16_t *)((uint8_t *)dst + y * dst_stride);
+ unsigned x;
+
+ for (x = 0; x + 4 <= width; x += 4) {
+ uint16_t a = (uint16_t)s[0] | ((uint16_t)(s[1] & 0x03) << 8);
+ uint16_t b = ((uint16_t)s[1] >> 2) | ((uint16_t)(s[2] & 0x0F) << 6);
+ uint16_t c = ((uint16_t)s[2] >> 4) | ((uint16_t)(s[3] & 0x3F) << 4);
+ uint16_t e = ((uint16_t)s[3] >> 6) | ((uint16_t)s[4] << 2);
+
+ d[0] = (uint16_t)(a << 6);
+ d[1] = (uint16_t)(b << 6);
+ d[2] = (uint16_t)(c << 6);
+ d[3] = (uint16_t)(e << 6);
+
+ d += 4;
+ s += 5;
+ }
+
+ if (x < width) {
+ unsigned rem = width - x;
+ uint16_t pix[4] = { 0, 0, 0, 0 };
+
+ pix[0] = (uint16_t)s[0] | ((uint16_t)(s[1] & 0x03) << 8);
+ if (rem >= 2)
+ pix[1] = ((uint16_t)s[1] >> 2) | ((uint16_t)(s[2] & 0x0F) << 6);
+ if (rem >= 3)
+ pix[2] = ((uint16_t)s[2] >> 4) | ((uint16_t)(s[3] & 0x3F) << 4);
+ if (rem >= 4)
+ pix[3] = ((uint16_t)s[3] >> 6) | ((uint16_t)s[4] << 2);
+
+ for (unsigned j = 0; j < rem; j++)
+ d[j] = (uint16_t)(pix[j] << 6);
+ }
+ }
+}
+
static int v4l2request_transfer_get_formats(AVHWFramesContext *hwfc,
enum AVHWFrameTransferDirection dir,
enum AVPixelFormat **formats)
@@ -1082,6 +1132,22 @@ static int v4l2request_transfer_get_formats(AVHWFramesContext *hwfc,
if (dir == AV_HWFRAME_TRANSFER_DIRECTION_TO)
return AVERROR(ENOSYS);
+ /*
+ * NV15-backed surfaces (sw_format = YUV420P10) are exposed as P010 to
+ * downstream filters: the unpack below converts the packed 10-bit
+ * samples into the standard high-bits-of-16 layout. Hi10P / Main10
+ * VAAPI/v4l2-request decode reaches userspace through this path.
+ */
+ if (hwfc->sw_format == AV_PIX_FMT_YUV420P10) {
+ fmts = av_malloc_array(2, sizeof(*fmts));
+ if (!fmts)
+ return AVERROR(ENOMEM);
+ fmts[0] = AV_PIX_FMT_P010;
+ fmts[1] = AV_PIX_FMT_NONE;
+ *formats = fmts;
+ return 0;
+ }
+
fmts = av_malloc_array(2, sizeof(*fmts));
if (!fmts)
return AVERROR(ENOMEM);
@@ -1089,8 +1155,13 @@ static int v4l2request_transfer_get_formats(AVHWFramesContext *hwfc,
fmts[0] = hwfc->sw_format;
fmts[1] = AV_PIX_FMT_NONE;
+ /*
+ * Tiled-NV12-32L32 (Allwinner) and NV20 (rkvdec 4:2:2 10-bit) still need
+ * dedicated unpacks before hwdownload can consume them; leave them as
+ * "no transfer formats" so the filter graph reports the limitation
+ * rather than silently producing garbage.
+ */
if (hwfc->sw_format == AV_PIX_FMT_YUV420P ||
- hwfc->sw_format == AV_PIX_FMT_YUV420P10 ||
hwfc->sw_format == AV_PIX_FMT_YUV422P10)
fmts[0] = AV_PIX_FMT_NONE;
@@ -1110,6 +1181,44 @@ static int v4l2request_transfer_data_from(AVHWFramesContext *hwfc,
map = av_frame_alloc();
if (!map)
return AVERROR(ENOMEM);
+
+ /*
+ * For NV15→P010, map the raw NV15 bytes (sw_format) and unpack into
+ * dst's P010 storage. Otherwise fall through to the original byte-copy
+ * path used for 1:1 sw_format matches (NV12, NV16, AFBC handled by DRM).
+ */
+ if (hwfc->sw_format == AV_PIX_FMT_YUV420P10) {
+ /*
+ * Only P010 is advertised by transfer_get_formats for this sw_format;
+ * a caller that bypasses get_formats and asks for anything else would
+ * silently corrupt output via av_frame_copy on NV15-packed bytes.
+ * Reject explicitly.
+ */
+ if (dst->format != AV_PIX_FMT_P010) {
+ ret = AVERROR(ENOSYS);
+ goto fail;
+ }
+
+ map->format = hwfc->sw_format;
+ ret = v4l2request_map_frame(hwfc, map, src);
+ if (ret)
+ goto fail;
+
+ v4l2request_nv15_unpack_plane_to_p010(map->data[0],
+ (uint16_t *)dst->data[0],
+ dst->width, dst->height,
+ map->linesize[0],
+ dst->linesize[0]);
+ /* NV15 chroma plane is W × H/2 samples (4:2:0, UV interleaved). */
+ v4l2request_nv15_unpack_plane_to_p010(map->data[1],
+ (uint16_t *)dst->data[1],
+ dst->width, dst->height / 2,
+ map->linesize[1],
+ dst->linesize[1]);
+ ret = 0;
+ goto fail;
+ }
+
map->format = dst->format;
ret = v4l2request_map_frame(hwfc, map, src);
--
2.47.3
+177
View File
@@ -0,0 +1,177 @@
#!/bin/bash
# Build ffmpeg-v4l2-request-fourier_<ver>_arm64.deb (the Kwiboo FFmpeg
# fork with the V4L2-Request API hwaccel patches).
#
# Mirror of arch/ffmpeg-v4l2-request-fourier into the Debian tree.
# Provides the patched `ffmpeg` + `ffprobe` binaries plus the shared
# libav* libraries with v4l2-request support, so consumers like
# mpv-fourier (and the kdirect bit-exact reference test rig) work
# end-to-end without a stock-Debian FFmpeg fallback.
#
# Conflicts: ffmpeg, libav* — this is a drop-in replacement. Pi 5 /
# CM5 (BCM2712) hosts using daedalus_v4l2 + libva-v4l2-request-fourier
# DON'T strictly need this package (stock Debian ffmpeg + libva path
# is sufficient for the VAAPI route), but the kdirect bit-exact
# reference + firefox-fourier / mpv-fourier consumers do.
#
# Big build: 25-40 min on a runner. Output is a single .deb that
# bundles ffmpeg/ffprobe + libavcodec.so.61 + libavformat.so.61 etc.
# all together — no split per-library subpackages. Easier to consume,
# trades off finer-grained downgrades.
#
# Sibling Arch package: ../../arch/ffmpeg-v4l2-request-fourier/PKGBUILD
# Upstream: https://github.com/Kwiboo/FFmpeg (branch v4l2-request-n8.1)
set -euo pipefail
# Same pin as arch/ — v4l2-request-n8.1 tip 2026-04-24.
UPSTREAM_COMMIT=b57fbbe50c9b2656fad86a1a7eeabfd2b2a50935
FFMPEG_VERSION=8.1
# epoch 2 matches Debian's stock ffmpeg (currently 7:7.1.x in trixie);
# +rfourier suffix to avoid colliding with upstream/Debian rebuilds.
PKGVER=2:${FFMPEG_VERSION}+rfourier+gb57fbbe
PKGREL=1
HERE=$(dirname "$(readlink -f "$0")")
# Reproducible build. 2026-05-18 23:00 UTC — Phase 8.13 close.
export SOURCE_DATE_EPOCH=1779231600
work=$(mktemp -d)
trap "rm -rf $work" EXIT
cd "$work"
git clone --depth 1 \
--branch v4l2-request-n8.1 \
https://github.com/Kwiboo/FFmpeg.git FFmpeg
cd FFmpeg
# git fetch the specific commit (depth=1 of branch tip might not be it)
if [ "$(git rev-parse HEAD)" != "$UPSTREAM_COMMIT" ]; then
git fetch origin "$UPSTREAM_COMMIT"
git checkout "$UPSTREAM_COMMIT"
fi
# Apply patches (same as Arch).
patch -Np1 -i "$HERE/0001-libudev-bypass-fallback.patch"
patch -Np1 -i "$HERE/0002-nv15-to-p010-unpack.patch"
# Configure with Arch-parity flags. Drops the same set of features
# (X11, AMF, CUDA, FireWire, AviSynth, Bluray, OpenMPT, JPEG-XL,
# Theora, XVid, rsvg, soxr, ssh, vidstab, modplug, SDL2, Vulkan,
# JACK, GSM, Speex) — not needed on the Fourier fleet, keeps the .deb
# smaller and the dependency graph tighter.
./configure \
--prefix=/usr \
--libdir=/usr/lib/aarch64-linux-gnu \
--shlibdir=/usr/lib/aarch64-linux-gnu \
--incdir=/usr/include/aarch64-linux-gnu \
--disable-debug \
--disable-static \
--disable-doc \
--disable-stripping \
--enable-shared \
--enable-gpl \
--enable-version3 \
--enable-pic \
--enable-neon \
--arch=aarch64 \
--enable-libdrm \
--enable-libv4l2 \
--enable-libudev \
--enable-v4l2-request \
--enable-v4l2_m2m \
--enable-vaapi \
--enable-opengl \
--enable-gnutls \
--enable-fontconfig \
--enable-libass \
--enable-libfreetype \
--enable-libfribidi \
--enable-libxml2 \
--enable-libpulse \
--enable-libdav1d \
--enable-libopus \
--enable-libvorbis \
--enable-libmp3lame \
--enable-libvpx \
--enable-libx264 \
--enable-libx265 \
--enable-libwebp \
--host-cflags='-fPIC'
make -j"$(nproc)"
make tools/qt-faststart
# Stage
ROOT="$work/pkgroot"
make DESTDIR="$ROOT" install
install -Dm755 tools/qt-faststart "$ROOT/usr/bin/qt-faststart"
# Doc
mkdir -p "$ROOT/usr/share/doc/ffmpeg-v4l2-request-fourier" "$ROOT/DEBIAN"
install -Dm644 "$HERE/debian/copyright" \
"$ROOT/usr/share/doc/ffmpeg-v4l2-request-fourier/copyright"
install -Dm644 "$HERE/debian/changelog" \
"$ROOT/usr/share/doc/ffmpeg-v4l2-request-fourier/changelog.Debian"
gzip -9 -n "$ROOT/usr/share/doc/ffmpeg-v4l2-request-fourier/changelog.Debian"
cat > "$ROOT/DEBIAN/control" <<EOF
Package: ffmpeg-v4l2-request-fourier
Version: ${PKGVER}-${PKGREL}
Section: video
Priority: optional
Architecture: arm64
Depends: libc6,
libdrm2,
libfontconfig1,
libfreetype6,
libfribidi0,
libxml2,
libpulse0,
libdav1d7 | libdav1d6,
libopus0,
libvorbis0a,
libvorbisenc2,
libmp3lame0,
libvpx9 | libvpx8 | libvpx7,
libx264-164 | libx264-163,
libx265-215 | libx265-209 | libx265-199,
libwebp7 | libwebp6,
libwebpmux3,
libass9,
libgnutls30,
libudev1,
libv4l-0,
libva2,
libva-drm2
Conflicts: ffmpeg, libavcodec61, libavformat61, libavutil59,
libswresample5, libswscale8, libavdevice61, libavfilter10,
libpostproc58
Replaces: ffmpeg, libavcodec61, libavformat61, libavutil59,
libswresample5, libswscale8, libavdevice61, libavfilter10,
libpostproc58
Provides: ffmpeg (= ${PKGVER}-${PKGREL}),
libavcodec.so.61,
libavformat.so.61,
libavutil.so.59
Maintainer: Markus Fritsche <mfritsche@reauktion.de>
Homepage: https://github.com/Kwiboo/FFmpeg
Description: FFmpeg with V4L2 Request API hwaccel (Kwiboo fork)
FFmpeg ${FFMPEG_VERSION} patched with the V4L2 Request API stateless
video decoder hwaccel — Kwiboo's long-running rebase of Jernej
Škrabec's v4l2-request patchset onto FFmpeg release tags.
.
Provides 'ffmpeg -hwaccel v4l2request' / '-hwaccel drm' routes that
drive rkvdec / hantro / cedrus / rpi-hevc-dec / daedalus_v4l2
stateless decoders directly through libavcodec's hwdevice DRM path,
bypassing libva. Used by mpv-fourier and firefox-fourier as their
backing FFmpeg, and as the kdirect bit-exact reference in libva-v4l2-
request-fourier validation.
.
Drops X11/AMF/CUDA/Bluray/JACK/Vulkan/SDL2/Theora/XVid/Speex/JPEG-XL
per Fourier fleet policy (Wayland + ARM + video-decode focus).
No 'ffplay' binary; mpv-fourier covers interactive playback.
EOF
DEB_OUT="ffmpeg-v4l2-request-fourier_${PKGVER//:/%3a}-${PKGREL}_arm64.deb"
dpkg-deb --root-owner-group --build "$ROOT" "$HERE/$DEB_OUT"
echo "built: $HERE/$DEB_OUT"
+14
View File
@@ -0,0 +1,14 @@
ffmpeg-v4l2-request-fourier (2:8.1+rfourier+gb57fbbe-1) bookworm trixie; urgency=medium
* Initial Debian packaging for the Kwiboo FFmpeg fork with V4L2
Request API hwaccel patches.
* Mirror of arch/ffmpeg-v4l2-request-fourier (same pin b57fbbe,
same configure flags, same 2 patches: libudev-bypass-fallback +
nv15-to-p010-unpack).
* Drop-in replacement for Debian's stock ffmpeg + libav*; takes
epoch 2 to win the apt version comparison.
* Required by mpv-fourier and firefox-fourier; not strictly
required for the VAAPI-only path on daedalus-v4l2 hosts (stock
libva + Debian ffmpeg works there).
-- Markus Fritsche <mfritsche@reauktion.de> Mon, 18 May 2026 23:00:00 +0000
+61
View File
@@ -0,0 +1,61 @@
Source: ffmpeg-v4l2-request-fourier
Section: video
Priority: optional
Maintainer: Markus Fritsche <mfritsche@reauktion.de>
Build-Depends: debhelper-compat (= 13),
git,
nasm,
pkg-config,
libass-dev,
libdrm-dev,
libfontconfig1-dev,
libfreetype6-dev,
libfribidi-dev,
libgnutls28-dev,
libmp3lame-dev,
libopus-dev,
libpulse-dev,
libudev-dev,
libv4l-dev,
libva-dev,
libvorbis-dev,
libvpx-dev,
libwebp-dev,
libx264-dev,
libx265-dev,
libxml2-dev,
libdav1d-dev,
linux-libc-dev
Standards-Version: 4.6.2
Homepage: https://github.com/Kwiboo/FFmpeg
Package: ffmpeg-v4l2-request-fourier
Architecture: arm64
Depends: ${misc:Depends}, ${shlibs:Depends}
Conflicts: ffmpeg,
libavcodec61, libavformat61, libavutil59,
libswresample5, libswscale8, libavdevice61, libavfilter10,
libpostproc58
Replaces: ffmpeg,
libavcodec61, libavformat61, libavutil59,
libswresample5, libswscale8, libavdevice61, libavfilter10,
libpostproc58
Provides: ffmpeg (= ${binary:Version}),
libavcodec.so.61,
libavformat.so.61,
libavutil.so.59
Description: FFmpeg with V4L2 Request API hwaccel (Kwiboo fork)
FFmpeg 8.1 patched with the V4L2 Request API stateless video decoder
hwaccel — Kwiboo's long-running rebase of Jernej Škrabec's
v4l2-request patchset onto FFmpeg release tags.
.
Provides 'ffmpeg -hwaccel v4l2request' / '-hwaccel drm' routes that
drive rkvdec / hantro / cedrus / rpi-hevc-dec / daedalus_v4l2
stateless decoders directly through libavcodec's hwdevice DRM path,
bypassing libva. Used by mpv-fourier and firefox-fourier as their
backing FFmpeg, and as the kdirect bit-exact reference in
libva-v4l2-request-fourier validation.
.
Drops X11/AMF/CUDA/Bluray/JACK/Vulkan/SDL2/Theora/XVid/Speex/JPEG-XL
per Fourier fleet policy (Wayland + ARM + video-decode focus). No
'ffplay' binary; mpv-fourier covers interactive playback.
+21
View File
@@ -0,0 +1,21 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: FFmpeg (Kwiboo v4l2-request fork)
Upstream-Contact: Niklas Haas <niklas@haasn.dev>, Jernej Škrabec, Kwiboo
Source: https://github.com/Kwiboo/FFmpeg
Files: *
Copyright: 2000-2026 The FFmpeg developers
License: GPL-3.0-or-later
Files: 0001-libudev-bypass-fallback.patch 0002-nv15-to-p010-unpack.patch
Copyright: 2024-2026 Markus Fritsche <fritsche.markus@gmail.com>
License: GPL-3.0-or-later
License: GPL-3.0-or-later
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
.
On Debian systems, the complete text of the GNU General Public
License v3 can be found in `/usr/share/common-licenses/GPL-3'.
@@ -0,0 +1,56 @@
From 9d3bbd3651eb8405b8609e4f5e8c4978056483d0 Mon Sep 17 00:00:00 2001
From: Jonas Karlman <jonas@kwiboo.se>
Date: Sun, 18 Aug 2024 17:42:14 -0700
Subject: [PATCH 1/2] meson: add detection logic for v4l2request support
We will probably adjust this to look for a specific libavutil version after
v4l2request support is merged upstream, but this check is fine for now.
---
meson.build | 11 +++++++++++
meson.options | 1 +
2 files changed, 12 insertions(+)
diff --git a/meson.build b/meson.build
index d4c75a907f..540f279dc7 100644
--- a/meson.build
+++ b/meson.build
@@ -1444,6 +1444,16 @@ if features['ios-gl']
sources += files('video/out/hwdec/hwdec_ios_gl.m')
endif
+v4l2request = get_option('v4l2request').require(
+ cc.has_header_symbol('libavutil/hwcontext.h',
+ 'AV_HWDEVICE_TYPE_V4L2REQUEST',
+ dependencies: libavutil)
+)
+features += {'v4l2request': v4l2request.allowed()}
+if features['v4l2request']
+ sources += files('video/v4l2request.c')
+endif
+
libva = dependency('libva', version: '>= 1.1.0', required: get_option('vaapi'))
vaapi_drm = dependency('libva-drm', version: '>= 1.1.0', required:
@@ -1911,6 +1921,7 @@ summary({'cocoa': features['cocoa'] and features['swift'],
'libmpv': get_option('libmpv'),
'lua': features['lua'],
'opengl': features['gl'],
+ 'v4l2request': features['v4l2request'],
'vulkan': features['vulkan'],
'wayland': features['wayland'],
'x11': features['x11']},
diff --git a/meson.options b/meson.options
index 836d16d03f..54ec2dccfc 100644
--- a/meson.options
+++ b/meson.options
@@ -103,6 +103,7 @@ option('d3d-hwaccel', type: 'feature', value: 'auto', description: 'D3D11VA hwac
option('d3d9-hwaccel', type: 'feature', value: 'auto', description: 'DXVA2 hwaccel')
option('gl-dxinterop-d3d9', type: 'feature', value: 'auto', description: 'OpenGL/DirectX DXVA2 hwaccel')
option('ios-gl', type: 'feature', value: 'auto', description: 'iOS OpenGL ES interop support')
+option('v4l2request', type: 'feature', value: 'auto', description: 'V4L2 Request API hwaccel')
option('videotoolbox-gl', type: 'feature', value: 'auto', description: 'Videotoolbox with OpenGL')
option('videotoolbox-pl', type: 'feature', value: 'auto', description: 'Videotoolbox with libplacebo')
--
2.52.0
@@ -0,0 +1,81 @@
From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001
From: Markus Fritsche <fritsche.markus@gmail.com>
Date: Fri, 8 May 2026 23:30:00 +0000
Subject: [PATCH] vo_dmabuf_wayland: explicit DMA_BUF_IOCTL_SYNC on import fds
V4L2 does not attach implicit fences (dma_resv) to CAPTURE buffers
on VIDIOC_DQBUF. When the buffer is forwarded to a Wayland compositor
that imports it via wl_dmabuf and samples in the GPU, the GPU may
read from physical memory before the producer's writes have flushed,
producing all-zero output (manifests as solid green for BT.601
limited-range YUV(0,0,0) -> RGB(0, 135, 0) on the consumer side).
Issue an explicit DMA_BUF_IOCTL_SYNC(SYNC_START|SYNC_RW) +
SYNC_END(SYNC_RW) round-trip on each unique dma_buf fd before
zwp_linux_buffer_params_v1_add(). This invokes the producer driver's
dma_buf_ops->begin_cpu_access / end_cpu_access, which on most ARM
SoCs flushes write buffers and synchronizes coherent memory before
the compositor's GPU import.
This is a userspace workaround. Root cause is the missing implicit
fence on V4L2 CAPTURE DQBUF and is being addressed upstream via
the vb2_dma_resv RFC.
Without this patch, on RK3566 (hantro VPU + Mali-G52 panfrost +
KDE Plasma 6 / KWin 6.6.4), `mpv --hwdec=vaapi --vo=dmabuf-wayland`
shows solid green frames for all hardware-decoded content. With
this patch, decoded frames are presented correctly.
Signed-off-by: Markus Fritsche <fritsche.markus@gmail.com>
---
diff --git a/video/out/vo_dmabuf_wayland.c b/video/out/vo_dmabuf_wayland.c
index 6b7c511..16e3d18 100644
--- a/video/out/vo_dmabuf_wayland.c
+++ b/video/out/vo_dmabuf_wayland.c
@@ -27,6 +27,12 @@
#include <va/va_drmcommon.h>
#endif
+/* fourier patch: explicit dma_buf cache sync workaround for missing
+ * implicit-fence on V4L2 stateless CAPTURE buffers. Applies to both
+ * VAAPI and DRMPrime import paths. */
+#include <linux/dma-buf.h>
+#include <sys/ioctl.h>
+
#include "gpu/hwdec.h"
#include "gpu/video.h"
#include "mpv_talloc.h"
@@ -205,6 +211,14 @@ static void vaapi_dmabuf_importer(struct buffer *buf, struct mp_image *src,
buf->drm_format = 0;
goto done;
}
+ /* fourier patch: explicit cache coherency sync on each dma_buf fd
+ * before submitting to the compositor. See top-of-file comment. */
+ for (int obj_no = 0; obj_no < desc.num_objects; obj_no++) {
+ struct dma_buf_sync sync = { .flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW };
+ (void)ioctl(desc.objects[obj_no].fd, DMA_BUF_IOCTL_SYNC, &sync);
+ sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;
+ (void)ioctl(desc.objects[obj_no].fd, DMA_BUF_IOCTL_SYNC, &sync);
+ }
for (int plane_no = 0; plane_no < desc.layers[layer_no].num_planes; ++plane_no) {
int object = desc.layers[layer_no].object_index[plane_no];
uint64_t modifier = desc.objects[object].drm_format_modifier;
@@ -258,6 +272,16 @@ static void drmprime_dmabuf_importer(struct buffer *buf, struct mp_image *src,
return;
buf->id = drmprime_surface_id(src);
+
+ /* fourier patch: explicit cache coherency sync on each dma_buf fd
+ * before submitting to the compositor. See top-of-file comment. */
+ for (int obj_no = 0; obj_no < desc->nb_objects; obj_no++) {
+ struct dma_buf_sync sync = { .flags = DMA_BUF_SYNC_START | DMA_BUF_SYNC_RW };
+ (void)ioctl(desc->objects[obj_no].fd, DMA_BUF_IOCTL_SYNC, &sync);
+ sync.flags = DMA_BUF_SYNC_END | DMA_BUF_SYNC_RW;
+ (void)ioctl(desc->objects[obj_no].fd, DMA_BUF_IOCTL_SYNC, &sync);
+ }
+
for (layer_no = 0; layer_no < desc->nb_layers; layer_no++) {
AVDRMLayerDescriptor layer = desc->layers[layer_no];
--
2.51.0
@@ -0,0 +1,435 @@
From dd1e1fd6fe884d66c49dc26af715e1423c7471a3 Mon Sep 17 00:00:00 2001
From: Philip Langdale <philipl@overt.org>
Date: Sun, 18 Aug 2024 17:43:41 -0700
Subject: [PATCH 2/2] vo: hwdec: drmprime: add separate hwdecs for v4l2request
With all the machinery in place, we can now add the v4l2request hwdecs with a
different hw device type, and a different initialisation path. This applies to
both the drmprime and drmprime_overlay hwdecs.
At the moment, the device initialisation is done in the bare minimum way, but
it can be extended to take a device path (for example) if that makes sense as
we better understand what meaningful configuration will be.
Co-authored-by: Jonas Karlman <jonas@kwiboo.se>
---
video/hwdec.c | 3 +
video/hwdec.h | 1 +
video/out/gpu/hwdec.c | 6 ++
video/out/hwdec/hwdec_drmprime.c | 125 +++++++++++++++++------
video/out/hwdec/hwdec_drmprime_overlay.c | 81 +++++++++++++--
video/out/vo_dmabuf_wayland.c | 1 +
video/v4l2request.c | 34 ++++++
7 files changed, 210 insertions(+), 41 deletions(-)
create mode 100644 video/v4l2request.c
diff --git a/video/hwdec.c b/video/hwdec.c
index deba518e82..de2ffecc40 100644
--- a/video/hwdec.c
+++ b/video/hwdec.c
@@ -125,6 +125,9 @@ static const struct hwcontext_fns *const hwcontext_fns[] = {
#if HAVE_DRM
&hwcontext_fns_drmprime,
#endif
+#if HAVE_V4L2REQUEST
+ &hwcontext_fns_v4l2request,
+#endif
#if HAVE_VAAPI
&hwcontext_fns_vaapi,
#endif
diff --git a/video/hwdec.h b/video/hwdec.h
index e7734e5d7e..bf337389cb 100644
--- a/video/hwdec.h
+++ b/video/hwdec.h
@@ -119,6 +119,7 @@ extern const struct hwcontext_fns hwcontext_fns_cuda;
extern const struct hwcontext_fns hwcontext_fns_d3d11;
extern const struct hwcontext_fns hwcontext_fns_drmprime;
extern const struct hwcontext_fns hwcontext_fns_dxva2;
+extern const struct hwcontext_fns hwcontext_fns_v4l2request;
extern const struct hwcontext_fns hwcontext_fns_vaapi;
extern const struct hwcontext_fns hwcontext_fns_vdpau;
diff --git a/video/out/gpu/hwdec.c b/video/out/gpu/hwdec.c
index be39c507d0..f50b927851 100644
--- a/video/out/gpu/hwdec.c
+++ b/video/out/gpu/hwdec.c
@@ -38,6 +38,8 @@ extern const struct ra_hwdec_driver ra_hwdec_drmprime;
extern const struct ra_hwdec_driver ra_hwdec_drmprime_overlay;
extern const struct ra_hwdec_driver ra_hwdec_aimagereader;
extern const struct ra_hwdec_driver ra_hwdec_vulkan;
+extern const struct ra_hwdec_driver ra_hwdec_v4l2request;
+extern const struct ra_hwdec_driver ra_hwdec_v4l2request_overlay;
const struct ra_hwdec_driver *const ra_hwdec_drivers[] = {
#if HAVE_D3D_HWACCEL
@@ -73,6 +75,10 @@ const struct ra_hwdec_driver *const ra_hwdec_drivers[] = {
&ra_hwdec_drmprime,
&ra_hwdec_drmprime_overlay,
#endif
+#if HAVE_V4L2REQUEST
+ &ra_hwdec_v4l2request,
+ &ra_hwdec_v4l2request_overlay,
+#endif
#if HAVE_ANDROID_MEDIA_NDK
&ra_hwdec_aimagereader,
#endif
diff --git a/video/out/hwdec/hwdec_drmprime.c b/video/out/hwdec/hwdec_drmprime.c
index 7869eb124a..446f63de44 100644
--- a/video/out/hwdec/hwdec_drmprime.c
+++ b/video/out/hwdec/hwdec_drmprime.c
@@ -77,7 +77,7 @@ static const char *forked_pix_fmt_names[] = {
"rpi4_10",
};
-static int init(struct ra_hwdec *hw)
+static int pre_init(struct ra_hwdec *hw)
{
struct priv_owner *p = hw->priv;
@@ -92,36 +92,12 @@ static int init(struct ra_hwdec *hw)
return -1;
}
- /*
- * The drm_params resource is not provided when using X11 or Wayland, but
- * there are extensions that supposedly provide this information from the
- * drivers. Not properly documented. Of course.
- */
- mpv_opengl_drm_params_v2 *params = ra_get_native_resource(hw->ra_ctx->ra,
- "drm_params_v2");
-
- /*
- * Respect drm_device option, so there is a way to control this when not
- * using a DRM gpu context. If drm_params_v2 are present, they will already
- * respect this option.
- */
- void *tmp = talloc_new(NULL);
- struct drm_opts *drm_opts = mp_get_config_group(tmp, hw->global, &drm_conf);
- const char *opt_path = drm_opts->device_path;
-
- const char *device_path = params && params->render_fd > -1 ?
- drmGetRenderDeviceNameFromFd(params->render_fd) :
- opt_path ? opt_path : "/dev/dri/renderD128";
- MP_VERBOSE(hw, "Using DRM device: %s\n", device_path);
+ return 0;
+}
- int ret = av_hwdevice_ctx_create(&p->hwctx.av_device_ref,
- AV_HWDEVICE_TYPE_DRM,
- device_path, NULL, 0);
- talloc_free(tmp);
- if (ret != 0) {
- MP_VERBOSE(hw, "Failed to create hwdevice_ctx: %s\n", av_err2str(ret));
- return -1;
- }
+static int post_init(struct ra_hwdec *hw)
+{
+ struct priv_owner *p = hw->priv;
/*
* At the moment, there is no way to discover compatible formats
@@ -154,6 +130,75 @@ static int init(struct ra_hwdec *hw)
return 0;
}
+static int init_drmprime(struct ra_hwdec *hw)
+{
+ struct priv_owner *p = hw->priv;
+
+ int ret = pre_init(hw);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * The drm_params resource is not provided when using X11 or Wayland, but
+ * there are extensions that supposedly provide this information from the
+ * drivers. Not properly documented. Of course.
+ */
+ mpv_opengl_drm_params_v2 *params = ra_get_native_resource(hw->ra_ctx->ra,
+ "drm_params_v2");
+
+ /*
+ * Respect drm_device option, so there is a way to control this when not
+ * using a DRM gpu context. If drm_params_v2 are present, they will already
+ * respect this option.
+ */
+ void *tmp = talloc_new(NULL);
+ struct drm_opts *drm_opts = mp_get_config_group(tmp, hw->global, &drm_conf);
+ const char *opt_path = drm_opts->device_path;
+
+ const char *device_path = params && params->render_fd > -1 ?
+ drmGetRenderDeviceNameFromFd(params->render_fd) :
+ opt_path ? opt_path : "/dev/dri/renderD128";
+ MP_VERBOSE(hw, "Using DRM device: %s\n", device_path);
+
+ ret = av_hwdevice_ctx_create(&p->hwctx.av_device_ref,
+ AV_HWDEVICE_TYPE_DRM,
+ device_path, NULL, 0);
+ talloc_free(tmp);
+ if (ret < 0) {
+ MP_VERBOSE(hw, "Failed to create hwdevice_ctx: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ return post_init(hw);
+}
+
+#if HAVE_V4L2REQUEST
+static int init_v4l2request(struct ra_hwdec *hw)
+{
+ struct priv_owner *p = hw->priv;
+
+ int ret = pre_init(hw);
+ if (ret < 0)
+ return ret;
+
+ /*
+ * AVCodecHWConfig contains a combo of a pixel format and hwdevice type,
+ * correct type must be created here or hwaccel will fail.
+ *
+ * FIXME: Create hwdevice based on type in AVCodecHWConfig
+ */
+ ret = av_hwdevice_ctx_create(&p->hwctx.av_device_ref,
+ AV_HWDEVICE_TYPE_V4L2REQUEST,
+ NULL, NULL, 0);
+ if (ret < 0) {
+ MP_VERBOSE(hw, "Failed to create hwdevice_ctx: %s\n", av_err2str(ret));
+ return ret;
+ }
+
+ return post_init(hw);
+}
+#endif
+
static void mapper_unmap(struct ra_hwdec_mapper *mapper)
{
struct priv_owner *p_owner = mapper->owner->priv;
@@ -308,7 +353,7 @@ const struct ra_hwdec_driver ra_hwdec_drmprime = {
.priv_size = sizeof(struct priv_owner),
.imgfmts = {IMGFMT_DRMPRIME, 0},
.device_type = AV_HWDEVICE_TYPE_DRM,
- .init = init,
+ .init = init_drmprime,
.uninit = uninit,
.mapper = &(const struct ra_hwdec_mapper_driver){
.priv_size = sizeof(struct dmabuf_interop_priv),
@@ -318,3 +363,21 @@ const struct ra_hwdec_driver ra_hwdec_drmprime = {
.unmap = mapper_unmap,
},
};
+
+#if HAVE_V4L2REQUEST
+const struct ra_hwdec_driver ra_hwdec_v4l2request = {
+ .name = "v4l2request",
+ .priv_size = sizeof(struct priv_owner),
+ .imgfmts = {IMGFMT_DRMPRIME, 0},
+ .device_type = AV_HWDEVICE_TYPE_V4L2REQUEST,
+ .init = init_v4l2request,
+ .uninit = uninit,
+ .mapper = &(const struct ra_hwdec_mapper_driver){
+ .priv_size = sizeof(struct dmabuf_interop_priv),
+ .init = mapper_init,
+ .uninit = mapper_uninit,
+ .map = mapper_map,
+ .unmap = mapper_unmap,
+ },
+};
+#endif
diff --git a/video/out/hwdec/hwdec_drmprime_overlay.c b/video/out/hwdec/hwdec_drmprime_overlay.c
index 61514f8e89..689e9b04e5 100644
--- a/video/out/hwdec/hwdec_drmprime_overlay.c
+++ b/video/out/hwdec/hwdec_drmprime_overlay.c
@@ -246,7 +246,7 @@ static void uninit(struct ra_hwdec *hw)
}
}
-static int init(struct ra_hwdec *hw)
+static int pre_init(struct ra_hwdec *hw)
{
struct priv *p = hw->priv;
int draw_plane, drmprime_video_plane;
@@ -267,15 +267,15 @@ static int init(struct ra_hwdec *hw)
drm_params->connector_id, draw_plane, drmprime_video_plane);
if (!p->ctx) {
mp_err(p->log, "Failed to retrieve DRM atomic context.\n");
- goto err;
+ return -1;
}
if (!p->ctx->drmprime_video_plane) {
mp_warn(p->log, "No drmprime video plane. You might need to specify it manually using --drm-drmprime-video-plane\n");
- goto err;
+ return -1;
}
} else {
mp_verbose(p->log, "Failed to retrieve DRM fd from native display.\n");
- goto err;
+ return -1;
}
drmModeCrtcPtr crtc;
@@ -289,7 +289,7 @@ static int init(struct ra_hwdec *hw)
uint64_t has_prime;
if (drmGetCap(p->ctx->fd, DRM_CAP_PRIME, &has_prime) < 0) {
MP_ERR(hw, "Card does not support prime handles.\n");
- goto err;
+ return -1;
}
if (has_prime) {
@@ -298,19 +298,67 @@ static int init(struct ra_hwdec *hw)
disable_video_plane(hw);
+ return 0;
+}
+
+static int init_drmprime(struct ra_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+
+ int ret = pre_init(hw);
+ if (ret < 0)
+ goto err;
+
p->hwctx = (struct mp_hwdec_ctx) {
.driver_name = hw->driver->name,
.hw_imgfmt = IMGFMT_DRMPRIME,
};
char *device = drmGetDeviceNameFromFd2(p->ctx->fd);
- int ret = av_hwdevice_ctx_create(&p->hwctx.av_device_ref,
- AV_HWDEVICE_TYPE_DRM, device, NULL, 0);
+ ret = av_hwdevice_ctx_create(&p->hwctx.av_device_ref,
+ AV_HWDEVICE_TYPE_DRM, device, NULL, 0);
if (device)
free(device);
- if (ret != 0) {
+ if (ret < 0) {
+ MP_VERBOSE(hw, "Failed to create hwdevice_ctx: %s\n", av_err2str(ret));
+ goto err;
+ }
+
+ hwdec_devices_add(hw->devs, &p->hwctx);
+
+ return 0;
+
+err:
+ uninit(hw);
+ return ret;
+}
+
+#if HAVE_V4L2REQUEST
+static int init_v4l2request(struct ra_hwdec *hw)
+{
+ struct priv *p = hw->priv;
+
+ int ret = pre_init(hw);
+ if (ret < 0)
+ goto err;
+
+ p->hwctx = (struct mp_hwdec_ctx) {
+ .driver_name = hw->driver->name,
+ .hw_imgfmt = IMGFMT_DRMPRIME,
+ };
+
+ /*
+ * AVCodecHWConfig contains a combo of a pixel format and hwdevice type,
+ * correct type must be created here or hwaccel will fail.
+ *
+ * FIXME: Create hwdevice based on type in AVCodecHWConfig
+ */
+ ret = av_hwdevice_ctx_create(&p->hwctx.av_device_ref,
+ AV_HWDEVICE_TYPE_V4L2REQUEST,
+ NULL, NULL, 0);
+ if (ret < 0) {
MP_VERBOSE(hw, "Failed to create hwdevice_ctx: %s\n", av_err2str(ret));
goto err;
}
@@ -321,15 +369,28 @@ static int init(struct ra_hwdec *hw)
err:
uninit(hw);
- return -1;
+ return ret;
}
+#endif
const struct ra_hwdec_driver ra_hwdec_drmprime_overlay = {
.name = "drmprime-overlay",
.priv_size = sizeof(struct priv),
.imgfmts = {IMGFMT_DRMPRIME, 0},
.device_type = AV_HWDEVICE_TYPE_DRM,
- .init = init,
+ .init = init_drmprime,
+ .overlay_frame = overlay_frame,
+ .uninit = uninit,
+};
+
+#if HAVE_V4L2REQUEST
+const struct ra_hwdec_driver ra_hwdec_v4l2request_overlay = {
+ .name = "v4l2request-overlay",
+ .priv_size = sizeof(struct priv),
+ .imgfmts = {IMGFMT_DRMPRIME, 0},
+ .device_type = AV_HWDEVICE_TYPE_V4L2REQUEST,
+ .init = init_v4l2request,
.overlay_frame = overlay_frame,
.uninit = uninit,
};
+#endif
diff --git a/video/out/vo_dmabuf_wayland.c b/video/out/vo_dmabuf_wayland.c
index 9b06643544..6d62849568 100644
--- a/video/out/vo_dmabuf_wayland.c
+++ b/video/out/vo_dmabuf_wayland.c
@@ -860,6 +860,7 @@ static int preinit(struct vo *vo)
// Initialize all possible hwdec drivers.
ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, "vaapi", false);
ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, "drmprime", false);
+ ra_hwdec_ctx_init(&p->hwdec_ctx, vo->hwdec_devs, "v4l2request", false);
p->src = (struct mp_rect){0, 0, 0, 0};
return 0;
diff --git a/video/v4l2request.c b/video/v4l2request.c
new file mode 100644
index 0000000000..2aa4d14fea
--- /dev/null
+++ b/video/v4l2request.c
@@ -0,0 +1,34 @@
+/*
+ * This file is part of mpv.
+ *
+ * mpv is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * mpv is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with mpv. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <libavutil/hwcontext.h>
+
+#include "hwdec.h"
+
+static struct AVBufferRef *v4l2request_create_standalone(struct mpv_global *global,
+ struct mp_log *log, struct hwcontext_create_dev_params *params)
+{
+ AVBufferRef* ref = NULL;
+ av_hwdevice_ctx_create(&ref, AV_HWDEVICE_TYPE_V4L2REQUEST, NULL, NULL, 0);
+
+ return ref;
+}
+
+const struct hwcontext_fns hwcontext_fns_v4l2request = {
+ .av_hwdevice_type = AV_HWDEVICE_TYPE_V4L2REQUEST,
+ .create_dev = v4l2request_create_standalone,
+};
--
2.52.0
+134
View File
@@ -0,0 +1,134 @@
#!/bin/bash
# Build mpv-fourier_<ver>_arm64.deb (mpv with v4l2request hwdec wiring +
# vo_dmabuf_wayland cache-sync fix).
#
# Mirror of arch/mpv-fourier into the Debian tree. Same source pin
# (mpv 0.41.0), same 3 patches, same meson options.
#
# Depends on ffmpeg-v4l2-request-fourier for the libavcodec backing
# library — the v4l2request hwdec mpv now offers requires
# AV_HWDEVICE_TYPE_V4L2REQUEST which only exists in Kwiboo's FFmpeg
# fork, not in Debian stock ffmpeg.
#
# Conflicts: mpv — drop-in replacement. Pi 5 / CM5 hosts using stock
# Firefox + VAAPI don't need this; the firefox-fourier / kdirect
# bench-test paths do.
#
# Sibling Arch package: ../../arch/mpv-fourier/PKGBUILD
# Sibling FFmpeg package: ../ffmpeg-v4l2-request-fourier/build-deb.sh
# Upstream: https://github.com/mpv-player/mpv
set -euo pipefail
MPV_VERSION=0.41.0
PKGVER=1:${MPV_VERSION}+rfourier
PKGREL=1
MPV_TARBALL_SHA256=ee21092a5ee427353392360929dc64645c54479aefdb5babc5cfbb5fad626209
HERE=$(dirname "$(readlink -f "$0")")
# Reproducible build. 2026-05-18 23:00 UTC — Phase 8.13 close.
export SOURCE_DATE_EPOCH=1779231600
work=$(mktemp -d)
trap "rm -rf $work" EXIT
cd "$work"
curl -sSLfo mpv.tar.gz \
"https://github.com/mpv-player/mpv/archive/v${MPV_VERSION}/mpv-${MPV_VERSION}.tar.gz"
echo "$MPV_TARBALL_SHA256 mpv.tar.gz" | sha256sum -c
tar xzf mpv.tar.gz
cd "mpv-${MPV_VERSION}"
# Apply the 3 fourier patches.
patch -p1 < "$HERE/0001-meson-add-detection-logic-for-v4l2request-support.patch"
patch -p1 < "$HERE/0002-vo-hwdec-drmprime-add-separate-hwdecs-for-v4l2reques.patch"
patch -p1 < "$HERE/0001-vo_dmabuf_wayland-explicit-cache-sync-on-import-fd.patch"
# Configure. Same shape as arch/mpv-fourier's meson options.
meson setup build \
--prefix=/usr \
--libdir=lib/aarch64-linux-gnu \
--buildtype=release \
--auto-features=auto \
-Dv4l2request=enabled \
-Dlibmpv=true \
-Dgl-x11=enabled \
-Dcaca=disabled \
-Dcdda=enabled \
-Ddrm=enabled \
-Ddvbin=enabled \
-Ddvdnav=enabled \
-Dlibarchive=enabled \
-Dopenal=enabled \
-Dsdl2-audio=enabled \
-Dsdl2-video=enabled \
-Dsdl2-gamepad=enabled
meson compile -C build -j"$(nproc)"
# Stage
ROOT="$work/pkgroot"
DESTDIR="$ROOT" meson install -C build
# Drop private linkage entries from the .pc (only matter for static linking).
sed -i -e '/Requires.private/d' -e '/Libs.private/d' \
"$ROOT/usr/lib/aarch64-linux-gnu/pkgconfig/mpv.pc"
# Docs
mkdir -p "$ROOT/usr/share/doc/mpv-fourier" "$ROOT/DEBIAN"
install -m 644 DOCS/encoding.rst "$ROOT/usr/share/doc/mpv-fourier/"
install -m 644 DOCS/tech-overview.txt "$ROOT/usr/share/doc/mpv-fourier/"
install -Dm644 "$HERE/debian/copyright" "$ROOT/usr/share/doc/mpv-fourier/copyright"
install -Dm644 "$HERE/debian/changelog" "$ROOT/usr/share/doc/mpv-fourier/changelog.Debian"
gzip -9 -n "$ROOT/usr/share/doc/mpv-fourier/changelog.Debian"
# TOOLS scripts
mkdir -p "$ROOT/usr/share/mpv/scripts"
install -m 644 TOOLS/umpv TOOLS/mpv_identify.sh TOOLS/stats-conv.py TOOLS/idet.sh \
"$ROOT/usr/share/mpv/scripts/" 2>/dev/null || true
[ -d TOOLS/lua ] && cp TOOLS/lua/* "$ROOT/usr/share/mpv/scripts/" 2>/dev/null || true
cat > "$ROOT/DEBIAN/control" <<EOF
Package: mpv-fourier
Version: ${PKGVER}-${PKGREL}
Section: video
Priority: optional
Architecture: arm64
Depends: ffmpeg-v4l2-request-fourier (>= 2:8.1+rfourier),
libc6, libdrm2,
libass9, libbluray2, libcdio19, libcdio-paranoia2,
libdvdnav4, libdvdread8,
libegl1, libgl1, libjpeg62-turbo | libjpeg62,
libplacebo338 | libplacebo208,
libpulse0, libsixel1, libluajit-5.1-2,
libmujs3 | libmujs2 | libmujs1,
libpipewire-0.3-0, librubberband2, libsdl2-2.0-0,
libopenal1, libuchardet0, libvulkan1,
libwayland-client0, libwayland-egl1, libwayland-cursor0,
libxkbcommon0, libxext6, libxpresent1,
libxrandr2, libxss1, libxv1, libx11-6,
zlib1g
Conflicts: mpv, libmpv2 | libmpv1
Replaces: mpv, libmpv2, libmpv1
Provides: mpv (= ${PKGVER}-${PKGREL}), libmpv.so.2
Maintainer: Markus Fritsche <mfritsche@reauktion.de>
Homepage: https://mpv.io/
Description: mpv media player with V4L2-Request hwdec (fourier fork)
mpv ${MPV_VERSION} patched with:
- Kwiboo + Langdale v4l2request hwdec wiring (meson detection +
vo_hwdec_drmprime separate hwdec instance for AV_HWDEVICE_TYPE_
V4L2REQUEST) so '--hwdec=v4l2request' actually engages on
dmabuf-wayland.
- vo_dmabuf_wayland explicit DMA_BUF_IOCTL_SYNC on import fds
(iter1 of dmabuf-modifier-triage) — workaround for missing
implicit-fence on V4L2 stateless CAPTURE buffers. Fixes the
KWin-on-RK3566 dark-green chroma readback regression on ohm.
.
Requires ffmpeg-v4l2-request-fourier as the backing libavcodec.
Stock Debian ffmpeg does not provide AV_HWDEVICE_TYPE_V4L2REQUEST.
EOF
DEB_OUT="mpv-fourier_${PKGVER//:/%3a}-${PKGREL}_arm64.deb"
dpkg-deb --root-owner-group --build "$ROOT" "$HERE/$DEB_OUT"
echo "built: $HERE/$DEB_OUT"
+12
View File
@@ -0,0 +1,12 @@
mpv-fourier (1:0.41.0+rfourier-1) bookworm trixie; urgency=medium
* Initial Debian packaging for mpv with the fourier-umbrella patches.
* Mirror of arch/mpv-fourier (same source tarball pin 0.41.0,
same 3 patches: v4l2request meson detection, drmprime hwdec
wiring, vo_dmabuf_wayland explicit cache-sync).
* Depends on ffmpeg-v4l2-request-fourier (>= 2:8.1+rfourier) — the
v4l2request hwdec requires AV_HWDEVICE_TYPE_V4L2REQUEST which
only exists in Kwiboo's FFmpeg fork.
* Drop-in replacement for Debian's stock mpv; takes epoch 1.
-- Markus Fritsche <mfritsche@reauktion.de> Mon, 18 May 2026 23:00:00 +0000
+70
View File
@@ -0,0 +1,70 @@
Source: mpv-fourier
Section: video
Priority: optional
Maintainer: Markus Fritsche <mfritsche@reauktion.de>
Build-Depends: debhelper-compat (= 13),
meson (>= 1.0),
ninja-build,
pkg-config,
python3-docutils,
ffmpeg-v4l2-request-fourier (>= 2:8.1+rfourier),
libdrm-dev,
libass-dev,
libbluray-dev,
libcdio-dev,
libcdio-paranoia-dev,
libdvdnav-dev,
libdvdread-dev,
libegl-dev,
libgl-dev,
libjpeg-dev,
libplacebo-dev,
libpulse-dev,
libsixel-dev,
libluajit-5.1-dev,
libmujs-dev,
libpipewire-0.3-dev,
librubberband-dev,
libsdl2-dev,
libopenal-dev,
libuchardet-dev,
libvulkan-dev,
libwayland-dev,
wayland-protocols,
libxkbcommon-dev,
libxext-dev,
libxpresent-dev,
libxrandr-dev,
libxss-dev,
libxv-dev,
libx11-dev,
libv4l-dev,
libudev-dev,
libsystemd-dev,
libarchive-dev,
libxml2-dev,
libdvbpsi-dev,
zlib1g-dev
Standards-Version: 4.6.2
Homepage: https://mpv.io/
Package: mpv-fourier
Architecture: arm64
Depends: ${misc:Depends}, ${shlibs:Depends},
ffmpeg-v4l2-request-fourier (>= 2:8.1+rfourier)
Conflicts: mpv, libmpv2, libmpv1
Replaces: mpv, libmpv2, libmpv1
Provides: mpv (= ${binary:Version}), libmpv.so.2
Description: mpv media player with V4L2-Request hwdec (fourier fork)
mpv 0.41.0 patched with:
- Kwiboo + Langdale v4l2request hwdec wiring (meson detection +
vo_hwdec_drmprime separate hwdec instance for AV_HWDEVICE_TYPE_
V4L2REQUEST) so '--hwdec=v4l2request' actually engages on
dmabuf-wayland.
- vo_dmabuf_wayland explicit DMA_BUF_IOCTL_SYNC on import fds
(iter1 of dmabuf-modifier-triage) — workaround for missing
implicit-fence on V4L2 stateless CAPTURE buffers. Fixes the
KWin-on-RK3566 dark-green chroma readback regression on ohm.
.
Requires ffmpeg-v4l2-request-fourier as the backing libavcodec.
Stock Debian ffmpeg does not provide AV_HWDEVICE_TYPE_V4L2REQUEST.
+25
View File
@@ -0,0 +1,25 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: mpv
Upstream-Contact: https://github.com/mpv-player/mpv/issues
Source: https://github.com/mpv-player/mpv
Files: *
Copyright: 2000-2026 The mpv developers
License: GPL-2.0-or-later AND LGPL-2.1-or-later
Files: 0001-meson-add-detection-logic-for-v4l2request-support.patch
0002-vo-hwdec-drmprime-add-separate-hwdecs-for-v4l2reques.patch
Copyright: 2024 Kwiboo, Niklas Haas, contributors
License: LGPL-2.1-or-later
Files: 0001-vo_dmabuf_wayland-explicit-cache-sync-on-import-fd.patch
Copyright: 2026 Markus Fritsche <fritsche.markus@gmail.com>
License: LGPL-2.1-or-later
License: GPL-2.0-or-later
On Debian systems, the complete text of the GNU General Public
License v2 can be found in `/usr/share/common-licenses/GPL-2'.
License: LGPL-2.1-or-later
On Debian systems, the complete text of the GNU Lesser General
Public License v2.1 can be found in `/usr/share/common-licenses/LGPL-2.1'.