use super::h264_refs::RefListEntry;
use super::h264_slice::{SliceHeader, SliceType};
use super::h264_sps_pps::{Pps, Sps};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
#[repr(transparent)]
pub(super) struct DxvaPicEntryH264(pub(super) u8);
impl DxvaPicEntryH264 {
pub(super) const UNUSED: Self = Self(0xFF);
pub(super) const fn pack(index7_bits: u8, long_term: bool) -> Self {
let base = index7_bits & 0x7F;
Self(if long_term { base | 0x80 } else { base })
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub(super) struct DxvaPicParamsH264 {
pub(super) w_frame_width_in_mbs_minus1: u16,
pub(super) w_frame_height_in_mbs_minus1: u16,
pub(super) curr_pic: DxvaPicEntryH264,
pub(super) num_ref_frames: u8,
pub(super) w_bit_fields: u16,
pub(super) bit_depth_luma_minus8: u8,
pub(super) bit_depth_chroma_minus8: u8,
pub(super) reserved16_bits: u16,
pub(super) status_report_feedback_number: u32,
pub(super) ref_frame_list: [DxvaPicEntryH264; 16],
pub(super) curr_field_order_cnt: [i32; 2],
pub(super) field_order_cnt_list: [[i32; 2]; 16],
pub(super) pic_init_qs_minus26: i8,
pub(super) chroma_qp_index_offset: i8,
pub(super) second_chroma_qp_index_offset: i8,
pub(super) continuation_flag: u8,
pub(super) pic_init_qp_minus26: i8,
pub(super) num_ref_idx_l0_active_minus1: u8,
pub(super) num_ref_idx_l1_active_minus1: u8,
pub(super) reserved8_bits_a: u8,
pub(super) frame_num_list: [u16; 16],
pub(super) used_for_reference_flags: u32,
pub(super) non_existing_frame_flags: u16,
pub(super) frame_num: u16,
pub(super) log2_max_frame_num_minus4: u8,
pub(super) pic_order_cnt_type: u8,
pub(super) log2_max_pic_order_cnt_lsb_minus4: u8,
pub(super) delta_pic_order_always_zero_flag: u8,
pub(super) direct_8x8_inference_flag: u8,
pub(super) entropy_coding_mode_flag: u8,
pub(super) pic_order_present_flag: u8,
pub(super) num_slice_groups_minus1: u8,
pub(super) slice_group_map_type: u8,
pub(super) deblocking_filter_control_present_flag: u8,
pub(super) redundant_pic_cnt_present_flag: u8,
pub(super) reserved8_bits_b: u8,
pub(super) slice_group_change_rate_minus1: u16,
pub(super) slice_group_map: [u8; 810],
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub(super) struct DxvaQmatrixH264 {
pub(super) scaling_lists_4x4: [[u8; 16]; 6],
pub(super) scaling_lists_8x8: [[u8; 64]; 2],
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub(super) struct DxvaSliceH264Long {
pub(super) bs_nal_unit_data_location: u32,
pub(super) slice_bytes_in_buffer: u32,
pub(super) w_bad_slice_chopping: u16,
pub(super) first_mb_in_slice: u16,
pub(super) num_mbs_for_slice: u16,
pub(super) bit_offset_to_slice_data: u16,
pub(super) slice_type: u8,
pub(super) luma_log2_weight_denom: u8,
pub(super) chroma_log2_weight_denom: u8,
pub(super) num_ref_idx_l0_active_minus1: u8,
pub(super) num_ref_idx_l1_active_minus1: u8,
pub(super) slice_alpha_c0_offset_div2: i8,
pub(super) slice_beta_offset_div2: i8,
pub(super) reserved8_bits: u8,
pub(super) ref_pic_list: [[DxvaPicEntryH264; 32]; 2],
pub(super) weights: [[[[i16; 2]; 3]; 32]; 2],
pub(super) slice_qs_delta: i8,
pub(super) slice_qp_delta: i8,
pub(super) redundant_pic_cnt: u8,
pub(super) direct_spatial_mv_pred_flag: u8,
pub(super) cabac_init_idc: u8,
pub(super) disable_deblocking_filter_idc: u8,
pub(super) slice_id: u16,
}
#[allow(
clippy::too_many_arguments,
clippy::fn_params_excessive_bools,
reason = "mirrors the DXVA bitfield layout 1:1 — each bool is one independent bitfield, not a state machine"
)]
fn pack_bit_fields(
field_pic_flag: bool,
mbaff_frame_flag: bool,
chroma_format_idc: u16,
ref_pic_flag: bool,
constrained_intra_pred_flag: bool,
weighted_pred_flag: bool,
weighted_bipred_idc: u16,
mbs_consecutive_flag: bool,
frame_mbs_only_flag: bool,
transform_8x8_mode_flag: bool,
min_luma_bipred_size8x8_flag: bool,
intra_pic_flag: bool,
) -> u16 {
u16::from(field_pic_flag)
| (u16::from(mbaff_frame_flag) << 1)
| ((chroma_format_idc & 0b11) << 4)
| (u16::from(ref_pic_flag) << 6)
| (u16::from(constrained_intra_pred_flag) << 7)
| (u16::from(weighted_pred_flag) << 8)
| ((weighted_bipred_idc & 0b11) << 9)
| (u16::from(mbs_consecutive_flag) << 11)
| (u16::from(frame_mbs_only_flag) << 12)
| (u16::from(transform_8x8_mode_flag) << 13)
| (u16::from(min_luma_bipred_size8x8_flag) << 14)
| (u16::from(intra_pic_flag) << 15)
}
#[allow(
clippy::too_many_arguments,
reason = "one linear DXVA struct fill, mirrors FFmpeg's own dxva2_h264.c fill_picture_parameters shape"
)]
pub(super) fn build_pic_params(
sps: &Sps,
pps: &Pps,
sh: &SliceHeader,
curr_top_poc: i32,
curr_bottom_poc: i32,
curr_pic_slot: u32,
nal_ref_idc: u8,
frame_num: u32,
refs: &[(u32, super::h264_refs::H264RefMeta)],
status_report_feedback_number: u32,
) -> DxvaPicParamsH264 {
let mut ref_frame_list = [DxvaPicEntryH264::UNUSED; 16];
let mut field_order_cnt_list = [[0i32; 2]; 16];
let mut frame_num_list = [0u16; 16];
let mut used_for_reference_flags = 0u32;
for (i, &(slot, meta)) in refs.iter().take(16).enumerate() {
ref_frame_list[i] = DxvaPicEntryH264::pack(u8::try_from(slot).unwrap_or(0), false);
field_order_cnt_list[i] = [meta.top_field_order_cnt, meta.bottom_field_order_cnt];
frame_num_list[i] = u16::try_from(meta.frame_num).unwrap_or(0);
used_for_reference_flags |= 0b11 << (i * 2);
}
let is_intra_only = matches!(sh.slice_type, SliceType::I);
let w_bit_fields = pack_bit_fields(
false, false, 1, nal_ref_idc != 0,
pps.constrained_intra_pred_flag,
pps.weighted_pred_flag,
u16::try_from(pps.weighted_bipred_idc).unwrap_or(0),
true, true, pps.transform_8x8_mode_flag,
sps.direct_8x8_inference_flag, is_intra_only,
);
DxvaPicParamsH264 {
w_frame_width_in_mbs_minus1: u16::try_from(sps.mb_width.saturating_sub(1)).unwrap_or(0),
w_frame_height_in_mbs_minus1: u16::try_from(sps.mb_height.saturating_sub(1)).unwrap_or(0),
curr_pic: DxvaPicEntryH264::pack(u8::try_from(curr_pic_slot).unwrap_or(0), false),
num_ref_frames: u8::try_from(sps.max_num_ref_frames).unwrap_or(16),
w_bit_fields,
bit_depth_luma_minus8: u8::try_from(sps.bit_depth_luma_minus8).unwrap_or(0),
bit_depth_chroma_minus8: u8::try_from(sps.bit_depth_chroma_minus8).unwrap_or(0),
reserved16_bits: 0,
status_report_feedback_number,
ref_frame_list,
curr_field_order_cnt: [curr_top_poc, curr_bottom_poc],
field_order_cnt_list,
pic_init_qs_minus26: i8::try_from(pps.pic_init_qs_minus26).unwrap_or(0),
chroma_qp_index_offset: i8::try_from(pps.chroma_qp_index_offset).unwrap_or(0),
second_chroma_qp_index_offset: i8::try_from(pps.second_chroma_qp_index_offset).unwrap_or(0),
continuation_flag: 1,
pic_init_qp_minus26: i8::try_from(pps.pic_init_qp_minus26).unwrap_or(0),
num_ref_idx_l0_active_minus1: u8::try_from(sh.num_ref_idx_l0_active_minus1).unwrap_or(0),
num_ref_idx_l1_active_minus1: u8::try_from(sh.num_ref_idx_l1_active_minus1).unwrap_or(0),
reserved8_bits_a: 0,
frame_num_list,
used_for_reference_flags,
non_existing_frame_flags: 0,
frame_num: u16::try_from(frame_num).unwrap_or(0),
log2_max_frame_num_minus4: u8::try_from(sps.log2_max_frame_num.saturating_sub(4))
.unwrap_or(0),
pic_order_cnt_type: u8::try_from(sps.pic_order_cnt_type).unwrap_or(0),
log2_max_pic_order_cnt_lsb_minus4: u8::try_from(
sps.log2_max_pic_order_cnt_lsb.saturating_sub(4),
)
.unwrap_or(0),
delta_pic_order_always_zero_flag: u8::from(sps.delta_pic_order_always_zero_flag),
direct_8x8_inference_flag: u8::from(sps.direct_8x8_inference_flag),
entropy_coding_mode_flag: u8::from(pps.entropy_coding_mode_flag),
pic_order_present_flag: u8::from(pps.bottom_field_pic_order_in_frame_present_flag),
num_slice_groups_minus1: u8::try_from(pps.num_slice_groups_minus1).unwrap_or(0),
slice_group_map_type: 0,
deblocking_filter_control_present_flag: u8::from(
pps.deblocking_filter_control_present_flag,
),
redundant_pic_cnt_present_flag: u8::from(pps.redundant_pic_cnt_present_flag),
reserved8_bits_b: 0,
slice_group_change_rate_minus1: 0,
slice_group_map: [0u8; 810],
}
}
pub(super) const fn flat_qmatrix() -> DxvaQmatrixH264 {
DxvaQmatrixH264 {
scaling_lists_4x4: [[16u8; 16]; 6],
scaling_lists_8x8: [[16u8; 64]; 2],
}
}
const fn slice_type_dxva_value(slice_type: SliceType) -> u8 {
match slice_type {
SliceType::P => 0,
SliceType::B => 1,
SliceType::I => 2,
SliceType::Sp => 3,
SliceType::Si => 4,
}
}
fn pack_ref_pic_list(entries: &[RefListEntry]) -> [DxvaPicEntryH264; 32] {
let mut out = [DxvaPicEntryH264::UNUSED; 32];
for (i, entry) in entries.iter().take(32).enumerate() {
out[i] = DxvaPicEntryH264::pack(u8::try_from(entry.slot).unwrap_or(0), false);
}
out
}
#[allow(
clippy::too_many_arguments,
reason = "mirrors DXVA_Slice_H264_Long's field list 1:1"
)]
pub(super) fn build_slice_long(
sh: &SliceHeader,
num_mbs_for_slice: u32,
bit_offset_to_slice_data: u32,
bs_nal_unit_data_location: u32,
slice_bytes_in_buffer: u32,
ref_list0: &[RefListEntry],
ref_list1: &[RefListEntry],
) -> DxvaSliceH264Long {
DxvaSliceH264Long {
bs_nal_unit_data_location,
slice_bytes_in_buffer,
w_bad_slice_chopping: 0,
first_mb_in_slice: 0,
num_mbs_for_slice: u16::try_from(num_mbs_for_slice).unwrap_or(u16::MAX),
bit_offset_to_slice_data: u16::try_from(bit_offset_to_slice_data).unwrap_or(u16::MAX),
slice_type: slice_type_dxva_value(sh.slice_type),
luma_log2_weight_denom: 0,
chroma_log2_weight_denom: 0,
num_ref_idx_l0_active_minus1: u8::try_from(sh.num_ref_idx_l0_active_minus1).unwrap_or(0),
num_ref_idx_l1_active_minus1: u8::try_from(sh.num_ref_idx_l1_active_minus1).unwrap_or(0),
slice_alpha_c0_offset_div2: i8::try_from(sh.slice_alpha_c0_offset_div2).unwrap_or(0),
slice_beta_offset_div2: i8::try_from(sh.slice_beta_offset_div2).unwrap_or(0),
reserved8_bits: 0,
ref_pic_list: [pack_ref_pic_list(ref_list0), pack_ref_pic_list(ref_list1)],
weights: [[[[0i16; 2]; 3]; 32]; 2],
slice_qs_delta: 0,
slice_qp_delta: i8::try_from(sh.slice_qp_delta).unwrap_or(0),
redundant_pic_cnt: 0,
direct_spatial_mv_pred_flag: u8::from(sh.direct_spatial_mv_pred_flag),
cabac_init_idc: u8::try_from(sh.cabac_init_idc).unwrap_or(0),
disable_deblocking_filter_idc: u8::try_from(sh.disable_deblocking_filter_idc).unwrap_or(0),
slice_id: 0,
}
}