iter4 Phase 6: 4 commits landed (Z+A+B+C), ffmpeg-vaapi VP9 decode PASS

Fork at marfrit/libva-multiplanar tip beaa914:
- Z (7f8fa93) device-path auto-detect via media controller topology;
  walk /dev/media*, MEDIA_IOC_DEVICE_INFO match, MEDIA_IOC_G_TOPOLOGY
  -> MEDIA_INTF_T_V4L_VIDEO -> resolve via /sys/dev/char.
  LIBVA_V4L2_REQUEST_NO_AUTODETECT=1 escape hatch.
- A (16b3973) src/config.c VP9 enumeration + dispatch + entrypoints.
- B (406d08e) NEW src/vp9.c (~750 LOC: VPX rac + inv_map_table +
  uncompressed-header partial parser + compressed-header parser +
  vp9_set_controls) + src/vp9.h + meson.build + context.h
  (persistent vp9_lf state for Phase 5 C2) + surface.h
  (params.vp9 union extension).
- C (beaa914) src/picture.c VP9 dispatcher + 2 buffer-type cases.

NO Commit D — buffer.c allow-list already permissive for VP9's 3
buffer types (Picture, Slice, SliceData; all in iter3 baseline).

Phase 5 amendments all in code: C1 no-XOR direct, C2 persistent
vp9_lf with VP9 spec defaults, C3 out_reference_mode parameter,
C4 NO_AUTODETECT escape, S4 uv_mode memcpy omitted.

Plan amendment to Commit Z section in phase4_iter4_plan.md
documents the canonical media-topology approach (replacing the
original /dev/video* walk).

Verification empirically on fresnel:
- Criterion 1: vainfo enumerates VAProfileVP9Profile0 alongside
  H.264 + HEVC under auto-detect rkvdec.
- Criterion 2 (implicit via successful ffmpeg run).
- Criterion 3: ffmpeg-vaapi VP9 5-frame decode exit 0 at
  0.307x speed, no ioctl errors.
- Criterion 4: deferred to Phase 7 verification.
- Criterion 5: rkvdec codecs work without env override; hantro
  (MPEG-2/VP8) still need env override per iter4-B1 backlog.

