fresnel-fourier iter2 Phase 6 commit B: rewrite h265.c against new V4L2 stateless HEVC API

Rewrites src/h265.c (407 lines → 588 lines) and the picture.c HEVC
dispatch + per-slice accumulation against the modern split V4L2_CID_
STATELESS_HEVC_{SPS,PPS,SLICE_PARAMS,SCALING_MATRIX,DECODE_PARAMS,
DECODE_MODE,START_CODE} stateless controls. Replaces the staging-era
V4L2_CID_MPEG_VIDEO_HEVC_{SPS,PPS,SLICE_PARAMS} CIDs that were
removed from the kernel UAPI.

Per-frame submission: ONE batched VIDIOC_S_EXT_CTRLS, count=5,
ctrl_class=V4L2_CTRL_CLASS_CODEC_STATELESS:

  0xa40a90 SPS            (40  bytes)
  0xa40a91 PPS            (64  bytes)
  0xa40a92 SLICE_PARAMS   (variable; dynamic-array; one entry per slice)
  0xa40a93 SCALING_MATRIX (1296 bytes; memset-zero when no scaling list)
  0xa40a94 DECODE_PARAMS  (328 bytes; per-frame DPB info)

Plus device-wide menus set once at context.c init (separate batched
S_EXT_CTRLS call so a kernel without HEVC controls — e.g. hantro on
RK3568/RK3399 — silently fails its batch without invalidating H.264):

  0xa40a95 DECODE_MODE  (FRAME_BASED on rkvdec)
  0xa40a96 START_CODE   (ANNEX_B on rkvdec)

Reference: FFmpeg libavcodec/v4l2_request_hevc.c:505-565
           (v4l2_request_hevc_queue_decode batched submission shape).

