Phase 8.12: first VP9 frame decoded via libva
ffmpeg -hwaccel vaapi → libva-v4l2-request-fourier → /dev/video0 → daedalus_v4l2 kernel → REQ_DECODE on the chardev → daemon FFmpeg decode → byte-exact NV12 (FNV-1a 0x1eb34bfe, same hash the standalone test_m2m_stream produces for the same 128x96 VP9 keyframe). The pixel-correct decode through the libva path is the milestone. What's NOT yet working: libva times out on the media_request fd because buf_request_complete never fires (vb->req_obj.req is NULL when buf_done runs — the S_EXT_CTRLS EINVAL leaves the buffer un-bound to the request even though the buffer queues anyway). Phase 8.13 fixes the EINVAL so the request bind takes and the completion signal propagates. Kernel V4L2 request API integration: - media_device_ops.req_validate / req_queue = vb2_request_ validate / v4l2_m2m_request_queue (Phase 8.11) — MEDIA_IOC_REQUEST_ALLOC succeeds. - vb2_queue.supports_requests = true on OUTPUT queue — without this v4l2-core rejects S_EXT_CTRLS(REQUEST_VAL). - vb2_ops.buf_request_complete = daedalus_buf_request_complete → v4l2_ctrl_request_complete(req, &ctx->hdl). Without this v4l2-core WARNs at videobuf2-v4l2.c:440. - vb2_ops.buf_out_validate: sets field=V4L2_FIELD_NONE on OUTPUT buf. Required for the same WARN check. - requires_requests intentionally NOT set: lets the existing test_m2m_stream (direct QBUF, no request) keep working alongside the libva path. Stateless control re-registration: - Switched from v4l2_ctrl_new_std_compound(NULL p_def) to v4l2_ctrl_new_custom(&cfg, NULL) — pattern rkvdec / cedrus / hantro use. v4l2-core auto-fills elem_size + type from std table (verified: VP9_FRAME elem_size=168, matches sizeof(struct v4l2_ctrl_vp9_frame)). - No-op s_ctrl callback so SET requests don't crash — daemon ignores values, FFmpeg re-parses the bitstream. Verification on hertz (Pi 5, 6.12.75+rpt-rpi-2712): ffmpeg -hwaccel vaapi -i vp9_small.ivf … daemon: REQ_DECODE cookie=1 codec=1 bitstream=1566 bytes capture=128x96 1 planes daemon: decoder: opened vp9 context daemon: decoder: OK 128x96 fmt=0 (yuv420p) fnv1a=0x1eb34bfe … Same FNV-1a hash as the standalone test_m2m_stream produces for the same VP9 keyframe. End-to-end through libva. Remaining (Phase 8.13): - S_EXT_CTRLS EINVAL on V4L2_CID_STATELESS_VP9_FRAME despite matching elem_size — needs deeper validate-path debugging. - Once the request bind takes, buf_request_complete fires on buf_done, request fd signals completion, libva DQBUFs the decoded NV12. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -224,12 +224,15 @@ static int daedalus_register_stateless_ctrls(struct v4l2_ctrl_handler *hdl)
|
||||
.ops = &daedalus_ctrl_ops,
|
||||
.id = daedalus_stateless_ctrls[i],
|
||||
};
|
||||
v4l2_ctrl_new_custom(hdl, &cfg, NULL);
|
||||
struct v4l2_ctrl *ctrl;
|
||||
|
||||
ctrl = v4l2_ctrl_new_custom(hdl, &cfg, NULL);
|
||||
if (hdl->error) {
|
||||
pr_debug("daedalus_v4l2: skipping unsupported CID 0x%x (err=%d)\n",
|
||||
daedalus_stateless_ctrls[i], hdl->error);
|
||||
hdl->error = 0;
|
||||
}
|
||||
(void) ctrl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -397,14 +400,43 @@ static void daedalus_stop_streaming(struct vb2_queue *vq)
|
||||
v4l2_m2m_buf_done(vbuf, VB2_BUF_STATE_ERROR);
|
||||
}
|
||||
|
||||
/*
|
||||
* Phase 8.12: request API vb2 hooks.
|
||||
*
|
||||
* buf_out_validate: called on QBUF of an OUTPUT buf with a bound
|
||||
* request fd. We only accept progressive (FIELD_NONE) frames, so
|
||||
* normalise + accept. Without this op v4l2-core WARNs at
|
||||
* vb2_queue_or_prepare_buf and rejects with EINVAL.
|
||||
*
|
||||
* buf_request_complete: called when a request completes or is
|
||||
* cancelled; v4l2_ctrl_request_complete is the canonical helper
|
||||
* (releases the per-request control state cloned off ctx->hdl).
|
||||
*/
|
||||
static int daedalus_buf_out_validate(struct vb2_buffer *vb)
|
||||
{
|
||||
struct vb2_v4l2_buffer *vbuf = to_vb2_v4l2_buffer(vb);
|
||||
|
||||
vbuf->field = V4L2_FIELD_NONE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void daedalus_buf_request_complete(struct vb2_buffer *vb)
|
||||
{
|
||||
struct daedalus_ctx *ctx = vb2_get_drv_priv(vb->vb2_queue);
|
||||
|
||||
v4l2_ctrl_request_complete(vb->req_obj.req, &ctx->hdl);
|
||||
}
|
||||
|
||||
static const struct vb2_ops daedalus_qops = {
|
||||
.queue_setup = daedalus_queue_setup,
|
||||
.buf_prepare = daedalus_buf_prepare,
|
||||
.buf_queue = daedalus_buf_queue,
|
||||
.start_streaming = daedalus_start_streaming,
|
||||
.stop_streaming = daedalus_stop_streaming,
|
||||
.wait_prepare = vb2_ops_wait_prepare,
|
||||
.wait_finish = vb2_ops_wait_finish,
|
||||
.queue_setup = daedalus_queue_setup,
|
||||
.buf_prepare = daedalus_buf_prepare,
|
||||
.buf_queue = daedalus_buf_queue,
|
||||
.start_streaming = daedalus_start_streaming,
|
||||
.stop_streaming = daedalus_stop_streaming,
|
||||
.wait_prepare = vb2_ops_wait_prepare,
|
||||
.wait_finish = vb2_ops_wait_finish,
|
||||
.buf_out_validate = daedalus_buf_out_validate,
|
||||
.buf_request_complete = daedalus_buf_request_complete,
|
||||
};
|
||||
|
||||
/* -- m2m queue init -------------------------------------------------- */
|
||||
@@ -418,6 +450,24 @@ static int daedalus_queue_init(void *priv, struct vb2_queue *src_vq,
|
||||
src_vq->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
|
||||
src_vq->io_modes = VB2_MMAP | VB2_USERPTR | VB2_DMABUF;
|
||||
src_vq->drv_priv = ctx;
|
||||
/*
|
||||
* Phase 8.12: Request API support on OUTPUT queue. vb2
|
||||
* binds the per-request control state (the v4l2_ctrl_handler
|
||||
* clone used by VIDIOC_S_EXT_CTRLS(which=REQUEST_VAL)) to
|
||||
* the OUTPUT queue. Without this flag v4l2-core rejects
|
||||
* REQUEST_VAL writes before our s_ctrl is ever reached.
|
||||
*/
|
||||
src_vq->supports_requests = true;
|
||||
/*
|
||||
* requires_requests would reject any QBUF without a bound
|
||||
* media_request — useful when the daemon truly needs the
|
||||
* per-frame stateless controls to decode. Our daemon
|
||||
* re-parses the bitstream so it doesn't actually need the
|
||||
* controls; leaving requires_requests off lets non-request
|
||||
* clients (test_m2m_stream etc.) keep working AND lets the
|
||||
* libva path proceed even if its S_EXT_CTRLS bind didn't
|
||||
* fully take.
|
||||
*/
|
||||
src_vq->buf_struct_size = sizeof(struct v4l2_m2m_buffer);
|
||||
src_vq->ops = &daedalus_qops;
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user