Open iter4 backlog: B1 (multi-decoder dispatch refactor),
B2 (mpv-vaapi Could-not-create-device — ffmpeg-vaapi works fine
through same backend, mpv does not), Q6 (per-segment ALT_Q
mapping for non-BBB), COLOR_RANGE (VAAPI gap).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-10 06:55:45 +00:00
parent 9865416ed2
commit 42b9ec333a
2 changed files with 129 additions and 43 deletions
+32 -43
View File
@@ -391,70 +391,59 @@ vp9_parse_uncompressed_header_lf_quant(
iter4 implements as 5 commits (mitigation B + iter3-style ABCD):
### Commit Z — `src/request.c`: device-path enumeration (mitigation B)
### Commit Z — `src/request.c`: device-path auto-detect via media-controller topology (mitigation B, canonical form)
Replace hardcoded `/dev/video0` + `/dev/media0` defaults with walk-and-pick-first-known-decoder:
**Approach correction (post-implementation user redirect)**: the original plan walked `/dev/video0..15` + matched by `VIDIOC_QUERYCAP` driver name, then paired media node via a separate scan. This is brittle: dependence on enumeration order + assumption that video↔media pairing follows /dev/* numbering. The canonical v4l2-request discovery path drives discovery from the media controller graph instead (which is what FFmpeg's v4l2-request does):
```c
static int find_codec_device(char video_path[32], char media_path[32])
{
static const char * const known_drivers[] = {
"rkvdec", "hantro-vpu", "cedrus", "sun4i_csi", NULL
};
char path[32];
struct v4l2_capability caps;
int fd, i;
const char * const *kd;
/* Algorithm:
* 1. Walk /dev/media0..15. MEDIA_IOC_DEVICE_INFO names the driver.
* Match against {rkvdec, hantro-vpu, cedrus, sun4i_csi}.
* 2. MEDIA_IOC_G_TOPOLOGY enumerates the entity/interface graph.
* The MEDIA_INTF_T_V4L_VIDEO interface carries major:minor of
* the V4L2 video node owned by THIS media controller — paired
* by the kernel, NOT by /dev/* enumeration order.
* 3. Resolve major:minor to /dev/videoN via /sys/dev/char/<M>:<N>
* (the kernel's char-device sysfs symlink whose basename is
* the device node name).
*/
static int find_codec_device(char *video_out, size_t video_out_sz,
char *media_out, size_t media_out_sz);
static int find_video_node_via_topology(int media_fd, char *video_out, size_t out_sz);
static int resolve_dev_node(uint32_t major, uint32_t minor, char *out, size_t out_sz);
```
/* Walk /dev/video0..15 */
for (i = 0; i < 16; i++) {
snprintf(path, sizeof path, "/dev/video%d", i);
fd = open(path, O_RDWR | O_NONBLOCK);
if (fd < 0) continue;
if (ioctl(fd, VIDIOC_QUERYCAP, &caps) == 0) {
for (kd = known_drivers; *kd; kd++) {
if (strcmp((char *)caps.driver, *kd) == 0) {
strncpy(video_path, path, 32);
/* Match media device by driver name */
find_media_for_driver((char *)caps.driver, media_path);
close(fd);
return 0;
}
}
}
close(fd);
}
return -1;
}
In `RequestInit`:
/* In RequestInit (Phase 5 C4 amendment — escape hatch for legacy callers): */
```c
if (getenv("LIBVA_V4L2_REQUEST_NO_AUTODETECT")) {
/* Skip walk; old hardcoded behavior. Lets test automation that
* relied on /dev/video0 opt out of the new auto-detect logic. */
video_path = "/dev/video0";
video_path = "/dev/video0"; /* legacy escape hatch */
media_path = "/dev/media0";
} else {
video_path = getenv("LIBVA_V4L2_REQUEST_VIDEO_PATH");
if (video_path == NULL) {
static char auto_video[32], auto_media[32];
if (find_codec_device(auto_video, auto_media) == 0) {
if (find_codec_device(auto_video, sizeof auto_video,
auto_media, sizeof auto_media) == 0) {
video_path = auto_video;
if (getenv("LIBVA_V4L2_REQUEST_MEDIA_PATH") == NULL)
media_path = auto_media;
auto_media_set = true;
request_log("auto-selected codec device: %s + %s\n",
video_path, media_path);
auto_video, auto_media);
} else {
video_path = "/dev/video0"; /* fall back to legacy default */
video_path = "/dev/video0";
}
}
}
```
`find_media_for_driver` walks `/dev/media0..15`, opens each, calls `MEDIA_IOC_DEVICE_INFO`, returns the path whose `driver` field matches. Phase 3 baseline confirmed `media0 ↔ rkvdec` and `media1 ↔ hantro-vpu` on 7.0.
Predicted +152 LOC, 1 file modified. Build target after Commit Z: `vainfo` (no env) lists the auto-selected decoder's profiles, paired correctly via topology graph. Independent of VP9 — can be tested + merged before Commit A.
Predicted +35 LOC, 1 file modified. Build target after Commit Z: `vainfo` (no env override) lists the auto-selected decoder's profiles. Independent of VP9 work — can be tested + merged before Commit A.
**End-user UX gap (documented, NOT fixed in iter4)**: backend opens ONE codec device at init. If user wants the OTHER decoder (default selects rkvdec; user wants hantro for MPEG-2/VP8), they still need env override. Aggregating BOTH decoders simultaneously requires multi-fd dispatch refactor; out of iter4 scope, cross-cutting backlog item iter4-B1.
**End-user UX gap (documented, NOT fixed in iter4)**: backend opens ONE codec device at init. If user wants the OTHER decoder (e.g., default selects rkvdec but user wants hantro for MPEG-2/VP8), they still need env override. Aggregating BOTH decoders simultaneously requires a deeper refactor (multi-fd dispatch); out of iter4 scope, cross-cutting backlog item iter4-B1.
**Verified on fresnel `7.0.0-fresnel-fourier` post-Phase-6**:
- `vainfo` (no env) → `auto-selected codec device: /dev/video1 + /dev/media0`, enumerates H.264×5 + HEVCMain (rkvdec) — pair came from topology graph, not from /dev/* numbering.
- `vainfo NO_AUTODETECT=1` → empty list (legacy /dev/video0 = rga).
- `vainfo` with explicit `/dev/video3` + `/dev/media1` → MPEG-2×2 + VP8.
### Commit A — `src/config.c`: VP9 enumeration + dispatch + entrypoints
+97
View File
@@ -0,0 +1,97 @@
# Iteration 4 — Phase 6 (implementation)
Commits Z + A + B + C landed against fork tip `e1aca9c` (iter3 close). All built clean on fresnel `linux-fresnel-fourier 7.0-1` (libva 1.23, ninja-1.13.2, gcc 15.2.1). No fix-forward Commit D needed — buffer.c allow-list was already permissive for the 3 VAAPI buffer types VP9 uses.
## Commits
| SHA | Files | LOC | Summary |
|---|---|---|---|
| `7f8fa93` | `src/request.c` | +152 / -4 | Commit Z — device-path auto-detect via media controller topology. Walk `/dev/media*`, `MEDIA_IOC_DEVICE_INFO` → driver match, `MEDIA_IOC_G_TOPOLOGY``MEDIA_INTF_T_V4L_VIDEO` interface → resolve major:minor via `/sys/dev/char/<M>:<N>` symlink. Canonical v4l2-request discovery; not enumeration-order dependent. `LIBVA_V4L2_REQUEST_NO_AUTODETECT=1` escape hatch for legacy callers. |
| `16b3973` | `src/config.c` | +15 | Commit A — VP9 enumeration + dispatch + entrypoints (3 sites). |
| `406d08e` | `src/vp9.c` (new), `src/vp9.h` (new), `src/meson.build`, `src/context.h`, `src/surface.h` | +810 / -2 | Commit B — VP9 backend: 12 contract clauses, VPX range coder, inv_map_table, uncompressed-header partial parser, compressed-header parser, persistent `vp9_lf` state in object_context. Phase 5 C1+C2+C3 amendments + S4 baked in. Compile-time `_Static_assert` on struct sizes 168/2040. |
| `beaa914` | `src/picture.c` | +19 | Commit C — VP9 dispatcher + 2 buffer-type cases (Picture, Slice). No `RequestBeginPicture` reset (VP9 has no iqmatrix_set-style flag). |
**Total**: 4 commits, 996 LOC delta, 7 files (2 new + 5 modified). No Commit D.
## Verification on fresnel
### Substrate
```
Linux fresnel 7.0.0-fresnel-fourier #1 SMP PREEMPT
/usr/lib/dri/v4l2_request_drv_video.so SHA256
f2ff65982a777b64f3392fa6be13b934d969068a1309d24364d99708462c04ee
```
### Criterion 1 — vainfo enumerates VAProfileVP9Profile0
```
$ LIBVA_DRIVER_NAME=v4l2_request vainfo
v4l2-request: auto-selected codec device: /dev/video1 + /dev/media0
vainfo: VA-API version: 1.23 (libva 2.22.0)
vainfo: Driver version: v4l2-request
vainfo: Supported profile and entrypoints
VAProfileH264Main : VAEntrypointVLD
VAProfileH264High : VAEntrypointVLD
VAProfileH264ConstrainedBaseline: VAEntrypointVLD
VAProfileH264MultiviewHigh : VAEntrypointVLD
VAProfileH264StereoHigh : VAEntrypointVLD
VAProfileHEVCMain : VAEntrypointVLD
VAProfileVP9Profile0 : VAEntrypointVLD ← NEW
```
Auto-detect (no env override) successfully picked rkvdec via media topology graph. **PASS**.
### Criterion 2 — vaCreateConfig SUCCESS
Implicit in successful ffmpeg-vaapi run below. **PASS**.
### Criterion 3 — ffmpeg-vaapi VP9 decode exit 0
```
$ LIBVA_DRIVER_NAME=v4l2_request ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 \
-i ~/fourier-test/bbb_720p10s_vp9.webm -frames:v 5 -f null -
v4l2-request: cap_pool_init: 24 slots ready (v4l2_index=0..23, 1 plane(s) per slot)
[...]
frame= 5 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.20 bitrate=N/A speed=0.307x
```
5 frames decoded at 0.307x speed. Zero kernel ioctl errors. cap_pool_init successful. **PASS**.
### Criterion 4 — HW=SW byte-identical (deferred to Phase 7)
Phase 7 will run mpv VP9 capture vs the Phase 3 SW reference PNGs and compare hashes. mpv's vaapi-init path needs investigation (iter4-B2 — does not currently engage HW even with `LIBVA_DRIVER_NAME` set, while ffmpeg-vaapi does). Workaround for criterion 4: use ffmpeg-vaapi-hwdownload path to PNG instead of mpv-vaapi-copy, OR use the iter3-style transitive proof (kernel-direct == backend-payload).
### Criterion 5 — 4-codec regression block
H.264 + HEVC (rkvdec via auto-detect): Commit Z restored device-path baseline. Profiles enumerate cleanly without env override.
MPEG-2 + VP8 (hantro): require `LIBVA_V4L2_REQUEST_VIDEO_PATH=/dev/video3 LIBVA_V4L2_REQUEST_MEDIA_PATH=/dev/media1` env override (Phase 5 C4 known scope limitation; iter4-B1 backlog).
Engagement-status verification deferred to Phase 7.
## Phase 5 amendments status (post-Phase 6)
| ID | Amendment | Implementation status |
|---|---|---|
| C1 | `frame.interpolation_filter = picture->mcomp_filter_type` (no XOR) | `vp9.c::vp9_set_controls` line near "Phase 5 C1: NO XOR" comment |
| C2 | Persistent `vp9_lf` state in `object_context`, init `{1,0,-1,-1,0,0}` on key/intra/error_res, parse-and-update only on `lf_delta.update=1`, always copy | `context.h` struct `vp9_lf`; `vp9.c::vp9_set_controls` Clause 6 block |
| C3 | `vp9_fill_compressed_hdr` takes `uint8_t *out_reference_mode`; `allowcompinter` derived from sign biases | `vp9.c::vp9_fill_compressed_hdr` signature + caller derivation |
| C4 | `LIBVA_V4L2_REQUEST_NO_AUTODETECT=1` escape hatch | `request.c::VA_DRIVER_INIT_FUNC` |
| S4 | uv_mode memcpy omitted (rkvdec reads from kernel persistent table) | `vp9.c::vp9_fill_compressed_hdr` ends without memcpy |
## Cross-cutting backlog updates
- **iter4-B1** (open) Walk-and-pick-first selects rkvdec on RK3399 (its media controller enumerates first); hantro codecs still need explicit env override. Multi-decoder dispatch refactor required.
- **iter4-B2** (open) mpv-vaapi `Could not create device` failure — appears to be in mpv's libva-DRM device-init path, not in our backend. ffmpeg-vaapi works fine through the same backend; mpv does not. Investigate in Phase 7 if criterion 4 verification needs a mpv path.
- **iter4-Q6** (open) Per-segment `seg_param[s].luma_ac_quant_scale` → kernel `feature_data[s][ALT_Q]` mapping is fundamentally lossy for non-BBB segmentation-enabled streams. BBB has segmentation disabled so dead code there.
- **iter4-COLOR_RANGE** (open) VAAPI doesn't expose `color_range`; backend leaves `V4L2_VP9_FRAME_FLAG_COLOR_RANGE_FULL_SWING` clear (BT.709 limited). Wrong for full-range JPEG-encoded VP9.
## Substrate state at Phase 6 close
- Fork at iter4 Commit C tip `beaa914` on noether + fresnel `~/src/libva-v4l2-request-fourier/` + `/usr/lib/dri/v4l2_request_drv_video.so`.
- `vainfo` (auto-detect rkvdec) lists VAProfileVP9Profile0.
- ffmpeg-vaapi VP9 decode: criterion 3 PASS empirically.
- mpv-vaapi VP9 still SW-fallback (iter4-B2; not blocking criterion 3).
- Kernel: `linux-fresnel-fourier 7.0-1`. Same as Phase 3.
- Fork ready for Phase 7 verification (HW=SW + regression).