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:
@@ -520,6 +520,14 @@ struct daedalus_inflight {
|
||||
struct daedalus_ctx *ctx;
|
||||
struct vb2_v4l2_buffer *src_buf;
|
||||
struct vb2_v4l2_buffer *dst_buf;
|
||||
/*
|
||||
* Captured media_request the src_buf was bound to (if any).
|
||||
* Set by device_run from src_buf->vb2_buf.req_obj.req;
|
||||
* consumed by the completion path to call
|
||||
* v4l2_ctrl_request_complete + signal request fd. NULL for
|
||||
* non-request flows (e.g. test_m2m_stream direct QBUF).
|
||||
*/
|
||||
struct media_request *req;
|
||||
};
|
||||
|
||||
static struct daedalus_inflight *
|
||||
@@ -666,6 +674,14 @@ static void daedalus_device_run(void *priv)
|
||||
inf->ctx = ctx;
|
||||
inf->src_buf = src_buf;
|
||||
inf->dst_buf = dst_buf;
|
||||
/*
|
||||
* Capture the bound media_request (if any) so the
|
||||
* completion path can call v4l2_ctrl_request_complete +
|
||||
* trigger MEDIA_REQUEST_STATE_COMPLETE. vb2-core's normal
|
||||
* buf_done path unbinds the buffer's req_obj but leaves the
|
||||
* control object bound — the driver has to complete it.
|
||||
*/
|
||||
inf->req = src_buf->vb2_buf.req_obj.req;
|
||||
|
||||
mutex_lock(&dev->inflight_lock);
|
||||
list_add_tail(&inf->list, &dev->inflight);
|
||||
@@ -789,6 +805,22 @@ void daedalus_complete_resp_frame(u32 cookie,
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 8.14: if the src_buf was bound to a media_request
|
||||
* (libva-driven decode path), complete the per-request
|
||||
* control state BEFORE buf_done_and_job_finish. vb2-core's
|
||||
* buf_done unbinds the buffer's req_obj on its own, but the
|
||||
* control object stays bound until v4l2_ctrl_request_complete
|
||||
* runs — only after BOTH objects unbind does the request
|
||||
* transition to MEDIA_REQUEST_STATE_COMPLETE and wake any
|
||||
* userspace poll on the request fd.
|
||||
*
|
||||
* For non-request flows (test_m2m_stream direct QBUF) inf->req
|
||||
* is NULL and v4l2_ctrl_request_complete just no-ops.
|
||||
*/
|
||||
if (inf->req)
|
||||
v4l2_ctrl_request_complete(inf->req, &inf->ctx->hdl);
|
||||
|
||||
/*
|
||||
* Use the buf_done_and_job_finish helper rather than plain
|
||||
* buf_done + job_finish: the helper pops the buffers off
|
||||
@@ -968,6 +1000,15 @@ static int daedalus_open(struct file *file)
|
||||
|
||||
v4l2_ctrl_handler_init(&ctx->hdl, ARRAY_SIZE(daedalus_stateless_ctrls));
|
||||
daedalus_register_stateless_ctrls(&ctx->hdl);
|
||||
/*
|
||||
* v4l2_ctrl_handler_setup runs s_ctrl for every registered
|
||||
* control with its default value — required to bring each
|
||||
* control out of "uninitialised" state. Without this the
|
||||
* per-request handler clone path returns EINVAL on
|
||||
* VIDIOC_S_EXT_CTRLS(which=REQUEST_VAL). rkvdec/cedrus/
|
||||
* hantro all call this after registration.
|
||||
*/
|
||||
v4l2_ctrl_handler_setup(&ctx->hdl);
|
||||
ctx->fh.ctrl_handler = &ctx->hdl;
|
||||
|
||||
daedalus_fill_output_fmt(&ctx->src_fmt,
|
||||
|
||||
Reference in New Issue
Block a user