Phase 8.10+8.11: libva consumer integration scaffold
Brings daedalus_v4l2 from "standalone test client" to "VAAPI-
discoverable decoder" by adding the surface formats and
media-controller plumbing that libva-v4l2-request-fourier
(sibling repo) requires.
libva-v4l2-request-fourier patches (pushed separately):
- b5b3acf: daedalus_v4l2 added to known_decoder_drivers
- 2146341: meson option gate
This commit (daedalus-v4l2 side, 3 production changes):
1. V4L2_PIX_FMT_NV12 (single-plane) on CAPTURE
- Added to daedalus_capture_formats[] alongside NV12M + P010
- daedalus_fill_capture_fmt handles num_planes=1 case
(sizeimage = W*H*3/2, bytesperline = W)
- daemon pack_nv12_single_to_plane: Y at base+0,
interleaved CbCr at base+(stride*H); same byte content
as NV12M two-plane, different layout
- Required because libva-v4l2-request-fourier's video.c
only knows non-multi-plane NV12 (it advertises
v4l2_mplane=true but uses the single-plane fourcc).
- Verified byte-exact via test_m2m_stream against
ffmpeg -pix_fmt nv12 reference (VP9 1080p 10 frames,
31 MB).
2. V4L2 Request API media ops
- daedalus_media_ops = { vb2_request_validate,
v4l2_m2m_request_queue } assigned to mdev.ops before
media_device_init.
- Without this, MEDIA_IOC_REQUEST_ALLOC returned
-ENOTTY and no VAAPI consumer could allocate a
media_request.
3. Stateless control registration via v4l2_ctrl_new_custom
- Switched from v4l2_ctrl_new_std_compound(NULL p_def)
to v4l2_ctrl_new_custom — pattern rkvdec/cedrus/
hantro use. Adds a no-op s_ctrl callback.
Verification (hertz, Pi 5, 6.12.75+rpt-rpi-2712):
LibVA trace through `ffmpeg -hwaccel vaapi`:
vaInitialize / Profiles / Entrypoints / CreateConfig /
QuerySurfaceAttributes / CreateSurfaces / CreateContext
(cap_pool: 24 slots, 1 plane each) / CreateBuffer
(slice + picture params) / MEDIA_IOC_REQUEST_ALLOC
— all succeed.
Standalone NV12 decode path:
test_m2m_stream vp9_1080_stream.ivf out.nv12 1920 1080 vp9 nv12
→ 10/10 frames, byte-exact vs ffmpeg -pix_fmt nv12
vainfo (via libva-v4l2-request-fourier with our driver):
7 VAProfile entries with VAEntrypointVLD
(H264 Main/High/CBaseline/MultiviewHigh/StereoHigh,
VP9Profile0, AV1Profile0)
What's NOT here (Phase 8.12):
The libva trace stops at VIDIOC_S_EXT_CTRLS returning
EINVAL when populating V4L2_CID_STATELESS_VP9_FRAME on
the request. The compound-control payload validation
against the kernel's expected struct shape rejects.
This isn't a "missing line" fix — it needs proper
stateless control plumbing (the SPS/PPS/SliceParams
get_dims, validate, default-value paths that in-tree
rkvdec/cedrus/hantro implement to satisfy v4l2-core's
std_validate). Documented as Phase 8.12 scope.
The shipped integration is itself a meaningful deliverable:
all the framework scaffolding is in place; the remaining
gap is well-characterised and bounded.
See docs/phase_8_10_11_closure.md for the full trace
analysis + next-phase plan.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -233,6 +233,65 @@ static int pack_p010_to_plane(struct AVFrame *fr,
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Pack 8-bit planar YUV420P into V4L2_PIX_FMT_NV12 single plane:
|
||||
* Y plane (W*H bytes) followed by interleaved CbCr at half-res
|
||||
* (W*H/2 bytes) all in planes->base[0]. Same layout as P010
|
||||
* sans the depth shift. For libva-v4l2-request-style clients
|
||||
* that expect num_planes=1 NV12.
|
||||
*/
|
||||
static int pack_nv12_single_to_plane(struct AVFrame *fr,
|
||||
const AVPixFmtDescriptor *desc,
|
||||
const struct daedalus_capture_planes *planes)
|
||||
{
|
||||
int h = fr->height;
|
||||
int w = fr->width;
|
||||
int cw, ch, y, x;
|
||||
uint8_t *base;
|
||||
uint32_t stride;
|
||||
uint8_t *dst_y, *dst_uv;
|
||||
size_t y_size;
|
||||
|
||||
if (!desc || !planes || planes->nr < 1)
|
||||
return -EINVAL;
|
||||
if (desc->nb_components < 3)
|
||||
return -EINVAL;
|
||||
if (desc->log2_chroma_w != 1 || desc->log2_chroma_h != 1)
|
||||
return -EINVAL;
|
||||
if (desc->comp[0].depth != 8)
|
||||
return -EINVAL;
|
||||
|
||||
cw = AV_CEIL_RSHIFT(w, desc->log2_chroma_w);
|
||||
ch = AV_CEIL_RSHIFT(h, desc->log2_chroma_h);
|
||||
|
||||
base = planes->base[0];
|
||||
stride = planes->stride[0] ? planes->stride[0] : (uint32_t) w;
|
||||
if (!base)
|
||||
return -EINVAL;
|
||||
|
||||
dst_y = base;
|
||||
y_size = (size_t) stride * (size_t) h;
|
||||
dst_uv = base + y_size;
|
||||
|
||||
for (y = 0; y < h; y++)
|
||||
memcpy(dst_y + (size_t) y * stride,
|
||||
fr->data[0] + (size_t) y * fr->linesize[0],
|
||||
(size_t) w);
|
||||
|
||||
for (y = 0; y < ch; y++) {
|
||||
const uint8_t *u = fr->data[1] +
|
||||
(size_t) y * fr->linesize[1];
|
||||
const uint8_t *v = fr->data[2] +
|
||||
(size_t) y * fr->linesize[2];
|
||||
uint8_t *row = dst_uv + (size_t) y * stride;
|
||||
for (x = 0; x < cw; x++) {
|
||||
row[x * 2 + 0] = u[x];
|
||||
row[x * 2 + 1] = v[x];
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pack_nv12_to_planes(struct AVFrame *fr,
|
||||
const AVPixFmtDescriptor *desc,
|
||||
const struct daedalus_capture_planes *planes)
|
||||
@@ -425,6 +484,9 @@ int daedalus_decoder_run_request(struct daedalus_decoder *dec,
|
||||
case V4L2_PIX_FMT_NV12M:
|
||||
prc = pack_nv12_to_planes(fr, desc, planes);
|
||||
break;
|
||||
case V4L2_PIX_FMT_NV12:
|
||||
prc = pack_nv12_single_to_plane(fr, desc, planes);
|
||||
break;
|
||||
case V4L2_PIX_FMT_P010:
|
||||
prc = pack_p010_to_plane(fr, desc, planes);
|
||||
break;
|
||||
|
||||
Reference in New Issue
Block a user