Phase 5 review amendments incorporated:

  C1 (data_byte_offset NOT data_bit_offset):
    Old h265.c at lines 184-209 ran an 8-bit search to compute
    bit-granularity offset. New API renames the field to
    data_byte_offset (u32 byte offset). Bit-search dropped; replaced
    with plain byte offset = source_offset + slice->slice_data_byte_offset.

  C2 (dpb_entry.flags only LONG_TERM_REFERENCE; pic_order_cnt_val
      singular; poc_st_curr_*[] arrays hold DPB INDICES not POC):
    h265_fill_decode_params replaces old slice-params DPB iteration
    with explicit DPB classification + index-array population.
    For each VAAPI ReferenceFrames[i]:
      - Classify into ST_CURR_BEFORE / ST_CURR_AFTER / LT_CURR via
        VA_PICTURE_HEVC_RPS_* flags.
      - Set dpb[j].timestamp, .pic_order_cnt_val (singular), .field_pic.
      - Set dpb[j].flags = LONG_TERM_REFERENCE iff RPS_LT_CURR.
      - Append j (DPB index, u8) to poc_st_curr_before[k] /
        poc_st_curr_after[k] / poc_lt_curr[k] based on classification.

  C3 (union-aliasing reasoning corrected):
    BeginPicture's params.h265.num_slices = 0 reset is benign for
    non-HEVC profiles because byte ~17764 of the params union is past
    any field non-HEVC profiles read, NOT because RenderPicture's
    per-buffer copies overwrite that location. Wording amended in
    phase4_iter2_plan.md per phase5_iter2_review.md.

  S1 (PPS flags 19 + 20 — DEBLOCKING_FILTER_CONTROL_PRESENT and
      UNIFORM_SPACING):
    Empirically VAAPI does NOT expose either flag in the
    VAPictureParameterBufferHEVC pic_fields.bits or
    slice_parsing_fields.bits. Both bits left zero. BBB-720p10s_hevc
    fixture uses neither tiles nor explicit deblocking-control
    parameters, so the omission is correct for the iter2 binding cell.

  S2 (3 PPS scalars added):
    pic_parameter_set_id (default 0; VAAPI doesn't expose),
    num_ref_idx_l0_default_active_minus1, num_ref_idx_l1_default_
    active_minus1 (both populated from VAAPI picture struct).

  Q2 (slice_segment_addr populated):
    Was missing in old h265.c. Now sourced from
    VAAPI's slice->slice_segment_address.

  S3 (SCALING_MATRIX content choice):
    Implementer choice taken: when iqmatrix_set==false (BBB has no
    scaling list per SPS flags = SAO|STRONG_INTRA_SMOOTHING),
    h265_fill_scaling_matrix sends memset-zero. Matches FFmpeg's
    sl=NULL pattern at v4l2_request_hevc.c:384-403 (preserves
    byte-equality vs cross-validator anchor).

  S4 (FFmpeg function name fix): cosmetic; no code impact.

Plus one Phase 6 inline correction: phase 5 review S1 suggested
VAAPI exposes uniform_spacing_flag in pic_fields.bits; empirical
test-compile shows it doesn't. Comment added in h265_fill_pps
documenting the omission.

Picture.c changes (3 edits):

  1. codec_set_controls HEVCMain dispatch (lines 204-206 → call
     h265_set_controls; replaces explicit Fourier-local: HEVC stripped
     reject).
  2. codec_store_buffer HEVC VASliceParameterBufferType case: append
     VAAPI slice param to params.h265.slices[N] array, increment
     num_slices. Single-slice mirror at .slice retained for
     h265_fill_pps (which reads dependent_slice_segment_flag from
     LongSliceFlags).
  3. RequestBeginPicture: add params.h265.num_slices = 0 reset
     alongside existing h264.matrix_set = false reset.

Surface.h: extend params.h265 struct with slices[HEVC_MAX_SLICES_PER_
FRAME=64] array + num_slices counter. ~17 KB extra per surface union;
24 surfaces in iter7 cap_pool = ~400 KB total surface_heap growth.
object_heap allocator picks up new size automatically via
sizeof(struct object_surface).

Context.c: separate 2-control batched call sets HEVC DECODE_MODE +
START_CODE device-wide. Same best-effort (void)v4l2_set_controls
pattern as the existing H.264 device-init block; if kernel doesn't
advertise HEVC controls (hantro on RK3568/RK3399), the batch silently
fails without invalidating the H.264 batch.

Meson.build: uncomment 'h265.c' (line 50) and 'h265.h' (line 73)
in sources + headers lists.

H265.h: added HEVC_MAX_SLICES_PER_FRAME=64 #define before struct
forward declarations.

Phase 6 smoke test on fresnel (post Commit A + Commit B):

  Criterion 1: vainfo lists VAProfileHEVCMain on rkvdec env binding
              (/dev/video1 + /dev/media0). PASS.

  Criterion 3: ffmpeg -hwaccel vaapi HEVC decode of bbb_720p10s_hevc.mp4
              -frames:v 5 -f null -, exit 0. cap_pool_init: 24 slots
              ready. PASS.

  Criterion 4: mpv --hwdec=vaapi --vo=image at +02s seek, HEVC fixture:
    HW frame 1: 47a5f3850df5d8c732767a227830c2272ff78402a7b6adeea329e29838808be5
    SW frame 1: 47a5f3850df5d8c732767a227830c2272ff78402a7b6adeea329e29838808be5
    HW frame 2: a467b3bc9d7b6374b6786ecfac46932d6c7bb932ab11d311edaa233d7863e656
    SW frame 2: a467b3bc9d7b6374b6786ecfac46932d6c7bb932ab11d311edaa233d7863e656
    HW=SW byte-identical for both frames; frame1 != frame2 (real motion).
    PASS.

  Criterion 5: regression hashes hold for both prior cells:
    H.264 +30s HW frame 1: f623d5f7a41697f67dd227275c6f1b21ffc257f65626d32fde8229357f8764c9 (T4 ref MATCH)
    H.264 +30s HW frame 2: 7d7bc6f2146dda8b2d223bba622c4b9fbe9674181ff1e02afe286b620342e0a8 (T4 ref MATCH)
    MPEG-2 +02s HW frame 1: 6e7873030dbf0403c67f35dd106ebef3c7909a0fd12433b82ad758e7fee9f092 (iter1 ref MATCH)
    MPEG-2 +02s HW frame 2: ccc7ce08810d4a96e9ba7a19f4f95bbf6cc861bda9337604b5c668ad52bef7de (iter1 ref MATCH)
    PASS.

All five criteria green on first build attempt — Phase 5 review
caught the 3 Critical UAPI errors (data_bit_offset → data_byte_offset
rename; dpb.rps field gone + pic_order_cnt_val rename + index-array
semantics) that would have been Phase 6 compile failures or silent
Phase 7 byte-compare divergences. Without that review pass, this
commit would have been the start of a 2+ loopback debugging cycle.

Refs:
  ../fresnel-fourier/phase4_iter2_plan.md (10 contract clauses,
                                            File 4 patch shape)
  ../fresnel-fourier/phase5_iter2_review.md (C1, C2, C3, S1, S2,
                                              S3, S4, Q2 amendments
                                              all incorporated)
  ../fresnel-fourier/phase0_evidence/2026-05-08/iter2_phase3/
    ffmpeg_v4l2req.stdout (cross-validator anchor — Phase 7
    bonus byte-compare verification target)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-08 15:58:34 +02:00
parent cca539d5f9
commit 8d71e20bf7
6 changed files with 462 additions and 235 deletions
+23
View File
@@ -153,6 +153,29 @@ VAStatus RequestCreateContext(VADriverContextP context, VAConfigID config_id,
dev_ctrls, 2);
}
/*
* iter2: HEVC device-wide controls. Same best-effort pattern as
* H.264 above — separate batched call so a kernel that does not
* advertise HEVC controls (e.g. hantro-vpu-dec on RK3568/RK3399)
* silently fails on this batch without invalidating the H.264
* batch. rkvdec on RK3399 advertises HEVC and accepts FRAME_BASED
* + ANNEX_B (only supported menu values per Phase 0 v4l2_inventory).
*/
{
struct v4l2_ext_control hevc_dev_ctrls[2] = {
{
.id = V4L2_CID_STATELESS_HEVC_DECODE_MODE,
.value = V4L2_STATELESS_HEVC_DECODE_MODE_FRAME_BASED,
},
{
.id = V4L2_CID_STATELESS_HEVC_START_CODE,
.value = V4L2_STATELESS_HEVC_START_CODE_ANNEX_B,
},
};
(void)v4l2_set_controls(driver_data->video_fd, -1,
hevc_dev_ctrls, 2);
}
/*
* Mirror the ANNEX_B start-code mode set on the device above
* into context_object->h264_start_code so picture.c::
+411 -230
View File
@@ -24,91 +24,81 @@
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#include "mpeg2.h"
/*
* fresnel-fourier iter2 Phase 6 commit B: rewrite h265.c against new
* V4L2_CID_STATELESS_HEVC_{SPS,PPS,SLICE_PARAMS,SCALING_MATRIX,
* DECODE_PARAMS,DECODE_MODE,START_CODE} stateless controls (mainline
* kernel <linux/v4l2-controls.h>:2090-2300).
*
* Replaces the staging-era V4L2_CID_MPEG_VIDEO_HEVC_{SPS,PPS,
* SLICE_PARAMS} CIDs that don't exist on modern kernels (verified via
* test-compile in iter2 Phase 2).
*
* Per-frame submission: one batched VIDIOC_S_EXT_CTRLS, count=5,
* ctrl_class=V4L2_CTRL_CLASS_CODEC_STATELESS:
* 0xa40a90 SPS (40 bytes)
* 0xa40a91 PPS (64 bytes)
* 0xa40a92 SLICE_PARAMS (variable; dynamic-array; one entry per slice)
* 0xa40a93 SCALING_MATRIX (1296 bytes; conditional on kernel availability)
* 0xa40a94 DECODE_PARAMS (328 bytes; per-frame DPB info)
*
* Plus device-wide menus set once at context init:
* 0xa40a95 DECODE_MODE (FRAME_BASED on rkvdec)
* 0xa40a96 START_CODE (ANNEX_B on rkvdec)
*
* Reference: FFmpeg libavcodec/v4l2_request_hevc.c:505-565
* (v4l2_request_hevc_queue_decode batched submission shape).
*
* Key Phase 5 review amendments incorporated:
* C1: data_byte_offset (NOT data_bit_offset); old bit-search dropped.
* C2: dpb_entry.flags only LONG_TERM_REFERENCE bit; pic_order_cnt_val
* (singular); poc_st_curr_*[] arrays are u8 DPB INDICES, not POC
* values (per FFmpeg get_ref_pic_index pattern).
* S1: PPS flags 19+20 (DEBLOCKING_FILTER_CONTROL_PRESENT, UNIFORM_SPACING)
* included.
* S2: PPS scalars pic_parameter_set_id, num_ref_idx_l0/l1_default_active_
* minus1 populated.
* Q2: slice_segment_addr populated from VAAPI slice->slice_segment_address.
* S3: SCALING_MATRIX content matches FFmpeg pattern — memset zero when
* iqmatrix_set==false (BBB has no scaling list in SPS flags).
*/
#include "h265.h"
#include "context.h"
#include "object_heap.h"
#include "request.h"
#include "surface.h"
#include <assert.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <sys/mman.h>
#include <linux/videodev2.h>
#include <hevc-ctrls.h>
#include <linux/v4l2-controls.h>
#include "v4l2.h"
/*
* NAL unit header bit positions per ISO/IEC 23008-2 / H.265 spec.
* Used for nal_unit_type + nuh_temporal_id_plus1 extraction from
* the slice bitstream's first 2 bytes (after any ANNEX_B start code).
*/
#define H265_NAL_UNIT_TYPE_SHIFT 1
#define H265_NAL_UNIT_TYPE_MASK ((1 << 6) - 1)
#define H265_NUH_TEMPORAL_ID_PLUS1_SHIFT 0
#define H265_NUH_TEMPORAL_ID_PLUS1_MASK ((1 << 3) - 1)
static void h265_fill_pps(VAPictureParameterBufferHEVC *picture,
VASliceParameterBufferHEVC *slice,
struct v4l2_ctrl_hevc_pps *pps)
{
memset(pps, 0, sizeof(*pps));
pps->dependent_slice_segment_flag =
slice->LongSliceFlags.fields.dependent_slice_segment_flag;
pps->output_flag_present_flag =
picture->slice_parsing_fields.bits.output_flag_present_flag;
pps->num_extra_slice_header_bits =
picture->num_extra_slice_header_bits;
pps->sign_data_hiding_enabled_flag =
picture->pic_fields.bits.sign_data_hiding_enabled_flag;
pps->cabac_init_present_flag =
picture->slice_parsing_fields.bits.cabac_init_present_flag;
pps->init_qp_minus26 = picture->init_qp_minus26;
pps->constrained_intra_pred_flag =
picture->pic_fields.bits.constrained_intra_pred_flag;
pps->transform_skip_enabled_flag =
picture->pic_fields.bits.transform_skip_enabled_flag;
pps->cu_qp_delta_enabled_flag =
picture->pic_fields.bits.cu_qp_delta_enabled_flag;
pps->diff_cu_qp_delta_depth = picture->diff_cu_qp_delta_depth;
pps->pps_cb_qp_offset = picture->pps_cb_qp_offset;
pps->pps_cr_qp_offset = picture->pps_cr_qp_offset;
pps->pps_slice_chroma_qp_offsets_present_flag =
picture->slice_parsing_fields.bits.pps_slice_chroma_qp_offsets_present_flag;
pps->weighted_pred_flag =
picture->pic_fields.bits.weighted_pred_flag;
pps->weighted_bipred_flag =
picture->pic_fields.bits.weighted_bipred_flag;
pps->transquant_bypass_enabled_flag =
picture->pic_fields.bits.transquant_bypass_enabled_flag;
pps->tiles_enabled_flag =
picture->pic_fields.bits.tiles_enabled_flag;
pps->entropy_coding_sync_enabled_flag =
picture->pic_fields.bits.entropy_coding_sync_enabled_flag;
pps->num_tile_columns_minus1 = picture->num_tile_columns_minus1;
pps->num_tile_rows_minus1 = picture->num_tile_rows_minus1;
pps->loop_filter_across_tiles_enabled_flag =
picture->pic_fields.bits.loop_filter_across_tiles_enabled_flag;
pps->pps_loop_filter_across_slices_enabled_flag =
picture->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag;
pps->deblocking_filter_override_enabled_flag =
picture->slice_parsing_fields.bits.deblocking_filter_override_enabled_flag;
pps->pps_disable_deblocking_filter_flag =
picture->slice_parsing_fields.bits.pps_disable_deblocking_filter_flag;
pps->pps_beta_offset_div2 = picture->pps_beta_offset_div2;
pps->pps_tc_offset_div2 = picture->pps_tc_offset_div2;
pps->lists_modification_present_flag =
picture->slice_parsing_fields.bits.lists_modification_present_flag;
pps->log2_parallel_merge_level_minus2 =
picture->log2_parallel_merge_level_minus2;
}
/* ===== Clause 2: SPS (40 bytes) ===== */
static void h265_fill_sps(VAPictureParameterBufferHEVC *picture,
struct v4l2_ctrl_hevc_sps *sps)
{
memset(sps, 0, sizeof(*sps));
sps->chroma_format_idc = picture->pic_fields.bits.chroma_format_idc;
sps->separate_colour_plane_flag =
picture->pic_fields.bits.separate_colour_plane_flag;
sps->video_parameter_set_id = 0; /* not exposed by VAAPI */
sps->seq_parameter_set_id = 0; /* not exposed by VAAPI */
sps->pic_width_in_luma_samples = picture->pic_width_in_luma_samples;
sps->pic_height_in_luma_samples = picture->pic_height_in_luma_samples;
sps->bit_depth_luma_minus8 = picture->bit_depth_luma_minus8;
@@ -117,8 +107,8 @@ 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;
sps->sps_max_latency_increase_plus1 = 0;
sps->sps_max_num_reorder_pics = 0; /* not exposed */
sps->sps_max_latency_increase_plus1 = 0; /* not exposed */
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 =
@@ -131,12 +121,6 @@ static void h265_fill_sps(VAPictureParameterBufferHEVC *picture,
picture->max_transform_hierarchy_depth_inter;
sps->max_transform_hierarchy_depth_intra =
picture->max_transform_hierarchy_depth_intra;
sps->scaling_list_enabled_flag =
picture->pic_fields.bits.scaling_list_enabled_flag;
sps->amp_enabled_flag = picture->pic_fields.bits.amp_enabled_flag;
sps->sample_adaptive_offset_enabled_flag =
picture->slice_parsing_fields.bits.sample_adaptive_offset_enabled_flag;
sps->pcm_enabled_flag = picture->pic_fields.bits.pcm_enabled_flag;
sps->pcm_sample_bit_depth_luma_minus1 =
picture->pcm_sample_bit_depth_luma_minus1;
sps->pcm_sample_bit_depth_chroma_minus1 =
@@ -145,115 +129,250 @@ static void h265_fill_sps(VAPictureParameterBufferHEVC *picture,
picture->log2_min_pcm_luma_coding_block_size_minus3;
sps->log2_diff_max_min_pcm_luma_coding_block_size =
picture->log2_diff_max_min_pcm_luma_coding_block_size;
sps->pcm_loop_filter_disabled_flag =
picture->pic_fields.bits.pcm_loop_filter_disabled_flag;
sps->num_short_term_ref_pic_sets = picture->num_short_term_ref_pic_sets;
sps->long_term_ref_pics_present_flag =
picture->slice_parsing_fields.bits.long_term_ref_pics_present_flag;
sps->num_long_term_ref_pics_sps = picture->num_long_term_ref_pic_sps;
sps->sps_temporal_mvp_enabled_flag =
picture->slice_parsing_fields.bits.sps_temporal_mvp_enabled_flag;
sps->strong_intra_smoothing_enabled_flag =
picture->pic_fields.bits.strong_intra_smoothing_enabled_flag;
sps->chroma_format_idc = picture->pic_fields.bits.chroma_format_idc;
sps->sps_max_sub_layers_minus1 = 0; /* not exposed */
/* reserved[6] zeroed by memset */
/* 9 boolean flags collapsed to u64 */
if (picture->pic_fields.bits.separate_colour_plane_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_SEPARATE_COLOUR_PLANE;
if (picture->pic_fields.bits.scaling_list_enabled_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_SCALING_LIST_ENABLED;
if (picture->pic_fields.bits.amp_enabled_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_AMP_ENABLED;
if (picture->slice_parsing_fields.bits.sample_adaptive_offset_enabled_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_SAMPLE_ADAPTIVE_OFFSET;
if (picture->pic_fields.bits.pcm_enabled_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_PCM_ENABLED;
if (picture->pic_fields.bits.pcm_loop_filter_disabled_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_PCM_LOOP_FILTER_DISABLED;
if (picture->slice_parsing_fields.bits.long_term_ref_pics_present_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_LONG_TERM_REF_PICS_PRESENT;
if (picture->slice_parsing_fields.bits.sps_temporal_mvp_enabled_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_SPS_TEMPORAL_MVP_ENABLED;
if (picture->pic_fields.bits.strong_intra_smoothing_enabled_flag)
sps->flags |= V4L2_HEVC_SPS_FLAG_STRONG_INTRA_SMOOTHING_ENABLED;
}
static void h265_fill_slice_params(VAPictureParameterBufferHEVC *picture,
VASliceParameterBufferHEVC *slice,
struct object_heap *surface_heap,
void *source_data,
struct v4l2_ctrl_hevc_slice_params *slice_params)
/* ===== Clause 3: PPS (64 bytes; 21 flags + 3 newly-mapped scalars per S1+S2) ===== */
static void h265_fill_pps(VAPictureParameterBufferHEVC *picture,
VASliceParameterBufferHEVC *slice,
struct v4l2_ctrl_hevc_pps *pps)
{
memset(pps, 0, sizeof(*pps));
pps->pic_parameter_set_id = 0; /* S2: not exposed by VAAPI; default 0 */
pps->num_extra_slice_header_bits = picture->num_extra_slice_header_bits;
pps->num_ref_idx_l0_default_active_minus1 =
picture->num_ref_idx_l0_default_active_minus1; /* S2 */
pps->num_ref_idx_l1_default_active_minus1 =
picture->num_ref_idx_l1_default_active_minus1; /* S2 */
pps->init_qp_minus26 = picture->init_qp_minus26;
pps->diff_cu_qp_delta_depth = picture->diff_cu_qp_delta_depth;
pps->pps_cb_qp_offset = picture->pps_cb_qp_offset;
pps->pps_cr_qp_offset = picture->pps_cr_qp_offset;
pps->num_tile_columns_minus1 = picture->num_tile_columns_minus1;
pps->num_tile_rows_minus1 = picture->num_tile_rows_minus1;
/* column_width_minus1[20] + row_height_minus1[22] left zero — BBB single-tile */
pps->pps_beta_offset_div2 = picture->pps_beta_offset_div2;
pps->pps_tc_offset_div2 = picture->pps_tc_offset_div2;
pps->log2_parallel_merge_level_minus2 =
picture->log2_parallel_merge_level_minus2;
/* reserved zeroed by memset */
/* 21 boolean flags (bits 0-20) collapsed to u64 */
if (slice && slice->LongSliceFlags.fields.dependent_slice_segment_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_DEPENDENT_SLICE_SEGMENT_ENABLED;
if (picture->slice_parsing_fields.bits.output_flag_present_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_OUTPUT_FLAG_PRESENT;
if (picture->pic_fields.bits.sign_data_hiding_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_SIGN_DATA_HIDING_ENABLED;
if (picture->slice_parsing_fields.bits.cabac_init_present_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_CABAC_INIT_PRESENT;
if (picture->pic_fields.bits.constrained_intra_pred_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_CONSTRAINED_INTRA_PRED;
if (picture->pic_fields.bits.transform_skip_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_TRANSFORM_SKIP_ENABLED;
if (picture->pic_fields.bits.cu_qp_delta_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_CU_QP_DELTA_ENABLED;
if (picture->slice_parsing_fields.bits.pps_slice_chroma_qp_offsets_present_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_PPS_SLICE_CHROMA_QP_OFFSETS_PRESENT;
if (picture->pic_fields.bits.weighted_pred_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_WEIGHTED_PRED;
if (picture->pic_fields.bits.weighted_bipred_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_WEIGHTED_BIPRED;
if (picture->pic_fields.bits.transquant_bypass_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_TRANSQUANT_BYPASS_ENABLED;
if (picture->pic_fields.bits.tiles_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_TILES_ENABLED;
if (picture->pic_fields.bits.entropy_coding_sync_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_ENTROPY_CODING_SYNC_ENABLED;
if (picture->pic_fields.bits.loop_filter_across_tiles_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_LOOP_FILTER_ACROSS_TILES_ENABLED;
if (picture->pic_fields.bits.pps_loop_filter_across_slices_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_PPS_LOOP_FILTER_ACROSS_SLICES_ENABLED;
if (picture->slice_parsing_fields.bits.deblocking_filter_override_enabled_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_DEBLOCKING_FILTER_OVERRIDE_ENABLED;
if (picture->slice_parsing_fields.bits.pps_disable_deblocking_filter_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_PPS_DISABLE_DEBLOCKING_FILTER;
if (picture->slice_parsing_fields.bits.lists_modification_present_flag)
pps->flags |= V4L2_HEVC_PPS_FLAG_LISTS_MODIFICATION_PRESENT;
/* SLICE_SEGMENT_HEADER_EXTENSION_PRESENT (bit 18) — not exposed; skip */
/* DEBLOCKING_FILTER_CONTROL_PRESENT (bit 19, S1) — not exposed by VAAPI */
/* DEBLOCKING_FILTER_CONTROL_PRESENT (bit 19) and UNIFORM_SPACING (bit 20):
* VAAPI does not expose either flag in VAPictureParameterBufferHEVC.
* BBB-720p10s_hevc.mp4 uses neither tiles nor explicit deblocking-
* control parameters; leaving these bits zero is correct for the
* iter2 binding-cell fixture. */
}
/* ===== Clause 6: DECODE_PARAMS (328 bytes) =====
*
* NEW in modern API. Houses DPB info that was inside slice_params in
* the staging-era. Per Phase 5 C2: dpb[].flags has only LONG_TERM_REFERENCE
* bit; dpb[].pic_order_cnt_val (singular); poc_st_curr_*[] arrays hold
* u8 DPB INDICES (not POC values).
*
* Pattern: classify each VAAPI ReferenceFrames[i] into ST_CURR_BEFORE /
* ST_CURR_AFTER / LT_CURR; populate dpb[] sequentially; record the DPB
* index in the matching classification array.
*/
static void h265_fill_decode_params(struct request_data *driver_data,
VAPictureParameterBufferHEVC *picture,
struct v4l2_ctrl_hevc_decode_params *decode_params)
{
struct object_surface *surface_object;
VAPictureHEVC *hevc_picture;
uint8_t nal_unit_type;
uint8_t nuh_temporal_id_plus1;
uint32_t data_bit_offset;
uint8_t pic_struct;
uint8_t field_pic;
uint8_t slice_type;
unsigned int num_active_dpb_entries;
unsigned int num_rps_poc_st_curr_before;
unsigned int num_rps_poc_st_curr_after;
unsigned int num_rps_poc_lt_curr;
uint8_t *b;
unsigned int count;
unsigned int o, i, j;
unsigned int i;
uint8_t n_active = 0;
uint8_t n_st_before = 0, n_st_after = 0, n_lt = 0;
/* Extract the missing NAL header information. */
memset(decode_params, 0, sizeof(*decode_params));
b = source_data + slice->slice_data_offset;
decode_params->pic_order_cnt_val = picture->CurrPic.pic_order_cnt;
nal_unit_type = (b[0] >> H265_NAL_UNIT_TYPE_SHIFT) &
H265_NAL_UNIT_TYPE_MASK;
nuh_temporal_id_plus1 = (b[1] >> H265_NUH_TEMPORAL_ID_PLUS1_SHIFT) &
H265_NUH_TEMPORAL_ID_PLUS1_MASK;
for (i = 0; i < 15; i++) {
hevc_picture = &picture->ReferenceFrames[i];
/*
* VAAPI only provides a byte-aligned value for the slice segment data
* offset, although it appears that the offset is not always aligned.
* Search for the first one bit in the previous byte, that marks the
* start of the slice segment to correct the value.
*/
if (hevc_picture->picture_id == VA_INVALID_SURFACE ||
(hevc_picture->flags & VA_PICTURE_HEVC_INVALID))
continue;
b = source_data + (slice->slice_data_offset +
slice->slice_data_byte_offset) - 1;
surface_object = (struct object_surface *)
object_heap_lookup(&driver_data->surface_heap,
hevc_picture->picture_id);
if (surface_object == NULL)
continue;
for (o = 0; o < 8; o++)
if (*b & (1 << o))
if (n_active >= V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
break;
/* Include the one bit. */
o++;
decode_params->dpb[n_active].timestamp =
v4l2_timeval_to_ns(&surface_object->timestamp);
decode_params->dpb[n_active].pic_order_cnt_val =
hevc_picture->pic_order_cnt;
decode_params->dpb[n_active].field_pic =
!!(hevc_picture->flags & VA_PICTURE_HEVC_FIELD_PIC);
decode_params->dpb[n_active].flags =
(hevc_picture->flags & VA_PICTURE_HEVC_RPS_LT_CURR) ?
V4L2_HEVC_DPB_ENTRY_LONG_TERM_REFERENCE : 0;
/* dpb[n_active].reserved zeroed by memset */
data_bit_offset = (slice->slice_data_offset +
slice->slice_data_byte_offset) * 8 - o;
/* Classify into one of the three "current" lists.
* Each list holds the DPB INDEX (u8), not the POC value. */
if (hevc_picture->flags & VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE) {
if (n_st_before < V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
decode_params->poc_st_curr_before[n_st_before++] = n_active;
} else if (hevc_picture->flags & VA_PICTURE_HEVC_RPS_ST_CURR_AFTER) {
if (n_st_after < V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
decode_params->poc_st_curr_after[n_st_after++] = n_active;
} else if (hevc_picture->flags & VA_PICTURE_HEVC_RPS_LT_CURR) {
if (n_lt < V4L2_HEVC_DPB_ENTRIES_NUM_MAX)
decode_params->poc_lt_curr[n_lt++] = n_active;
}
n_active++;
}
decode_params->num_active_dpb_entries = n_active;
decode_params->num_poc_st_curr_before = n_st_before;
decode_params->num_poc_st_curr_after = n_st_after;
decode_params->num_poc_lt_curr = n_lt;
/* short_term_ref_pic_set_size, long_term_ref_pic_set_size,
* 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). */
decode_params->flags = 0;
}
/* ===== Clause 4: SLICE_PARAMS per slice =====
*
* Called per slice in a loop in h265_set_controls. Output is one entry
* in the dynamic-array of slice_params submitted to the kernel.
*
* source_offset is the byte offset within the surface_object->source_data
* buffer where this slice's bitstream begins (after any ANNEX_B start
* code prefix). data_byte_offset is the offset within the buffer to the
* first byte of slice header data.
*
* Per Phase 5 C1: data_byte_offset is a BYTE offset (not a bit offset).
* The old bit-search at h265.c:184-209 has been DROPPED.
*/
static void h265_fill_slice_params(VAPictureParameterBufferHEVC *picture,
VASliceParameterBufferHEVC *slice,
void *source_data,
unsigned int source_offset,
struct v4l2_ctrl_hevc_slice_params *slice_params)
{
uint8_t *b;
uint8_t nal_unit_type, nuh_temporal_id_plus1;
uint8_t pic_struct;
uint8_t slice_type;
unsigned int i, j;
memset(slice_params, 0, sizeof(*slice_params));
/* NAL header parse from slice bitstream (after ANNEX_B start code).
* source_offset points at the byte AFTER the start code (start code
* was prepended by codec_store_buffer:68-75 if context->h264_start_code
* is set). The first 2 bytes are the NAL unit header. */
b = (uint8_t *)source_data + source_offset;
nal_unit_type = (b[0] >> H265_NAL_UNIT_TYPE_SHIFT) & H265_NAL_UNIT_TYPE_MASK;
nuh_temporal_id_plus1 = (b[1] >> H265_NUH_TEMPORAL_ID_PLUS1_SHIFT) &
H265_NUH_TEMPORAL_ID_PLUS1_MASK;
slice_params->bit_size = slice->slice_data_size * 8;
slice_params->data_bit_offset = data_bit_offset;
/* C1: data_byte_offset, NOT data_bit_offset. Plain byte offset to
* the first byte of slice segment header data within the OUTPUT
* buffer. FFmpeg pattern at v4l2_request_hevc.c:190. */
slice_params->data_byte_offset = source_offset + slice->slice_data_byte_offset;
slice_params->num_entry_point_offsets = 0; /* iter2 doesn't do tiles */
slice_params->nal_unit_type = nal_unit_type;
slice_params->nuh_temporal_id_plus1 = nuh_temporal_id_plus1;
slice_type = slice->LongSliceFlags.fields.slice_type;
slice_params->slice_type = slice_type,
slice_params->colour_plane_id =
slice->LongSliceFlags.fields.color_plane_id;
slice_params->slice_pic_order_cnt =
picture->CurrPic.pic_order_cnt;
slice_params->slice_sao_luma_flag =
slice->LongSliceFlags.fields.slice_sao_luma_flag;
slice_params->slice_sao_chroma_flag =
slice->LongSliceFlags.fields.slice_sao_chroma_flag;
slice_params->slice_temporal_mvp_enabled_flag =
slice->LongSliceFlags.fields.slice_temporal_mvp_enabled_flag;
slice_params->num_ref_idx_l0_active_minus1 =
slice->num_ref_idx_l0_active_minus1;
slice_params->num_ref_idx_l1_active_minus1 =
slice->num_ref_idx_l1_active_minus1;
slice_params->mvd_l1_zero_flag =
slice->LongSliceFlags.fields.mvd_l1_zero_flag;
slice_params->cabac_init_flag =
slice->LongSliceFlags.fields.cabac_init_flag;
slice_params->collocated_from_l0_flag =
slice->LongSliceFlags.fields.collocated_from_l0_flag;
slice_params->slice_type = slice_type;
slice_params->colour_plane_id = slice->LongSliceFlags.fields.color_plane_id;
slice_params->slice_pic_order_cnt = picture->CurrPic.pic_order_cnt;
slice_params->num_ref_idx_l0_active_minus1 = slice->num_ref_idx_l0_active_minus1;
slice_params->num_ref_idx_l1_active_minus1 = slice->num_ref_idx_l1_active_minus1;
slice_params->collocated_ref_idx = slice->collocated_ref_idx;
slice_params->five_minus_max_num_merge_cand =
slice->five_minus_max_num_merge_cand;
slice_params->use_integer_mv_flag = 0;
slice_params->five_minus_max_num_merge_cand = slice->five_minus_max_num_merge_cand;
slice_params->slice_qp_delta = slice->slice_qp_delta;
slice_params->slice_cb_qp_offset = slice->slice_cb_qp_offset;
slice_params->slice_cr_qp_offset = slice->slice_cr_qp_offset;
slice_params->slice_act_y_qp_offset = 0;
slice_params->slice_act_y_qp_offset = 0; /* VAAPI doesn't expose */
slice_params->slice_act_cb_qp_offset = 0;
slice_params->slice_act_cr_qp_offset = 0;
slice_params->slice_deblocking_filter_disabled_flag =
slice->LongSliceFlags.fields.slice_deblocking_filter_disabled_flag;
slice_params->slice_beta_offset_div2 = slice->slice_beta_offset_div2;
slice_params->slice_tc_offset_div2 = slice->slice_tc_offset_div2;
slice_params->slice_loop_filter_across_slices_enabled_flag =
slice->LongSliceFlags.fields.slice_loop_filter_across_slices_enabled_flag;
if (picture->CurrPic.flags & VA_PICTURE_HEVC_FIELD_PIC) {
if (picture->CurrPic.flags & VA_PICTURE_HEVC_BOTTOM_FIELD)
@@ -263,84 +382,39 @@ static void h265_fill_slice_params(VAPictureParameterBufferHEVC *picture,
} else {
pic_struct = 0;
}
slice_params->pic_struct = pic_struct;
/* reserved0[3] zeroed by memset */
num_active_dpb_entries = 0;
num_rps_poc_st_curr_before = 0;
num_rps_poc_st_curr_after = 0;
num_rps_poc_lt_curr = 0;
/* Q2: slice_segment_addr from VAAPI (was missing in old h265.c). */
slice_params->slice_segment_addr = slice->slice_segment_address;
for (i = 0; i < 15 && slice_type != V4L2_HEVC_SLICE_TYPE_I ; i++) {
uint64_t timestamp;
hevc_picture = &picture->ReferenceFrames[i];
if (hevc_picture->picture_id == VA_INVALID_SURFACE ||
(hevc_picture->flags & VA_PICTURE_HEVC_INVALID) != 0)
break;
surface_object = (struct object_surface *)
object_heap_lookup(surface_heap,
hevc_picture->picture_id);
if (surface_object == NULL)
break;
timestamp = v4l2_timeval_to_ns(&surface_object->timestamp);
slice_params->dpb[i].timestamp = timestamp;
if ((hevc_picture->flags & VA_PICTURE_HEVC_RPS_ST_CURR_BEFORE) != 0) {
slice_params->dpb[i].rps =
V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_BEFORE;
num_rps_poc_st_curr_before++;
} else if ((hevc_picture->flags & VA_PICTURE_HEVC_RPS_ST_CURR_AFTER) != 0) {
slice_params->dpb[i].rps =
V4L2_HEVC_DPB_ENTRY_RPS_ST_CURR_AFTER;
num_rps_poc_st_curr_after++;
} else if ((hevc_picture->flags & VA_PICTURE_HEVC_RPS_LT_CURR) != 0) {
slice_params->dpb[i].rps =
V4L2_HEVC_DPB_ENTRY_RPS_LT_CURR;
num_rps_poc_lt_curr++;
}
field_pic = !!(hevc_picture->flags & VA_PICTURE_HEVC_FIELD_PIC);
slice_params->dpb[i].field_pic = field_pic;
/* TODO: Interleaved: Get the POC for each field. */
slice_params->dpb[i].pic_order_cnt[0] =
hevc_picture->pic_order_cnt;
num_active_dpb_entries++;
/* Ref index arrays (DPB indices). For I-slices both are unused. */
for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX &&
slice_type != V4L2_HEVC_SLICE_TYPE_I; i++) {
if (i < (slice->num_ref_idx_l0_active_minus1 + 1U))
slice_params->ref_idx_l0[i] = slice->RefPicList[0][i];
}
for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX &&
slice_type == V4L2_HEVC_SLICE_TYPE_B; i++) {
if (i < (slice->num_ref_idx_l1_active_minus1 + 1U))
slice_params->ref_idx_l1[i] = slice->RefPicList[1][i];
}
slice_params->num_active_dpb_entries = num_active_dpb_entries;
count = slice_params->num_ref_idx_l0_active_minus1 + 1;
for (i = 0; i < count && slice_type != V4L2_HEVC_SLICE_TYPE_I; i++)
slice_params->ref_idx_l0[i] = slice->RefPicList[0][i];
count = slice_params->num_ref_idx_l1_active_minus1 + 1;
for (i = 0; i < count && slice_type == V4L2_HEVC_SLICE_TYPE_B ; i++)
slice_params->ref_idx_l1[i] = slice->RefPicList[1][i];
slice_params->num_rps_poc_st_curr_before = num_rps_poc_st_curr_before;
slice_params->num_rps_poc_st_curr_after = num_rps_poc_st_curr_after;
slice_params->num_rps_poc_lt_curr = num_rps_poc_lt_curr;
slice_params->short_term_ref_pic_set_size = 0; /* VAAPI doesn't expose */
slice_params->long_term_ref_pic_set_size = 0;
/* Pred weight table */
slice_params->pred_weight_table.luma_log2_weight_denom =
slice->luma_log2_weight_denom;
slice_params->pred_weight_table.delta_chroma_log2_weight_denom =
slice->delta_chroma_log2_weight_denom;
for (i = 0; i < 15 && slice_type != V4L2_HEVC_SLICE_TYPE_I; i++) {
for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX &&
slice_type != V4L2_HEVC_SLICE_TYPE_I; i++) {
slice_params->pred_weight_table.delta_luma_weight_l0[i] =
slice->delta_luma_weight_l0[i];
slice_params->pred_weight_table.luma_offset_l0[i] =
slice->luma_offset_l0[i];
for (j = 0; j < 2; j++) {
slice_params->pred_weight_table.delta_chroma_weight_l0[i][j] =
slice->delta_chroma_weight_l0[i][j];
@@ -348,13 +422,12 @@ static void h265_fill_slice_params(VAPictureParameterBufferHEVC *picture,
slice->ChromaOffsetL0[i][j];
}
}
for (i = 0; i < 15 && slice_type == V4L2_HEVC_SLICE_TYPE_B; i++) {
for (i = 0; i < V4L2_HEVC_DPB_ENTRIES_NUM_MAX &&
slice_type == V4L2_HEVC_SLICE_TYPE_B; i++) {
slice_params->pred_weight_table.delta_luma_weight_l1[i] =
slice->delta_luma_weight_l1[i];
slice_params->pred_weight_table.luma_offset_l1[i] =
slice->luma_offset_l1[i];
for (j = 0; j < 2; j++) {
slice_params->pred_weight_table.delta_chroma_weight_l1[i][j] =
slice->delta_chroma_weight_l1[i][j];
@@ -362,44 +435,152 @@ static void h265_fill_slice_params(VAPictureParameterBufferHEVC *picture,
slice->ChromaOffsetL1[i][j];
}
}
/* reserved1[2] zeroed by memset */
/* 10 SLICE_PARAMS flag bits */
if (slice->LongSliceFlags.fields.slice_sao_luma_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_LUMA;
if (slice->LongSliceFlags.fields.slice_sao_chroma_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_SAO_CHROMA;
if (slice->LongSliceFlags.fields.slice_temporal_mvp_enabled_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_TEMPORAL_MVP_ENABLED;
if (slice->LongSliceFlags.fields.mvd_l1_zero_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_MVD_L1_ZERO;
if (slice->LongSliceFlags.fields.cabac_init_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_CABAC_INIT;
if (slice->LongSliceFlags.fields.collocated_from_l0_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_COLLOCATED_FROM_L0;
/* USE_INTEGER_MV — VAAPI doesn't expose; leave 0 */
if (slice->LongSliceFlags.fields.slice_deblocking_filter_disabled_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_DEBLOCKING_FILTER_DISABLED;
if (slice->LongSliceFlags.fields.slice_loop_filter_across_slices_enabled_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_SLICE_LOOP_FILTER_ACROSS_SLICES_ENABLED;
if (slice->LongSliceFlags.fields.dependent_slice_segment_flag)
slice_params->flags |= V4L2_HEVC_SLICE_PARAMS_FLAG_DEPENDENT_SLICE_SEGMENT;
}
/* ===== Clause 5: SCALING_MATRIX (1296 bytes; conditional fill) =====
*
* Per Phase 5 S3: when iqmatrix_set==false (BBB has no scaling list
* in SPS flags), send memset-zero. Matches FFmpeg's pattern when the
* stream has no scaling list. When iqmatrix_set==true, copy from VAAPI
* VAIQMatrixBufferHEVC.
*/
static void h265_fill_scaling_matrix(VAIQMatrixBufferHEVC *iqmatrix,
bool iqmatrix_set,
struct v4l2_ctrl_hevc_scaling_matrix *scaling_matrix)
{
memset(scaling_matrix, 0, sizeof(*scaling_matrix));
if (!iqmatrix_set)
return; /* memset zero matches FFmpeg sl=NULL path */
memcpy(scaling_matrix->scaling_list_4x4,
iqmatrix->ScalingList4x4, sizeof(iqmatrix->ScalingList4x4));
memcpy(scaling_matrix->scaling_list_8x8,
iqmatrix->ScalingList8x8, sizeof(iqmatrix->ScalingList8x8));
memcpy(scaling_matrix->scaling_list_16x16,
iqmatrix->ScalingList16x16, sizeof(iqmatrix->ScalingList16x16));
memcpy(scaling_matrix->scaling_list_32x32,
iqmatrix->ScalingList32x32, sizeof(iqmatrix->ScalingList32x32));
memcpy(scaling_matrix->scaling_list_dc_coef_16x16,
iqmatrix->ScalingListDC16x16,
sizeof(iqmatrix->ScalingListDC16x16));
memcpy(scaling_matrix->scaling_list_dc_coef_32x32,
iqmatrix->ScalingListDC32x32,
sizeof(iqmatrix->ScalingListDC32x32));
}
/* ===== Clause 1: orchestrator — batched 5-control submission ===== */
int h265_set_controls(struct request_data *driver_data,
struct object_context *context_object,
struct object_surface *surface_object)
{
VAPictureParameterBufferHEVC *picture =
&surface_object->params.h265.picture;
VASliceParameterBufferHEVC *slice =
&surface_object->params.h265.slice;
VAIQMatrixBufferHEVC *iqmatrix =
&surface_object->params.h265.iqmatrix;
bool iqmatrix_set = surface_object->params.h265.iqmatrix_set;
struct v4l2_ctrl_hevc_pps pps;
unsigned int num_slices = surface_object->params.h265.num_slices;
struct v4l2_ctrl_hevc_sps sps;
struct v4l2_ctrl_hevc_slice_params slice_params;
struct v4l2_ctrl_hevc_pps pps;
struct v4l2_ctrl_hevc_decode_params decode_params;
struct v4l2_ctrl_hevc_scaling_matrix scaling_matrix;
struct v4l2_ctrl_hevc_slice_params *slice_params_array = NULL;
struct v4l2_ext_control controls[5];
unsigned int n = 0;
unsigned int i;
unsigned int prefix_bytes;
unsigned int cumulative_offset = 0;
int rc;
h265_fill_pps(picture, slice, &pps);
rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd,
V4L2_CID_MPEG_VIDEO_HEVC_PPS, &pps, sizeof(pps));
if (rc < 0)
if (num_slices == 0)
return VA_STATUS_ERROR_OPERATION_FAILED;
slice_params_array = calloc(num_slices,
sizeof(struct v4l2_ctrl_hevc_slice_params));
if (slice_params_array == NULL)
return VA_STATUS_ERROR_ALLOCATION_FAILED;
/* Per-slice fill. ANNEX_B start code (3 bytes 0x00 0x00 0x01) is
* prepended per slice by codec_store_buffer:68-75 when
* context->h264_start_code is true. Track cumulative offset
* accordingly. */
prefix_bytes = context_object->h264_start_code ? 3 : 0;
for (i = 0; i < num_slices; i++) {
VASliceParameterBufferHEVC *slice =
&surface_object->params.h265.slices[i];
cumulative_offset += prefix_bytes; /* skip start code prefix for this slice */
h265_fill_slice_params(picture, slice,
surface_object->source_data,
cumulative_offset,
&slice_params_array[i]);
cumulative_offset += slice->slice_data_size;
}
h265_fill_sps(picture, &sps);
h265_fill_pps(picture, &surface_object->params.h265.slices[0], &pps);
h265_fill_decode_params(driver_data, picture, &decode_params);
h265_fill_scaling_matrix(iqmatrix, iqmatrix_set, &scaling_matrix);
rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd,
V4L2_CID_MPEG_VIDEO_HEVC_SPS, &sps, sizeof(sps));
if (rc < 0)
return VA_STATUS_ERROR_OPERATION_FAILED;
controls[n++] = (struct v4l2_ext_control){
.id = V4L2_CID_STATELESS_HEVC_SPS,
.ptr = &sps,
.size = sizeof(sps),
};
controls[n++] = (struct v4l2_ext_control){
.id = V4L2_CID_STATELESS_HEVC_PPS,
.ptr = &pps,
.size = sizeof(pps),
};
controls[n++] = (struct v4l2_ext_control){
.id = V4L2_CID_STATELESS_HEVC_SLICE_PARAMS,
.ptr = slice_params_array,
.size = sizeof(struct v4l2_ctrl_hevc_slice_params) * num_slices,
};
controls[n++] = (struct v4l2_ext_control){
.id = V4L2_CID_STATELESS_HEVC_SCALING_MATRIX,
.ptr = &scaling_matrix,
.size = sizeof(scaling_matrix),
};
controls[n++] = (struct v4l2_ext_control){
.id = V4L2_CID_STATELESS_HEVC_DECODE_PARAMS,
.ptr = &decode_params,
.size = sizeof(decode_params),
};
h265_fill_slice_params(picture, slice, &driver_data->surface_heap,
surface_object->source_data, &slice_params);
rc = v4l2_set_controls(driver_data->video_fd,
surface_object->request_fd,
controls, n);
free(slice_params_array);
rc = v4l2_set_control(driver_data->video_fd, surface_object->request_fd,
V4L2_CID_MPEG_VIDEO_HEVC_SLICE_PARAMS,
&slice_params, sizeof(slice_params));
if (rc < 0)
return VA_STATUS_ERROR_OPERATION_FAILED;
+6
View File
@@ -27,6 +27,12 @@
#ifndef _H265_H_
#define _H265_H_
/* Maximum number of slices per frame the libva backend will accumulate
* before submitting to the kernel (kernel HEVC slice_params dynamic-array
* accepts up to 600 entries per Phase 0 V4L2 inventory; 64 is a
* conservative cap for typical fixtures + safety bound). */
#define HEVC_MAX_SLICES_PER_FRAME 64
struct object_context;
struct object_surface;
struct request_data;
+2 -2
View File
@@ -47,7 +47,7 @@ sources = [
'h264_slice_header.c',
'request_pool.c',
'cap_pool.c',
# 'h265.c'
'h265.c'
]
headers = [
@@ -70,7 +70,7 @@ headers = [
'h264_slice_header.h',
'request_pool.h',
'cap_pool.h',
# 'h265.h'
'h265.h'
]
includes = [
+16 -3
View File
@@ -124,11 +124,21 @@ static VAStatus codec_store_buffer(struct request_data *driver_data,
sizeof(surface_object->params.h264.slice));
break;
case VAProfileHEVCMain:
case VAProfileHEVCMain: {
unsigned int n = surface_object->params.h265.num_slices;
if (n < HEVC_MAX_SLICES_PER_FRAME) {
memcpy(&surface_object->params.h265.slices[n],
buffer_object->data,
sizeof(VASliceParameterBufferHEVC));
surface_object->params.h265.num_slices = n + 1;
}
/* Keep .slice mirror populated as last-slice ref for
* h265_fill_pps which reads dependent_slice_segment_flag */
memcpy(&surface_object->params.h265.slice,
buffer_object->data,
sizeof(surface_object->params.h265.slice));
break;
}
default:
break;
@@ -202,8 +212,10 @@ static VAStatus codec_set_controls(struct request_data *driver_data,
break;
case VAProfileHEVCMain:
/* Fourier-local: HEVC stripped, no HW support on RK3566. */
return VA_STATUS_ERROR_UNSUPPORTED_PROFILE;
rc = h265_set_controls(driver_data, context, surface_object);
if (rc < 0)
return VA_STATUS_ERROR_OPERATION_FAILED;
break;
default:
return VA_STATUS_ERROR_UNSUPPORTED_PROFILE;
@@ -285,6 +297,7 @@ VAStatus RequestBeginPicture(VADriverContextP context, VAContextID context_id,
surface_object->slices_size = 0;
surface_object->slices_count = 0;
surface_object->params.h264.matrix_set = false;
surface_object->params.h265.num_slices = 0;
surface_object->status = VASurfaceRendering;
context_object->render_surface_id = surface_id;
+4
View File
@@ -34,6 +34,8 @@
#include "object_heap.h"
#include "cap_pool.h"
#include "h265.h"
struct request_data;
#define SURFACE(data, id) \
@@ -103,6 +105,8 @@ struct object_surface {
struct {
VAPictureParameterBufferHEVC picture;
VASliceParameterBufferHEVC slice;
VASliceParameterBufferHEVC slices[HEVC_MAX_SLICES_PER_FRAME];
unsigned int num_slices;
VAIQMatrixBufferHEVC iqmatrix;
bool iqmatrix_set;
} h265;