From 8e2c04f84b31132b385bba2c09c7b56c0c3a0de5 Mon Sep 17 00:00:00 2001 From: claude-noether Date: Thu, 14 May 2026 06:01:22 +0000 Subject: [PATCH] =?UTF-8?q?iter11=20Phase=206=20=CE=B1-13=20+=20=CE=B1-14:?= =?UTF-8?q?=20HEVC=20SPS=20hygiene=20+=20IRAP/IDR=20flags=20fix=20Bug=205?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two fixes in one commit: α-13 (h265_fill_sps): sps_max_num_reorder_pics now derived from sps_max_dec_pic_buffering_minus1 (safe upper bound per H.265 §A.4.2) instead of hardcoded 0. Phase 5b empirically showed rkvdec ignores this field on RK3399, so this is wire-correctness hygiene only — matches kdirect's payload pattern without behavior change. α-14 (h265_set_controls): derive IRAP_PIC / IDR_PIC flags from the first slice's nal_unit_type (parsed by h265_fill_slice_params into slice_params_array[0].nal_unit_type). Without these flags rkvdec doesn't recognise the keyframe boundary, treats IDR as inter without references, and produces all-zero CAPTURE output — observed as Bug 5 on libva HEVC (06b2c5a0...). kdirect sets these from the bitstream parse and decodes correctly (9340b832...). Mapping: nal_unit_type 16..23 -> IRAP_PIC nal_unit_type 19 (IDR_W_RADL) or 20 (IDR_N_LP) -> IDR_PIC HEVC-only (no risk to other codecs). h265_set_controls already profile-gated via picture.c::codec_set_controls VAProfileHEVCMain dispatch. Per feedback_unconditional_codec_state.md. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/h265.c | 58 +++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/src/h265.c b/src/h265.c index 6737c58..ba764ee 100644 --- a/src/h265.c +++ b/src/h265.c @@ -107,8 +107,20 @@ static void h265_fill_sps(VAPictureParameterBufferHEVC *picture, picture->log2_max_pic_order_cnt_lsb_minus4; sps->sps_max_dec_pic_buffering_minus1 = picture->sps_max_dec_pic_buffering_minus1; - sps->sps_max_num_reorder_pics = 0; /* not exposed */ - sps->sps_max_latency_increase_plus1 = 0; /* not exposed */ + /* + * iter11 α-13: VAAPI doesn't forward sps_max_num_reorder_pics or + * sps_max_latency_increase_plus1. kdirect parses the SPS NAL and + * submits the bitstream's true values; libva used to hardcode 0 + * (a structurally wrong "no reordering" hint, even though Phase 5b + * empirically confirmed rkvdec ignores both fields on RK3399, so + * this is wire-hygiene only — matches kdirect's payload more + * closely without behavior change). sps_max_dec_pic_buffering_minus1 + * is a safe upper bound per H.265 §A.4.2 (sps_max_num_reorder_pics ≤ + * sps_max_dec_pic_buffering_minus1 always holds). latency_increase_plus1 + * stays at 0 = spec "unconstrained". + */ + sps->sps_max_num_reorder_pics = picture->sps_max_dec_pic_buffering_minus1; + sps->sps_max_latency_increase_plus1 = 0; sps->log2_min_luma_coding_block_size_minus3 = picture->log2_min_luma_coding_block_size_minus3; sps->log2_diff_max_min_luma_coding_block_size = @@ -304,9 +316,20 @@ static void h265_fill_decode_params(struct request_data *driver_data, * num_delta_pocs_of_ref_rps_idx left zero (VAAPI doesn't expose; * matches FFmpeg's behavior for non-bitstream-driven population). */ - /* IRAP/IDR/NO_OUTPUT_OF_PRIOR flags — VAAPI doesn't expose; computing - * from CurrPic flags + nal_unit_type would require parsing. Leave - * zero for iter2 binding cell (BBB B/P-frames don't need these set). */ + /* + * iter11 α-14: IRAP/IDR/NO_OUTPUT_OF_PRIOR flags. VAAPI doesn't + * expose these in VAPictureParameterBufferHEVC. The iter2 binding + * cell hardcoded them to 0 with the comment "BBB B/P-frames don't + * need these set" — but IDR keyframes DO need IDR_PIC|IRAP_PIC. + * Without them rkvdec doesn't recognise the keyframe boundary, + * treats the IDR as inter without references, and produces all-zero + * CAPTURE output (Bug 5). + * + * The flags are derived at h265_set_controls level after slice_params + * have been parsed (slice_params[0].nal_unit_type carries the NAL + * type extracted from the bitstream). Initialise to 0 here; the caller + * patches the IRAP/IDR bits. + */ decode_params->flags = 0; } @@ -549,6 +572,31 @@ int h265_set_controls(struct request_data *driver_data, h265_fill_decode_params(driver_data, picture, &decode_params); h265_fill_scaling_matrix(iqmatrix, iqmatrix_set, &scaling_matrix); + /* + * iter11 α-14: derive IRAP_PIC / IDR_PIC flags from the first + * slice's nal_unit_type (already parsed by h265_fill_slice_params + * from the bitstream into slice_params_array[0].nal_unit_type). + * + * H.265 §7.4.2.2: + * nal_unit_type 16..23 are IRAP (random access). + * nal_unit_type 19 (IDR_W_RADL) and 20 (IDR_N_LP) are IDR. + * + * Without setting these, rkvdec doesn't recognise the keyframe + * boundary, treats the IDR as inter without references, and + * produces all-zero CAPTURE output. Phase 3 confirmed kdirect + * (ffmpeg-v4l2request) sets flags=0x03 (IRAP|IDR) on frame 1 + * and decodes correctly through the same kernel. + */ + if (num_slices > 0) { + uint8_t nut = slice_params_array[0].nal_unit_type; + if (nut >= 16 && nut <= 23) + decode_params.flags |= + V4L2_HEVC_DECODE_PARAM_FLAG_IRAP_PIC; + if (nut == 19 || nut == 20) + decode_params.flags |= + V4L2_HEVC_DECODE_PARAM_FLAG_IDR_PIC; + } + controls[n++] = (struct v4l2_ext_control){ .id = V4L2_CID_STATELESS_HEVC_SPS, .ptr = &sps,