#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::print_stderr,
reason = "unit tests"
)]
use crate::{RateControlConfig, VideoEncoder, VideoEncoderConfig, VideoInputPreference};
use mediaway_common::{
Bytes, CodecKind, GpuDeviceHandle, NativeHandle, PixelFormat, Rational, VideoFrame,
VideoFrameStorage,
};
use windows::Win32::Graphics::Direct3D::D3D_FEATURE_LEVEL_11_0;
use windows::Win32::Graphics::Direct3D12::{
D3D12_MESSAGE, D3D12CreateDevice, D3D12GetDebugInterface, ID3D12Debug, ID3D12Device,
ID3D12InfoQueue,
};
use windows::Win32::Graphics::Dxgi::{CreateDXGIFactory1, IDXGIAdapter1, IDXGIFactory1};
use windows::core::Interface;
use super::D3d12VideoEncoder;
#[path = "d3d12_video_encode_tests_av1.rs"]
mod av1_tests;
const WIDTH: u32 = 176;
const HEIGHT: u32 = 144;
const WIDTH_HEVC: u32 = 256;
const HEIGHT_HEVC: u32 = 192;
const WIDTH_AV1: u32 = WIDTH_HEVC;
const HEIGHT_AV1: u32 = HEIGHT_HEVC;
fn nal_unit_types(payload: &[u8]) -> Vec<u8> {
let mut types = Vec::new();
let mut i = 0usize;
while i + 3 < payload.len() {
let is_start_code_3 = payload[i] == 0 && payload[i + 1] == 0 && payload[i + 2] == 1;
let is_start_code_4 = i + 4 < payload.len()
&& payload[i] == 0
&& payload[i + 1] == 0
&& payload[i + 2] == 0
&& payload[i + 3] == 1;
if is_start_code_4 {
types.push(payload[i + 4] & 0x1F);
i += 5;
} else if is_start_code_3 {
types.push(payload[i + 3] & 0x1F);
i += 4;
} else {
i += 1;
}
}
types
}
fn nal_unit_types_hevc(payload: &[u8]) -> Vec<u8> {
let mut types = Vec::new();
let mut i = 0usize;
while i + 3 < payload.len() {
let is_start_code_3 = payload[i] == 0 && payload[i + 1] == 0 && payload[i + 2] == 1;
let is_start_code_4 = i + 4 < payload.len()
&& payload[i] == 0
&& payload[i + 1] == 0
&& payload[i + 2] == 0
&& payload[i + 3] == 1;
if is_start_code_4 && i + 5 < payload.len() {
types.push((payload[i + 4] >> 1) & 0x3F);
i += 6;
} else if is_start_code_3 && i + 4 < payload.len() {
types.push((payload[i + 3] >> 1) & 0x3F);
i += 5;
} else {
i += 1;
}
}
types
}
fn nv12_frame(pts: i64) -> VideoFrame {
nv12_frame_sized(pts, WIDTH, HEIGHT)
}
fn nv12_frame_sized(pts: i64, width: u32, height: u32) -> VideoFrame {
let len = (width as usize) * (height as usize) + (width as usize) * (height as usize) / 2;
let data = vec![128u8; len];
VideoFrame {
pts,
duration: 1,
width,
height,
format: PixelFormat::Nv12,
storage: VideoFrameStorage::Cpu {
data: Bytes::from(data),
},
}
}
fn open_real_d3d12_device() -> Option<ID3D12Device> {
let mut debug: Option<ID3D12Debug> = None;
if unsafe { D3D12GetDebugInterface(&raw mut debug) }.is_ok() {
if let Some(debug) = debug {
unsafe { debug.EnableDebugLayer() };
}
}
let factory: IDXGIFactory1 = match unsafe { CreateDXGIFactory1() } {
Ok(f) => f,
Err(e) => {
eprintln!("skip: CreateDXGIFactory1 ({e:?})");
return None;
}
};
let adapter: IDXGIAdapter1 = match unsafe { factory.EnumAdapters1(0) } {
Ok(a) => a,
Err(e) => {
eprintln!("skip: EnumAdapters1 ({e:?})");
return None;
}
};
let mut device: Option<ID3D12Device> = None;
if unsafe { D3D12CreateDevice(&adapter, D3D_FEATURE_LEVEL_11_0, &raw mut device) }.is_err() {
eprintln!("skip: D3D12CreateDevice failed");
return None;
}
if device.is_none() {
eprintln!("skip: null D3D12 device");
}
device
}
fn dump_d3d12_info_queue(iq: Option<&ID3D12InfoQueue>) {
let Some(iq) = iq else { return };
let n = unsafe { iq.GetNumStoredMessages() };
for i in 0..n {
let mut len = 0usize;
if unsafe { iq.GetMessage(i, None, &raw mut len) }.is_err() || len == 0 {
continue;
}
let mut buf: Vec<u64> = vec![0; len.div_ceil(8)];
let msg_ptr = buf.as_mut_ptr().cast::<D3D12_MESSAGE>();
if unsafe { iq.GetMessage(i, Some(msg_ptr), &raw mut len) }.is_ok() {
let msg = unsafe { &*msg_ptr };
let desc = unsafe {
std::slice::from_raw_parts(
msg.pDescription,
msg.DescriptionByteLength.saturating_sub(1),
)
};
eprintln!("D3D12 InfoQueue[{i}]: {}", String::from_utf8_lossy(desc));
}
}
unsafe { iq.ClearStoredMessages() };
}
#[test]
fn d3d12_native_h264_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::H264,
width: WIDTH,
height: HEIGHT,
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 failed ({e:?}) — no D3D12 H.264 video-encode \
support on this device/driver?"
);
return;
}
};
let mut packets = 0usize;
for i in 0..3i64 {
let frame = nv12_frame(i);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame failed ({e:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(p)) => p,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i}");
return;
}
Err(e) => {
eprintln!("skip: poll_packet failed ({e:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(packet.is_keyframe, "packet {i} should be an IDR keyframe");
let types = nal_unit_types(&packet.payload);
assert!(
types.contains(&7),
"packet {i} missing SPS NAL (type 7); found types {types:?}"
);
assert!(
types.contains(&5),
"packet {i} missing IDR slice NAL (type 5); found types {types:?}"
);
packets += 1;
}
enc.flush().expect("flush");
eprintln!("d3d12 native h264 encode ok: {packets} packets, all with real SPS+IDR Annex-B NALs");
}
#[test]
fn d3d12_native_h264_gop_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::H264,
width: WIDTH,
height: HEIGHT,
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: 3,
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 (GOP) failed ({e:?}) — no D3D12 H.264 video-encode \
support on this device/driver?"
);
return;
}
};
let mut keyframe_flags = Vec::new();
let mut nal_type_cadence = Vec::new();
for i in 0..7i64 {
let frame = nv12_frame(i);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (GOP) failed ({e:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(p)) => p,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (GOP)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (GOP) failed ({e:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
let types = nal_unit_types(&packet.payload);
let is_idr_nal = types.contains(&5);
let is_p_nal = types.contains(&1);
assert!(
is_idr_nal || is_p_nal,
"packet {i} has neither an IDR (type 5) nor a P (type 1) slice NAL; found {types:?}"
);
assert_eq!(
packet.is_keyframe, is_idr_nal,
"packet {i}: Packet::is_keyframe ({}) disagrees with its own NAL type {types:?}",
packet.is_keyframe
);
keyframe_flags.push(packet.is_keyframe);
nal_type_cadence.push(if is_idr_nal { 'I' } else { 'P' });
}
enc.flush().expect("flush");
let cadence: String = nal_type_cadence.iter().collect();
let is_gop_cadence = cadence == "IPPIPPI";
let is_idr_only_fallback = keyframe_flags.iter().all(|&k| k);
assert!(
is_gop_cadence || is_idr_only_fallback,
"unexpected I/P cadence {cadence:?} — neither GOP mode's IPPIPPI nor an all-IDR fallback"
);
eprintln!("d3d12 native h264 GOP encode ok: cadence {cadence:?}");
}
#[test]
fn d3d12_native_h264_intra_refresh_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::H264,
width: WIDTH,
height: HEIGHT,
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: Some(4),
};
let mut enc = match D3d12VideoEncoder::open(&cfg) {
Ok(e) => e,
Err(e) => {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!(
"skip: D3d12VideoEncoder::open (intra-refresh) failed ({e:?}) — no D3D12 \
H.264 video-encode support on this device/driver?"
);
return;
}
};
let mut nal_type_cadence = Vec::new();
for i in 0..9i64 {
let frame = nv12_frame(i);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (intra-refresh) failed ({e:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(p)) => p,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (intra-refresh)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (intra-refresh) failed ({e:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
let types = nal_unit_types(&packet.payload);
let is_idr_nal = types.contains(&5);
let is_p_nal = types.contains(&1);
assert!(
is_idr_nal || is_p_nal,
"packet {i} has neither an IDR (type 5) nor a P (type 1) slice NAL; found {types:?}"
);
assert_eq!(
packet.is_keyframe, is_idr_nal,
"packet {i}: Packet::is_keyframe ({}) disagrees with its own NAL type {types:?}",
packet.is_keyframe
);
nal_type_cadence.push(if is_idr_nal { 'I' } else { 'P' });
}
enc.flush().expect("flush");
let cadence: String = nal_type_cadence.iter().collect();
let is_intra_refresh_cadence = cadence == "IPPPPPPPP";
let is_idr_only_fallback = cadence == "IIIIIIIII";
assert!(
is_intra_refresh_cadence || is_idr_only_fallback,
"unexpected I/P cadence {cadence:?} — neither intra-refresh's single-IDR-forever \
nor an all-IDR fallback"
);
eprintln!("d3d12 native h264 intra-refresh encode ok: cadence {cadence:?}");
}
#[test]
fn d3d12_native_h264_cbr_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::H264,
width: WIDTH,
height: HEIGHT,
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: Some(RateControlConfig {
target_bitrate_bps: 500_000,
vbv_buffer_size_bytes: 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 (H.264 CBR) failed ({e:?})");
return;
}
};
for i in 0..3i64 {
let frame = nv12_frame(i);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (H.264 CBR) failed ({e:?})");
return;
}
match enc.poll_packet() {
Ok(Some(p)) => assert!(!p.payload.is_empty(), "packet {i} payload is empty"),
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (H.264 CBR)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (H.264 CBR) failed ({e:?})");
return;
}
}
}
let cbr_selected = match enc.set_bitrate(250_000) {
Ok(()) => true,
Err(e) => {
eprintln!("d3d12 h264 set_bitrate: {e:?} (fixed-QP fallback, expected)");
false
}
};
for i in 3..6i64 {
let frame = nv12_frame(i);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame after set_bitrate failed ({e:?})");
return;
}
match enc.poll_packet() {
Ok(Some(p)) => assert!(
!p.payload.is_empty(),
"post-set_bitrate packet {i} payload is empty"
),
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (post-set_bitrate)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (post-set_bitrate) failed ({e:?})");
return;
}
}
}
enc.flush().expect("flush");
eprintln!(
"d3d12 native h264 CBR encode ok: cbr_selected={cbr_selected}, encoding kept working \
across set_bitrate"
);
}
#[test]
fn d3d12_native_hevc_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::Hevc,
width: WIDTH_HEVC,
height: HEIGHT_HEVC,
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 (HEVC) failed ({e:?}) — no D3D12 HEVC video-encode \
support on this device/driver?"
);
return;
}
};
let mut packets = 0usize;
for i in 0..3i64 {
let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (HEVC) failed ({e:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(p)) => p,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (HEVC)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (HEVC) failed ({e:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(packet.is_keyframe, "packet {i} should be an IDR keyframe");
let types = nal_unit_types_hevc(&packet.payload);
assert!(
types.contains(&32),
"packet {i} missing VPS NAL (type 32); found types {types:?}"
);
assert!(
types.contains(&33),
"packet {i} missing SPS NAL (type 33); found types {types:?}"
);
assert!(
types.contains(&34),
"packet {i} missing PPS NAL (type 34); found types {types:?}"
);
assert!(
types.contains(&19) || types.contains(&20),
"packet {i} missing IDR slice NAL (type 19/20); found types {types:?}"
);
packets += 1;
}
enc.flush().expect("flush");
eprintln!(
"d3d12 native hevc encode ok: {packets} packets, all with real VPS+SPS+PPS+IDR Annex-B NALs"
);
}
#[test]
fn d3d12_native_hevc_gop_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::Hevc,
width: WIDTH_HEVC,
height: HEIGHT_HEVC,
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: 3,
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 (HEVC GOP) failed ({e:?}) — no D3D12 HEVC \
video-encode support on this device/driver?"
);
return;
}
};
let mut keyframe_flags = Vec::new();
let mut nal_type_cadence = Vec::new();
for i in 0..7i64 {
let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (HEVC GOP) failed ({e:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(p)) => p,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (HEVC GOP)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (HEVC GOP) failed ({e:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
let types = nal_unit_types_hevc(&packet.payload);
let is_idr_nal = types.contains(&19) || types.contains(&20);
let is_p_nal = types.contains(&1);
assert!(
is_idr_nal || is_p_nal,
"packet {i} has neither an IDR (type 19/20) nor a P (type 1, TRAIL_R) slice NAL; \
found {types:?}"
);
assert_eq!(
packet.is_keyframe, is_idr_nal,
"packet {i}: Packet::is_keyframe ({}) disagrees with its own NAL type {types:?}",
packet.is_keyframe
);
keyframe_flags.push(packet.is_keyframe);
nal_type_cadence.push(if is_idr_nal { 'I' } else { 'P' });
}
enc.flush().expect("flush");
let cadence: String = nal_type_cadence.iter().collect();
let is_gop_cadence = cadence == "IPPIPPI";
let is_idr_only_fallback = keyframe_flags.iter().all(|&k| k);
assert!(
is_gop_cadence || is_idr_only_fallback,
"unexpected I/P cadence {cadence:?} — neither GOP mode's IPPIPPI nor an all-IDR fallback"
);
eprintln!("d3d12 native hevc GOP encode ok: cadence {cadence:?}");
}
#[test]
fn d3d12_native_hevc_intra_refresh_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::Hevc,
width: WIDTH_HEVC,
height: HEIGHT_HEVC,
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: Some(4),
};
let mut enc = match D3d12VideoEncoder::open(&cfg) {
Ok(e) => e,
Err(e) => {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!(
"skip: D3d12VideoEncoder::open (HEVC intra-refresh) failed ({e:?}) — no D3D12 \
HEVC video-encode support on this device/driver?"
);
return;
}
};
let mut nal_type_cadence = Vec::new();
for i in 0..9i64 {
let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (HEVC intra-refresh) failed ({e:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(p)) => p,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (HEVC intra-refresh)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (HEVC intra-refresh) failed ({e:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
let types = nal_unit_types_hevc(&packet.payload);
let is_idr_nal = types.contains(&19) || types.contains(&20);
let is_p_nal = types.contains(&1);
assert!(
is_idr_nal || is_p_nal,
"packet {i} has neither an IDR (type 19/20) nor a P (type 1, TRAIL_R) slice NAL; \
found {types:?}"
);
assert_eq!(
packet.is_keyframe, is_idr_nal,
"packet {i}: Packet::is_keyframe ({}) disagrees with its own NAL type {types:?}",
packet.is_keyframe
);
nal_type_cadence.push(if is_idr_nal { 'I' } else { 'P' });
}
enc.flush().expect("flush");
let cadence: String = nal_type_cadence.iter().collect();
let is_intra_refresh_cadence = cadence == "IPPPPPPPP";
let is_idr_only_fallback = cadence == "IIIIIIIII";
assert!(
is_intra_refresh_cadence || is_idr_only_fallback,
"unexpected I/P cadence {cadence:?} — neither intra-refresh's single-IDR-forever \
nor an all-IDR fallback"
);
eprintln!("d3d12 native hevc intra-refresh encode ok: cadence {cadence:?}");
}
#[test]
fn d3d12_native_hevc_cbr_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::Hevc,
width: WIDTH_HEVC,
height: HEIGHT_HEVC,
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: Some(RateControlConfig {
target_bitrate_bps: 500_000,
vbv_buffer_size_bytes: 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 (HEVC CBR) failed ({e:?})");
return;
}
};
for i in 0..3i64 {
let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame (HEVC CBR) failed ({e:?})");
return;
}
match enc.poll_packet() {
Ok(Some(p)) => assert!(!p.payload.is_empty(), "packet {i} payload is empty"),
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (HEVC CBR)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (HEVC CBR) failed ({e:?})");
return;
}
}
}
let cbr_selected = match enc.set_bitrate(250_000) {
Ok(()) => true,
Err(e) => {
eprintln!("d3d12 hevc set_bitrate: {e:?} (fixed-QP fallback, expected)");
false
}
};
for i in 3..6i64 {
let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
if let Err(e) = enc.push_frame(&frame) {
dump_d3d12_info_queue(info_queue.as_ref());
eprintln!("skip: push_frame after set_bitrate failed ({e:?})");
return;
}
match enc.poll_packet() {
Ok(Some(p)) => assert!(
!p.payload.is_empty(),
"post-set_bitrate packet {i} payload is empty"
),
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (post-set_bitrate)");
return;
}
Err(e) => {
eprintln!("skip: poll_packet (post-set_bitrate) failed ({e:?})");
return;
}
}
}
enc.flush().expect("flush");
eprintln!(
"d3d12 native hevc CBR encode ok: cbr_selected={cbr_selected}, encoding kept working \
across set_bitrate"
);
}