iter39 α-31: H264 Hi10P + HEVC Main10 sub-profile support (10-bit, rkvdec NV15)
Adds VAProfileH264High10 and VAProfileHEVCMain10 to the libva-v4l2-request
backend. RK3399 rkvdec emits decoded frames as V4L2_PIX_FMT_NV15 (4 × 10-bit
values packed in 5 bytes per element); VAAPI consumers receive standard
VA_FOURCC_P010 via a new userspace unpack in copy_surface_to_image.
VP9 Profile 2 explicitly NOT added — RK3399 rkvdec kernel ctrl table
caps at V4L2_MPEG_VIDEO_VP9_PROFILE_0 (rkvdec.c::rkvdec_vp9_ctrl_descs).
Touchpoints (per Phase 5 sonnet-architect review amendments):
- include/drm_fourcc.h: define DRM_FORMAT_NV15 (vendored libdrm lacks it)
- src/nv15.{c,h}: NV15 → P010 plane unpack (LSB-first, per
Documentation/userspace-api/media/v4l/pixfmt-nv15.rst)
- src/video.c: NV15 entry in formats[] (else NULL-deref on video_format_find)
- src/codec.c: pixelformat_for_profile cases for Hi10P + Main10
- src/config.c: enumeration, validation, entrypoints, RT_FORMAT_YUV420_10
advertisement for 10-bit profiles
- src/context.c: per-profile CAPTURE pix_fmt (NV12/NV15), 10-bit synthetic
SPS (bit_depth_luma_minus8=2), video_format invalidation on bit-depth
transition (sibling to iter38 device-switch invalidation), is_10bit flag
- src/surface.c: RT_FORMAT_YUV420_10 admission, NV15 fourcc on PRIME export
- src/image.c: P010 reporting in DeriveImage + QueryImageFormats,
P010-aware sizing in CreateImage, NV15 → P010 unpack call in
copy_surface_to_image (gated on is_10bit + image.format.fourcc == P010)
- src/picture.c: 4 switch blocks route Hi10P/Main10 to existing H264/HEVC
per-codec paths
- src/request.h: MAX_PROFILES bump 11 → 13, driver_data->is_10bit flag
Scope: COPY path (vaGetImage / vaDeriveImage) only. Standard ffmpeg-vaapi
hwdownload, mpv vaapi-copy, and any consumer using vaGetImage works
end-to-end. PRIME-path consumers that only know NV12/P010 must use the
COPY path; PRIME consumers aware of NV15 (panfrost-Mesa et al.) get the
correct fourcc on RequestExportSurfaceHandle. PRIME-side P010 emission is
follow-up scope (would need DRM_FORMAT_P010 + per-plane unpack into a
GPU-accessible buffer).
Compile-tested on boltzmann (aarch64 native, gcc 15.2.1, libva 1.23.0,
libdrm 2.4.133): clean build, .so produced, 0 new warnings.
Phase 0/2 evidence: linux-mmind-v7.0 drivers/media/platform/rockchip/rkvdec.
rkvdec_h264_decoded_fmts[] and rkvdec_hevc_decoded_fmts[] both list NV15;
ctrl tables cap at HEVC MAIN_10 and H264 HIGH_422_INTRA (Hi10P < cap, not
in menu_skip_mask). image_fmt resolution (rkvdec-h264-common.c:196,
rkvdec-hevc-common.c:467) dispatches on bit_depth_luma_minus8 only.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
+68
-18
@@ -107,20 +107,47 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id,
|
||||
* the driver_data and is cached across CreateContext cycles. The
|
||||
* probe doesn't require any prior S_FMT — v4l2_find_format
|
||||
* enumerates the device's supported formats directly.
|
||||
*
|
||||
* iter39: choose NV15 (10-bit packed) for Hi10P / Main10 profiles,
|
||||
* NV12 (8-bit) otherwise. If the cached video_format doesn't match
|
||||
* the profile's bit-depth requirement, invalidate and re-probe —
|
||||
* sibling pattern to iter38's device-switch invalidation in
|
||||
* request_switch_device_for_profile().
|
||||
*/
|
||||
{
|
||||
bool want_10bit = (config_object->profile == VAProfileH264High10 ||
|
||||
config_object->profile == VAProfileHEVCMain10);
|
||||
unsigned int want_pixfmt = want_10bit ? V4L2_PIX_FMT_NV15
|
||||
: V4L2_PIX_FMT_NV12;
|
||||
if (driver_data->video_format &&
|
||||
driver_data->video_format->v4l2_format != want_pixfmt &&
|
||||
driver_data->video_format->v4l2_format != V4L2_PIX_FMT_SUNXI_TILED_NV12)
|
||||
driver_data->video_format = NULL;
|
||||
}
|
||||
if (!driver_data->video_format) {
|
||||
bool want_10bit = (config_object->profile == VAProfileH264High10 ||
|
||||
config_object->profile == VAProfileHEVCMain10);
|
||||
video_format = NULL;
|
||||
found = v4l2_find_format(driver_data->video_fd,
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
||||
V4L2_PIX_FMT_SUNXI_TILED_NV12);
|
||||
if (found)
|
||||
video_format = video_format_find(V4L2_PIX_FMT_SUNXI_TILED_NV12);
|
||||
|
||||
found = v4l2_find_format(driver_data->video_fd,
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
V4L2_PIX_FMT_NV12);
|
||||
if (found)
|
||||
video_format = video_format_find(V4L2_PIX_FMT_NV12);
|
||||
if (!want_10bit) {
|
||||
found = v4l2_find_format(driver_data->video_fd,
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE,
|
||||
V4L2_PIX_FMT_SUNXI_TILED_NV12);
|
||||
if (found)
|
||||
video_format = video_format_find(V4L2_PIX_FMT_SUNXI_TILED_NV12);
|
||||
|
||||
found = v4l2_find_format(driver_data->video_fd,
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
V4L2_PIX_FMT_NV12);
|
||||
if (found)
|
||||
video_format = video_format_find(V4L2_PIX_FMT_NV12);
|
||||
} else {
|
||||
found = v4l2_find_format(driver_data->video_fd,
|
||||
V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE,
|
||||
V4L2_PIX_FMT_NV15);
|
||||
if (found)
|
||||
video_format = video_format_find(V4L2_PIX_FMT_NV15);
|
||||
}
|
||||
|
||||
if (video_format == NULL) {
|
||||
status = VA_STATUS_ERROR_OPERATION_FAILED;
|
||||
@@ -131,6 +158,10 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id,
|
||||
}
|
||||
video_format = driver_data->video_format;
|
||||
|
||||
/* iter39: session-wide flag drives image.c reporting + unpack. */
|
||||
driver_data->is_10bit = (config_object->profile == VAProfileH264High10 ||
|
||||
config_object->profile == VAProfileHEVCMain10);
|
||||
|
||||
output_type = v4l2_type_video_output(video_format->v4l2_mplane);
|
||||
capture_type = v4l2_type_video_capture(video_format->v4l2_mplane);
|
||||
|
||||
@@ -175,7 +206,12 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id,
|
||||
* CAPTURE (sanity read-back, matches what S_FMT committed).
|
||||
*/
|
||||
{
|
||||
unsigned int capture_pixelformat = V4L2_PIX_FMT_NV12;
|
||||
/* iter39: NV15 for 10-bit profiles (rkvdec Hi10P/Main10),
|
||||
* NV12 otherwise. driver_data->is_10bit was set above from
|
||||
* the active profile. */
|
||||
unsigned int capture_pixelformat = driver_data->is_10bit
|
||||
? V4L2_PIX_FMT_NV15
|
||||
: V4L2_PIX_FMT_NV12;
|
||||
rc = v4l2_set_format(driver_data->video_fd, capture_type,
|
||||
capture_pixelformat, picture_width,
|
||||
picture_height);
|
||||
@@ -233,15 +269,26 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id,
|
||||
* void-cast best-effort, so this is consistent with prior pattern.
|
||||
*/
|
||||
{
|
||||
/*
|
||||
* iter39: 10-bit profiles set bit_depth_luma_minus8 = 2 in
|
||||
* the synthetic SPS so rkvdec's get_image_fmt resolves to
|
||||
* RKVDEC_IMG_FMT_420_10BIT (per rkvdec-h264-common.c:196 +
|
||||
* rkvdec-hevc-common.c:467). Image_fmt resolution depends
|
||||
* only on bit_depth_luma_minus8 and chroma_format_idc;
|
||||
* profile_idc is ignored for image_fmt and v4l2_ctrl_hevc_sps
|
||||
* has no profile_idc field at all.
|
||||
*/
|
||||
bool ten = driver_data->is_10bit;
|
||||
switch (config_object->profile) {
|
||||
case VAProfileHEVCMain: {
|
||||
case VAProfileHEVCMain:
|
||||
case VAProfileHEVCMain10: {
|
||||
struct v4l2_ctrl_hevc_sps dummy_sps;
|
||||
struct v4l2_ext_control dummy_ctrl;
|
||||
|
||||
memset(&dummy_sps, 0, sizeof(dummy_sps));
|
||||
dummy_sps.chroma_format_idc = 1; /* 4:2:0 */
|
||||
dummy_sps.bit_depth_luma_minus8 = 0; /* 8-bit */
|
||||
dummy_sps.bit_depth_chroma_minus8 = 0;
|
||||
dummy_sps.bit_depth_luma_minus8 = ten ? 2 : 0;
|
||||
dummy_sps.bit_depth_chroma_minus8 = ten ? 2 : 0;
|
||||
dummy_sps.pic_width_in_luma_samples = picture_width;
|
||||
dummy_sps.pic_height_in_luma_samples = picture_height;
|
||||
|
||||
@@ -256,19 +303,20 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id,
|
||||
case VAProfileH264High:
|
||||
case VAProfileH264ConstrainedBaseline:
|
||||
case VAProfileH264MultiviewHigh:
|
||||
case VAProfileH264StereoHigh: {
|
||||
case VAProfileH264StereoHigh:
|
||||
case VAProfileH264High10: {
|
||||
struct v4l2_ctrl_h264_sps dummy_sps;
|
||||
struct v4l2_ext_control dummy_ctrl;
|
||||
|
||||
memset(&dummy_sps, 0, sizeof(dummy_sps));
|
||||
dummy_sps.chroma_format_idc = 1; /* 4:2:0 */
|
||||
dummy_sps.bit_depth_luma_minus8 = 0;
|
||||
dummy_sps.bit_depth_chroma_minus8 = 0;
|
||||
dummy_sps.bit_depth_luma_minus8 = ten ? 2 : 0;
|
||||
dummy_sps.bit_depth_chroma_minus8 = ten ? 2 : 0;
|
||||
dummy_sps.pic_width_in_mbs_minus1 =
|
||||
(picture_width + 15) / 16 - 1;
|
||||
dummy_sps.pic_height_in_map_units_minus1 =
|
||||
(picture_height + 15) / 16 - 1;
|
||||
dummy_sps.profile_idc = 100; /* High */
|
||||
dummy_sps.profile_idc = ten ? 110 : 100; /* High10 : High */
|
||||
dummy_sps.level_idc = 41;
|
||||
/*
|
||||
* FRAME_MBS_ONLY required: rkvdec_h264_validate_sps
|
||||
@@ -636,6 +684,8 @@ VAStatus RequestDestroyContext(VADriverContextP context, VAContextID context_id)
|
||||
* The next CreateContext re-populates the cache.
|
||||
*/
|
||||
driver_data->fmt_valid = false;
|
||||
/* iter39: clear 10-bit session flag — next CreateContext re-sets. */
|
||||
driver_data->is_10bit = false;
|
||||
|
||||
return VA_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user