Phase 8.13: byte-exact end-to-end via libva (consumer target hit)

The project's consumer-side goal landed: a real VAAPI consumer
(ffmpeg with -hwaccel vaapi) drives our libva backend → V4L2
driver → daemon → byte-exact NV12 output back to ffmpeg.

  ffmpeg -hwaccel vaapi -hwaccel_device /dev/dri/renderD128 \
         -hwaccel_output_format nv12 -i vp9_small.ivf \
         -f rawvideo -y /tmp/vp9_via_libva.nv12
  cmp /tmp/vp9_via_libva.nv12 /tmp/vp9_ref_for_libva.nv12  → match

18432-byte NV12 byte-for-byte identical to plain ffmpeg
-pix_fmt nv12 software decode. The project_consumer_target
memory's deliverable shape — "V4L2 stateless node consumed by
a real VAAPI client" — is achieved.

Two related kernel changes:

1. v4l2_ctrl_handler_setup(&ctx->hdl) after registration —
   matches rkvdec/cedrus/hantro. Brings each registered
   compound control out of "uninitialised" state via
   std_init_compound defaults.

2. Per-request control completion in the decode path —
   the real fix for "Timeout when waiting for media request".
   vb2-core's vb2_buffer_done unbinds the BUFFER's req_obj
   on normal decode completion, but the per-request CONTROL
   object stays bound. buf_request_complete fires only from
   queue-cancel paths (vb2-core line 2284), NOT from normal
   buf_done. The driver must call
   v4l2_ctrl_request_complete(req, hdl) explicitly from the
   completion path.

   struct daedalus_inflight gained a `struct media_request
   *req` field, captured from src_buf->vb2_buf.req_obj.req
   in device_run. daedalus_complete_resp_frame then calls
   v4l2_ctrl_request_complete before
   v4l2_m2m_buf_done_and_job_finish — triggers
   MEDIA_REQUEST_STATE_COMPLETE and wakes the request fd
   poll.

   For non-request flows (test_m2m_stream direct QBUF)
   inf->req is NULL; the conditional skips the call.
   Both consumer styles work concurrently.

Diagnostic clarification (was Phase 8.13a):

strace traced three S_EXT_CTRLS calls per frame:
  1. H264_PROFILE + H264_LEVEL → EINVAL  (we don't register)
  2. HEVC_PROFILE + HEVC_LEVEL → EINVAL  (we don't register)
  3. VP9_FRAME + VP9_COMPRESSED_HDR → SUCCESS

The first two are harmless: libva probes whether we support
H264/HEVC integer profile/level controls during config
negotiation; we don't (we expose them as stateless), so EINVAL
just falls through. The actual VP9 stateless controls (#3)
succeeded all along — the libva-side "Unable to set control(s)"
log was misleading us into thinking the control path was the
bug.

Verification on hertz (Pi 5, 6.12.75+rpt-rpi-2712):

  daemon log:
    REQ_DECODE cookie=1 codec=1 bitstream=1566 bytes capture=128x96 1 planes
    decoder: opened vp9 context
    decoder: OK 128x96 fmt=0 (yuv420p) fnv1a=0x1eb34bfe ...

  ffmpeg side:
    no Timeout, no Decoding error
    /tmp/vp9_via_libva.nv12: 18432 bytes

  cmp vs reference: byte-for-byte identical.

Roadmap update:
- 8.10/8.11, 8.12, 8.13 marked closed with closure docs.
- 8.14 = multi-frame VP9 via libva, AV1 + H.264, mpv/Firefox
  higher-level consumers.

Per correctness-before-speed:
- strace + kernel-source-reading found the actual root cause
  rather than guessing.
- Conditional v4l2_ctrl_request_complete preserves the existing
  test_m2m_stream non-request path — both consumer styles work
  concurrently without per-flow branching elsewhere.
- Byte-exact pixel comparison, not "frame size matches."

Phase 8.14 next: multi-frame stream + multi-codec via libva +
mpv/Firefox.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-18 18:14:34 +00:00
parent a7d585eee8
commit f04d7000f8
3 changed files with 297 additions and 10 deletions
+56 -10
View File
@@ -134,18 +134,64 @@ See `docs/phase_8_8_closure.md`.
See `docs/phase_8_9_closure.md`.
### Phase 8.10 — libva-v4l2-request VP9/AV1 patch + end-to-end consumer
### Phase 8.10 + 8.11 — libva consumer integration scaffold (closed 2026-05-18)
1. Build libva-v4l2-request from source on hertz.
2. Add VP9_FRAME + AV1_FRAME profile mappings; add
V4L2_PIX_FMT_NV12 (single-plane) to our CAPTURE so
the library's video.c picks us.
3. End-to-end: `mpv --hwdec=vaapi` against test files;
then Firefox.
4. (Stretch) Upstream the patches to bootlin.
- Forked bootlin/libva-v4l2-request to marfrit/libva-v4l2-
request-fourier (gitea); discovered the existing fourier
fork already had VP9/AV1/HEVC support on Rockchip.
- Added daedalus_v4l2 to known_decoder_drivers + meson
build option.
- Added V4L2_PIX_FMT_NV12 single-plane + request API
media ops + stateless control registration to our
kernel.
- vainfo enumerates 7 VAProfile entries via our driver.
After 8.10 the project's user-facing loop is closed.
Optimisation phases (QPU dispatch, 4K) ship when motivated.
See `docs/phase_8_10_11_closure.md`.
### Phase 8.12 — first VP9 frame decoded via libva (closed 2026-05-18)
- vb2_queue supports_requests; vb2_ops buf_out_validate +
buf_request_complete; v4l2_ctrl_new_custom for stateless
ctrl registration.
- Daemon decoded byte-exact VP9 keyframe via the full
libva path (FNV-1a 0x1eb34bfe matches standalone).
- ffmpeg still timed out waiting for media_request
completion (request bind state).
See `docs/phase_8_12_closure.md`.
### Phase 8.13 — byte-exact end-to-end via libva (closed 2026-05-18)
- Traced the misleading "Unable to set control(s):
Invalid argument" — actually libva probing H264/HEVC
PROFILE/LEVEL we don't expose (harmless); the real VP9
stateless control SET succeeds.
- Diagnosed the "Timeout when waiting for media request"
root cause: per-request control object stays bound
because vb2's normal buf_done path doesn't fire
buf_request_complete (only queue-cancel does).
- Fix: capture media_request from src_buf in inflight
entry, call v4l2_ctrl_request_complete from the
completion path before buf_done_and_job_finish.
- **Byte-exact end-to-end**: ffmpeg -hwaccel vaapi →
libva-v4l2-request-fourier → /dev/video0 →
daedalus_v4l2 → daemon → 18432-byte NV12 byte-for-byte
identical to ffmpeg software reference.
- **Project consumer target hit**: V4L2 stateless node
consumed by a real VAAPI client.
See `docs/phase_8_13_closure.md`.
### Phase 8.14 — multi-frame + AV1/H.264 + higher-level consumers
1. Multi-frame VP9 stream via libva (vp9_60s.ivf from 8.9
stress test) — P-frame references across requests.
2. AV1 + H.264 single-frame via libva.
3. mpv --hwdec=vaapi end-to-end.
4. Firefox / WebRTC if motivated.
Optimisation work (QPU dispatch, 4K, HDR-in-libva) ships
when there's a concrete user-facing need.
## Effort estimate