use super::*; use super::ffi::*; use super::config::*; use super::surface::*; use super::align_up;
use crate::encode::tuning::{self, QualityTarget, QsvRateControl, SpeedTier};
use crate::frame::{ColorMetadata, PixelFormat, TransferFn};
use crate::qsv_ffi::{MfxExtBuffer, MfxFrameInfo, MfxInfoMfx};
#[test]
fn test_qsv_icq_quality_lands_on_correct_struct_field() {
let slots = rate_slots_for_rc(QsvRateControl::Icq, 0, 0, 28);
assert_eq!(
slots.slot1_qpp_or_kbps_or_icq, 28,
"ICQ quality must land in slot 1 (qpp_or_kbps_or_icq) per \
vendor/intel/mfxstructs.h:83 — this is the TargetKbps/QPP/ICQQuality \
union arm"
);
assert_eq!(
slots.slot0_qpi_or_delay, 0,
"slot 0 is InitialDelayInKB/QPI/Accuracy per \
vendor/intel/mfxstructs.h:74-78 — no ICQQuality here"
);
assert_eq!(
slots.slot2_qpb_or_maxkbps, 0,
"slot 2 is MaxKbps/QPB/Convergence per \
vendor/intel/mfxstructs.h:85-89 — no ICQQuality here"
);
}
#[test]
fn test_qsv_cqp_slots_mirror_qpi_qpp_qpb() {
let slots = rate_slots_for_rc(QsvRateControl::Cqp, 72, 96, 0);
assert_eq!(slots.slot0_qpi_or_delay, 72);
assert_eq!(slots.slot1_qpp_or_kbps_or_icq, 96);
assert_eq!(slots.slot2_qpb_or_maxkbps, 96);
}
#[test]
fn test_qsv_target_usage_maps_from_speed_tier() {
let (w, h) = (1920, 1080);
let cases = [
(SpeedTier::Archive, 1u16, "1 = BEST_QUALITY per mfxdefs.h:91"),
(SpeedTier::Standard, 4u16, "4 = BALANCED per mfxdefs.h:92"),
(SpeedTier::Draft, 6u16, "6 = one step from BEST_SPEED (7)"),
];
for (tier, expected, reason) in cases {
let tp = tuning::qsv_av1_params(QualityTarget::Standard, tier, w, h);
let got = clamp_target_usage(tp.target_usage);
assert_eq!(got, expected, "{tier:?} → {got} (want {expected}, {reason})");
assert!(
(1..=7).contains(&got),
"TargetUsage must be 1..7 per vendor/intel/mfxdefs.h:91-93"
);
}
}
#[test]
fn test_qsv_target_usage_clamps_out_of_range() {
assert_eq!(clamp_target_usage(0), 1, "0 clamps up to 1");
assert_eq!(clamp_target_usage(8), 7, "8 clamps down to 7");
assert_eq!(clamp_target_usage(255), 7, "255 clamps down to 7");
assert_eq!(clamp_target_usage(4), 4, "4 passes through");
}
#[test]
fn test_qsv_ring_buffer_index_cycles() {
let mut idx = 0usize;
let mut seen = Vec::new();
for _ in 0..(RING_SIZE * 3) {
seen.push(idx);
idx = (idx + 1) % RING_SIZE;
}
assert_eq!(
seen,
vec![0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3],
"ring index must cycle through 0..RING_SIZE"
);
}
#[test]
fn test_qsv_ring_size_is_four() {
assert_eq!(RING_SIZE, 4);
}
#[test]
fn test_qsv_more_data_on_encode_returns_no_packet() {
fn simulate_encode(rc: MfxStatus) -> std::result::Result<Option<()>, String> {
match rc {
MFX_ERR_NONE => Ok(Some(())), MFX_ERR_MORE_DATA => Ok(None), err if err > 0 => Ok(None), err => Err(format!("encode failed: {err}")),
}
}
assert_eq!(simulate_encode(MFX_ERR_MORE_DATA).unwrap(), None);
assert_eq!(simulate_encode(MFX_ERR_NONE).unwrap(), Some(()));
assert!(simulate_encode(-1).is_err(), "unknown negative = hard error");
}
#[test]
fn test_qsv_eof_drain_ends_cleanly() {
fn simulate_flush_tick(rc: MfxStatus) -> std::result::Result<bool, String> {
match rc {
MFX_ERR_NONE => Ok(false),
MFX_ERR_MORE_DATA => Ok(true),
err if err > 0 => Ok(false),
err => Err(format!("flush failed: {err}")),
}
}
assert_eq!(simulate_flush_tick(MFX_ERR_MORE_DATA).unwrap(), true,
"clean EOF: flush terminates on MORE_DATA without error");
assert_eq!(simulate_flush_tick(MFX_ERR_NONE).unwrap(), false,
"NONE: flush has more output to drain");
assert_eq!(simulate_flush_tick(MFX_WRN_VIDEO_PARAM_CHANGED).unwrap(), false,
"warning: flush keeps looping to drain the bitstream");
assert!(simulate_flush_tick(-5).is_err(), "hard negative error bails");
}
#[test]
fn test_qsv_more_data_and_more_surface_are_distinct() {
assert_eq!(MFX_ERR_MORE_DATA, -10);
assert_eq!(MFX_ERR_MORE_SURFACE, -11);
assert_ne!(MFX_ERR_MORE_DATA, MFX_ERR_MORE_SURFACE);
}
#[test]
fn test_qsv_fourcc_literals_match_macro() {
fn make(a: u8, b: u8, c: u8, d: u8) -> u32 {
(a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
}
assert_eq!(MFX_CODEC_AV1, make(b'A', b'V', b'1', b' '));
assert_eq!(MFX_FOURCC_NV12, make(b'N', b'V', b'1', b'2'));
assert_eq!(MFX_EXTBUFF_AV1_TILE_PARAM, make(b'A', b'1', b'T', b'L'));
assert_eq!(MFX_EXTBUFF_AV1_BITSTREAM_PARAM, make(b'A', b'V', b'1', b'B'));
}
#[test]
fn test_qsv_profile_main_equals_one() {
assert_eq!(MFX_PROFILE_AV1_MAIN, 1);
}
#[test]
fn test_qsv_chroma_format_yuv420_equals_one() {
assert_eq!(MFX_CHROMAFORMAT_YUV420, 1);
}
#[test]
fn test_qsv_rc_mode_values_match_spec() {
assert_eq!(MFX_RATECONTROL_CQP, 3);
assert_eq!(MFX_RATECONTROL_ICQ, 9); }
#[test]
fn test_qsv_mfx_info_fields_from_slots() {
use crate::qsv_ffi::MfxInfoMfx;
let slots = rate_slots_for_rc(QsvRateControl::Icq, 0, 0, 33);
let mfx = MfxInfoMfx {
reserved: [0; 7],
low_power: 0,
brc_param_multiplier: 0,
frame_info: MfxFrameInfo {
reserved: [0; 4],
channel_id: 0,
bit_depth_luma: 8,
bit_depth_chroma: 8,
shift: 0,
frame_id: [0; 4],
fourcc: MFX_FOURCC_NV12,
width: 1920,
height: 1080,
crop_x: 0,
crop_y: 0,
crop_w: 1920,
crop_h: 1080,
frame_rate_ext_n: 30000,
frame_rate_ext_d: 1000,
reserved3: 0,
aspect_ratio_w: 1,
aspect_ratio_h: 1,
pic_struct: MFX_PICSTRUCT_PROGRESSIVE,
chroma_format: MFX_CHROMAFORMAT_YUV420,
reserved2: 0,
},
codec_id: MFX_CODEC_AV1,
codec_profile: MFX_PROFILE_AV1_MAIN,
codec_level: 0,
num_thread: 0,
target_usage: 4,
gop_pic_size: 240,
gop_ref_dist: 1,
gop_opt_flag: 0,
idr_interval: 0,
rate_control_method: MFX_RATECONTROL_ICQ,
qpi_or_delay: slots.slot0_qpi_or_delay,
buffer_size_kb: 0,
qpp_or_kbps_or_icq: slots.slot1_qpp_or_kbps_or_icq,
qpb_or_maxkbps: slots.slot2_qpb_or_maxkbps,
num_slice: 0,
num_ref_frame: 1,
encoded_order: 0,
};
assert_eq!(mfx.qpp_or_kbps_or_icq, 33, "ICQQuality lives at slot 1");
assert_eq!(mfx.qpi_or_delay, 0, "slot 0 must be zero in ICQ mode");
assert_eq!(mfx.qpb_or_maxkbps, 0, "slot 2 must be zero in ICQ mode");
assert_eq!(mfx.rate_control_method, MFX_RATECONTROL_ICQ);
}
#[test]
fn test_qsv_zeroed_video_param_is_all_zero() {
let z = zeroed_video_param();
assert_eq!(z.mfx.codec_id, 0);
assert_eq!(z.mfx.codec_profile, 0);
assert_eq!(z.mfx.rate_control_method, 0);
assert_eq!(z.mfx.qpi_or_delay, 0);
assert_eq!(z.mfx.qpp_or_kbps_or_icq, 0);
assert_eq!(z.mfx.qpb_or_maxkbps, 0);
assert_eq!(z.mfx.frame_info.width, 0);
assert_eq!(z.mfx.frame_info.height, 0);
assert!(z.ext_param.is_null());
}
#[test]
fn test_qsv_align_up_power_of_two() {
assert_eq!(align_up(1u32, 16u32), 16);
assert_eq!(align_up(16u32, 16u32), 16);
assert_eq!(align_up(17u32, 16u32), 32);
assert_eq!(align_up(1920u32, 64u32), 1920);
assert_eq!(align_up(1921u32, 64u32), 1984);
}
#[test]
fn test_qsv_icq_flow_preserves_tuning_adapter_value() {
for (w, h) in [(640, 360), (1920, 1080), (3840, 2160)] {
for target in [
QualityTarget::Low,
QualityTarget::Standard,
QualityTarget::High,
] {
let tp = tuning::qsv_av1_params(target, SpeedTier::Standard, w, h);
assert_eq!(tp.rc_mode, QsvRateControl::Icq);
let slots = rate_slots_for_rc(tp.rc_mode, 0, 0, tp.icq_quality);
assert_eq!(
slots.slot1_qpp_or_kbps_or_icq, tp.icq_quality,
"ICQ quality value must reach slot 1 end-to-end — \
{target:?}/{w}x{h}: adapter={}, slot1={}",
tp.icq_quality, slots.slot1_qpp_or_kbps_or_icq
);
assert_eq!(slots.slot0_qpi_or_delay, 0);
assert_eq!(slots.slot2_qpb_or_maxkbps, 0);
}
}
}
#[test]
fn test_qsv_encode_ctrl_struct_size() {
assert_eq!(std::mem::size_of::<MfxEncodeCtrl>(), 56);
}
#[test]
fn test_qsv_fourcc_dispatch_10bit() {
assert_eq!(qsv_fourcc_for(PixelFormat::Yuv420p).unwrap(), MFX_FOURCC_NV12);
assert_eq!(qsv_fourcc_for(PixelFormat::Yuv420p10le).unwrap(), MFX_FOURCC_P010);
assert_eq!(MFX_FOURCC_P010, 0x30313050, "P010 FOURCC = 'P','0','1','0' LE");
}
#[test]
fn test_qsv_fourcc_dispatch_rejects_4_2_2_and_4_4_4() {
for unsupported in [
PixelFormat::Yuv422p,
PixelFormat::Yuv422p10le,
PixelFormat::Yuv444p,
PixelFormat::Yuv444p10le,
PixelFormat::Yuva444p10le,
PixelFormat::Nv12,
PixelFormat::Rgb24,
] {
assert!(
qsv_fourcc_for(unsupported).is_err(),
"{unsupported:?} must be rejected by QSV dispatch"
);
}
}
#[test]
fn test_qsv_bit_depth_triple_dispatch() {
let (luma8, chroma8, shift8) = qsv_bit_depth_triple(PixelFormat::Yuv420p);
assert_eq!((luma8, chroma8, shift8), (8, 8, 0), "NV12: 8-bit, no shift");
let (luma10, chroma10, shift10) = qsv_bit_depth_triple(PixelFormat::Yuv420p10le);
assert_eq!(
(luma10, chroma10, shift10),
(10, 10, 1),
"P010: 10-bit + Shift=1 (upper-10-bit convention)"
);
}
#[test]
fn test_qsv_transfer_to_h273_codes() {
assert_eq!(transfer_to_h273(TransferFn::Bt709), 1);
assert_eq!(transfer_to_h273(TransferFn::Bt470Bg), 4);
assert_eq!(transfer_to_h273(TransferFn::Linear), 8);
assert_eq!(transfer_to_h273(TransferFn::St2084), 16, "HDR10 PQ");
assert_eq!(transfer_to_h273(TransferFn::AribStdB67), 18, "HLG");
assert_eq!(transfer_to_h273(TransferFn::Unspecified), 1);
}
#[test]
fn test_qsv_frame_info_p010_layout() {
let (bdl, bdc, shift) = qsv_bit_depth_triple(PixelFormat::Yuv420p10le);
let fourcc = qsv_fourcc_for(PixelFormat::Yuv420p10le).unwrap();
let fi = MfxFrameInfo {
reserved: [0; 4],
channel_id: 0,
bit_depth_luma: bdl,
bit_depth_chroma: bdc,
shift,
frame_id: [0; 4],
fourcc,
width: 1920,
height: 1080,
crop_x: 0,
crop_y: 0,
crop_w: 1920,
crop_h: 1080,
frame_rate_ext_n: 30000,
frame_rate_ext_d: 1000,
reserved3: 0,
aspect_ratio_w: 1,
aspect_ratio_h: 1,
pic_struct: MFX_PICSTRUCT_PROGRESSIVE,
chroma_format: MFX_CHROMAFORMAT_YUV420,
reserved2: 0,
};
assert_eq!(fi.bit_depth_luma, 10);
assert_eq!(fi.bit_depth_chroma, 10);
assert_eq!(fi.shift, 1, "P010 must set Shift=1");
assert_eq!(fi.fourcc, MFX_FOURCC_P010);
assert_eq!(fi.chroma_format, MFX_CHROMAFORMAT_YUV420, "still 4:2:0 sub-sampling");
let bytes = unsafe {
std::slice::from_raw_parts(
&fi as *const MfxFrameInfo as *const u8,
std::mem::size_of::<MfxFrameInfo>(),
)
};
let fourcc_offset = std::mem::offset_of!(MfxFrameInfo, fourcc);
assert_eq!(
u32::from_le_bytes(bytes[fourcc_offset..fourcc_offset + 4].try_into().unwrap()),
MFX_FOURCC_P010,
"fourcc reads back as P010 from the expected struct offset"
);
}
#[test]
fn test_qsv_coding_option3_10bit_layout() {
let co3 = MfxExtCodingOption3 {
header: MfxExtBuffer {
buffer_id: MFX_EXTBUFF_CODING_OPTION3,
buffer_sz: std::mem::size_of::<MfxExtCodingOption3>() as u32,
},
_pad_to_158: [0; 150],
target_chroma_format_plus1: MFX_TARGET_CHROMAFORMAT_YUV420_PLUS1,
target_bit_depth_luma: 10,
target_bit_depth_chroma: 10,
_tail: [0; 348],
};
assert_eq!(co3.target_bit_depth_luma, 10, "AV1 BitDepth=10 in seq header");
assert_eq!(co3.target_bit_depth_chroma, 10, "AV1 BitDepth=10 in seq header");
assert_eq!(
co3.target_chroma_format_plus1, 2,
"MFX_CHROMAFORMAT_YUV420 (1) + 1 = 2"
);
assert_eq!(co3.header.buffer_id, MFX_EXTBUFF_CODING_OPTION3);
assert_eq!(memoffset_target_bit_depth_luma(), 160);
assert_eq!(MFX_EXTBUFF_CODING_OPTION3, 0x334f4443);
}
fn memoffset_target_bit_depth_luma() -> usize {
let base = std::mem::MaybeUninit::<MfxExtCodingOption3>::uninit();
let ptr = base.as_ptr();
unsafe {
(std::ptr::addr_of!((*ptr).target_bit_depth_luma) as usize) - (ptr as usize)
}
}
#[test]
fn test_qsv_signal_info_hdr10_layout() {
let cm = ColorMetadata {
transfer: TransferFn::St2084,
matrix_coefficients: 9, colour_primaries: 9, full_range: true,
mastering_display: None,
content_light_level: None,
};
let signal_info = MfxExtVideoSignalInfo {
header: MfxExtBuffer {
buffer_id: MFX_EXTBUFF_VIDEO_SIGNAL_INFO,
buffer_sz: std::mem::size_of::<MfxExtVideoSignalInfo>() as u32,
},
video_format: 5,
video_full_range: if cm.full_range { 1 } else { 0 },
colour_description_present: 1,
colour_primaries: cm.colour_primaries as u16,
transfer_characteristics: transfer_to_h273(cm.transfer),
matrix_coefficients: cm.matrix_coefficients as u16,
};
assert_eq!(signal_info.colour_description_present, 1, "must be set so codes emit");
assert_eq!(signal_info.colour_primaries, 9, "BT.2020");
assert_eq!(signal_info.transfer_characteristics, 16, "ST 2084 / PQ");
assert_eq!(signal_info.matrix_coefficients, 9, "BT.2020 NCL");
assert_eq!(signal_info.video_full_range, 1, "full range");
assert_eq!(signal_info.header.buffer_id, MFX_EXTBUFF_VIDEO_SIGNAL_INFO);
assert_eq!(
MFX_EXTBUFF_VIDEO_SIGNAL_INFO, 0x4e495356,
"ext buffer ID must match upstream MFX_MAKE_FOURCC('V','S','I','N')"
);
}
#[test]
fn test_qsv_8bit_sdr_layout_unchanged() {
let (bdl, bdc, shift) = qsv_bit_depth_triple(PixelFormat::Yuv420p);
assert_eq!((bdl, bdc, shift), (8, 8, 0), "8-bit dispatch unchanged");
let cm = ColorMetadata::default();
let signal_info = MfxExtVideoSignalInfo {
header: MfxExtBuffer {
buffer_id: MFX_EXTBUFF_VIDEO_SIGNAL_INFO,
buffer_sz: std::mem::size_of::<MfxExtVideoSignalInfo>() as u32,
},
video_format: 5,
video_full_range: if cm.full_range { 1 } else { 0 },
colour_description_present: 1,
colour_primaries: cm.colour_primaries as u16,
transfer_characteristics: transfer_to_h273(cm.transfer),
matrix_coefficients: cm.matrix_coefficients as u16,
};
assert_eq!(signal_info.colour_primaries, 1, "BT.709 default");
assert_eq!(signal_info.transfer_characteristics, 1, "BT.709 default");
assert_eq!(signal_info.matrix_coefficients, 1, "BT.709 default");
assert_eq!(signal_info.video_full_range, 0, "studio range default");
}