forked from marfrit/libva-v4l2-request-fourier
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:
@@ -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
@@ -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;
|
||||
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user