use super::*;
fn obu_types(payload: &[u8]) -> Vec<u8> {
let mut types = Vec::new();
let mut i = 0usize;
while i < payload.len() {
let header = payload[i];
let obu_type = (header >> 3) & 0x0F;
let has_size_field = (header >> 1) & 1 == 1;
let has_extension = (header >> 2) & 1 == 1;
i += 1;
if has_extension {
i += 1;
}
if !has_size_field || i >= payload.len() {
break;
}
let mut size = 0u64;
let mut shift = 0u32;
loop {
if i >= payload.len() {
return types;
}
let b = payload[i];
i += 1;
size |= u64::from(b & 0x7f) << shift;
shift += 7;
if b & 0x80 == 0 {
break;
}
}
types.push(obu_type);
let Ok(size) = usize::try_from(size) else {
break;
};
i += size;
}
types
}
#[test]
fn d3d12_native_av1_encode_or_skip() {
let Some(device) = open_real_d3d12_device() else {
return;
};
let Some(handle) = NativeHandle::new(Interface::as_raw(&device) as usize) else {
eprintln!("skip: null D3D12 device pointer");
return;
};
let info_queue: Option<ID3D12InfoQueue> = device.cast().ok();
let cfg = VideoEncoderConfig {
codec: CodecKind::Av1,
width: WIDTH_AV1,
height: HEIGHT_AV1,
time_base: Rational::new(1, 30),
bitrate_bps: 500_000,
pixel_format: PixelFormat::Nv12,
input: VideoInputPreference::CpuUploadOk,
gpu_device: Some(GpuDeviceHandle::DirectX12(handle)),
gop_size: 1,
rate_control: None,
intra_refresh_period: None,
};
let mut enc = match D3d12VideoEncoder::open(&cfg) {
Ok(e) => e,
Err(e) => {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!(
"skip: D3d12VideoEncoder::open (AV1) failed ({e:?}) — no D3D12 AV1 video-encode \
support on this device/driver?"
);
return;
}
};
let mut packets = 0usize;
for i in 0..3i64 {
let frame = nv12_frame_sized(i, WIDTH_AV1, HEIGHT_AV1);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (AV1) failed ({e:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(p)) => p,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (AV1)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (AV1) failed ({e:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(packet.is_keyframe, "packet {i} should be a key frame");
let types = obu_types(&packet.payload);
assert!(
types.contains(&2),
"packet {i} missing temporal delimiter OBU (type 2); found types {types:?}"
);
assert!(
types.contains(&1),
"packet {i} missing sequence header OBU (type 1); found types {types:?}"
);
assert!(
types.contains(&6),
"packet {i} missing frame OBU (type 6); found types {types:?}"
);
packets += 1;
}
enc.flush().expect("flush");
eprintln!("d3d12 native av1 encode ok: {packets} packets, all with real TD+SeqHdr+Frame OBUs");
}
#[test]
fn d3d12_av1_encode_codec_probe() {
use windows::Win32::Media::MediaFoundation::{
D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC, D3D12_FEATURE_VIDEO_ENCODER_CODEC,
D3D12_VIDEO_ENCODER_CODEC_AV1, ID3D12VideoDevice3,
};
use windows::core::BOOL;
let Some(device) = open_real_d3d12_device() else {
return;
};
let Ok(video_device) = device.cast::<ID3D12VideoDevice3>() else {
eprintln!("skip: no ID3D12VideoDevice3 on this device");
return;
};
let mut support = D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC {
NodeIndex: 0,
Codec: D3D12_VIDEO_ENCODER_CODEC_AV1,
IsSupported: BOOL::default(),
};
let hr = unsafe {
video_device.CheckFeatureSupport(
D3D12_FEATURE_VIDEO_ENCODER_CODEC,
std::ptr::from_mut(&mut support).cast(),
u32::try_from(std::mem::size_of::<D3D12_FEATURE_DATA_VIDEO_ENCODER_CODEC>())
.unwrap_or(u32::MAX),
)
};
match hr {
Ok(()) => eprintln!(
"d3d12 av1 encode codec probe: IsSupported={}",
support.IsSupported.as_bool()
),
Err(e) => eprintln!("d3d12 av1 encode codec probe: CheckFeatureSupport failed ({e:?})"),
}
}