#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::print_stderr,
reason = "test modules may unwrap / print"
)]
use crate::{RateControlConfig, VideoEncoder, VideoEncoderConfig, VideoInputPreference};
use mediaway_common::{Bytes, CodecKind, PixelFormat, Rational, VideoFrame, VideoFrameStorage};
use crate::vulkan::VulkanVideoEncoder;
use crate::vulkan::nal::{NalHeader, scan_nal_headers, scan_nal_headers_hevc, scan_obu_headers};
const WIDTH: u32 = 176;
const HEIGHT: u32 = 144;
const WIDTH_HEVC: u32 = 256;
const HEIGHT_HEVC: u32 = 192;
const WIDTH_AV1: u32 = 256;
const HEIGHT_AV1: u32 = 192;
fn nv12_frame_sized(pts: i64, width: u32, height: u32) -> VideoFrame {
let len = (width as usize) * (height as usize) * 3 / 2;
VideoFrame {
pts,
duration: 1,
width,
height,
format: PixelFormat::Nv12,
storage: VideoFrameStorage::Cpu {
data: Bytes::from(vec![128u8; len]),
},
}
}
fn nv12_frame(pts: i64) -> VideoFrame {
nv12_frame_sized(pts, WIDTH, HEIGHT)
}
#[test]
#[allow(
clippy::similar_names,
reason = "has_sps/has_pps/has_idr_slice read clearer than de-aliased names"
)]
fn push_three_frames_or_skip() {
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: None,
gop_size: 1,
rate_control: None,
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open failed ({error:?}) — no encode-capable Vulkan device?"
);
return;
}
};
let mut packets = 0usize;
for i in 0..3i64 {
let frame = nv12_frame(i);
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame failed ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i}");
return;
}
Err(error) => {
eprintln!("skip: poll_packet failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(packet.is_keyframe, "packet {i} should be a key frame");
let headers = scan_nal_headers(&packet.payload);
let has_sps = headers.iter().any(|h| h.nal_unit_type == 7);
let has_pps = headers.iter().any(|h| h.nal_unit_type == 8);
let has_idr_slice = headers.iter().any(|h| h.nal_unit_type == 5);
assert!(
has_sps,
"packet {i} missing SPS (type 7); found {headers:?}"
);
assert!(
has_pps,
"packet {i} missing PPS (type 8); found {headers:?}"
);
assert!(
has_idr_slice,
"packet {i} missing IDR slice (type 5); found {headers:?}"
);
packets += 1;
}
enc.flush().expect("flush");
eprintln!("vulkan H.264 VideoEncoder ok: {packets} packets, all real SPS+PPS+IDR Annex-B NALs");
}
#[test]
#[allow(
clippy::similar_names,
reason = "has_vps/has_sps/has_pps/has_idr_slice read clearer than de-aliased names"
)]
fn push_three_hevc_frames_or_skip() {
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: None,
gop_size: 1,
rate_control: None,
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open (HEVC) failed ({error:?}) — no encode-capable Vulkan device?"
);
return;
}
};
let mut packets = 0usize;
for i in 0..3i64 {
let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame (HEVC) failed ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (HEVC)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (HEVC) failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(packet.is_keyframe, "packet {i} should be a key frame");
let headers = scan_nal_headers_hevc(&packet.payload);
let has_vps = headers.iter().any(|h| h.nal_unit_type == 32);
let has_sps = headers.iter().any(|h| h.nal_unit_type == 33);
let has_pps = headers.iter().any(|h| h.nal_unit_type == 34);
let has_idr_slice = headers
.iter()
.any(|h| h.nal_unit_type == 19 || h.nal_unit_type == 20);
assert!(
has_vps,
"packet {i} missing VPS (type 32); found {headers:?}"
);
assert!(
has_sps,
"packet {i} missing SPS (type 33); found {headers:?}"
);
assert!(
has_pps,
"packet {i} missing PPS (type 34); found {headers:?}"
);
assert!(
has_idr_slice,
"packet {i} missing IDR slice (type 19/20); found {headers:?}"
);
packets += 1;
}
enc.flush().expect("flush");
eprintln!(
"vulkan HEVC VideoEncoder ok: {packets} packets, all real VPS+SPS+PPS+IDR Annex-B NALs"
);
}
#[test]
fn push_three_av1_frames_or_skip() {
const OBU_SEQUENCE_HEADER: u8 = 1;
const OBU_FRAME_HEADER: u8 = 3;
const OBU_FRAME: u8 = 6;
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: None,
gop_size: 1,
rate_control: None,
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open (AV1) failed ({error:?}) — no encode-capable Vulkan device, or this driver lacks AV1 encode?"
);
return;
}
};
let mut packets = 0usize;
for i in 0..3i64 {
let frame = nv12_frame_sized(i, WIDTH_AV1, HEIGHT_AV1);
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame (AV1) failed ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (AV1)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (AV1) failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(packet.is_keyframe, "packet {i} should be a key frame");
let headers = scan_obu_headers(&packet.payload);
let has_sequence_header = headers.iter().any(|h| h.obu_type == OBU_SEQUENCE_HEADER);
let has_frame = headers
.iter()
.any(|h| h.obu_type == OBU_FRAME || h.obu_type == OBU_FRAME_HEADER);
assert!(
has_sequence_header,
"packet {i} missing OBU_SEQUENCE_HEADER (type 1); found {headers:?}"
);
if !has_frame {
eprintln!(
"skip: packet {i}'s own frame data is not a valid OBU (found {headers:?}) — \
known driver-maturity limitation on this hardware, see `adr/0001`'s AV1 addendum"
);
return;
}
packets += 1;
}
enc.flush().expect("flush");
eprintln!(
"vulkan AV1 VideoEncoder ok: {packets} packets, all real OBU sequence header + frame OBUs"
);
}
#[test]
fn push_seven_frames_gop_or_skip() {
const GOP_SIZE: u32 = 3;
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: None,
gop_size: GOP_SIZE,
rate_control: None,
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open (GOP) failed ({error:?}) — no encode-capable Vulkan device?"
);
return;
}
};
let expected_idr = [true, false, false, true, false, false, true];
let mut idr_count = 0usize;
let mut p_count = 0usize;
for (i, &want_idr) in expected_idr.iter().enumerate() {
let frame = nv12_frame(i64::try_from(i).unwrap_or(0));
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame (GOP) failed at {i} ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (GOP)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (GOP) failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
let headers = scan_nal_headers(&packet.payload);
let has_idr_slice = headers.iter().any(|h| h.nal_unit_type == 5);
let has_p_slice = headers.iter().any(|h| h.nal_unit_type == 1);
if want_idr != packet.is_keyframe {
eprintln!(
"skip: packet {i} keyframe flag ({}) doesn't match expected GOP cadence ({want_idr}) — \
this driver may report Capabilities::supports_p_frames == false, degrading to \
IDR-only per ADR-0002's capability-gating fallback",
packet.is_keyframe
);
return;
}
if want_idr {
assert!(
has_idr_slice,
"packet {i} expected IDR (NAL type 5); found {headers:?}"
);
idr_count += 1;
} else {
assert!(
has_p_slice,
"packet {i} expected a P-slice (NAL type 1); found {headers:?}"
);
assert!(
!has_idr_slice,
"packet {i} expected no IDR NAL; found {headers:?}"
);
p_count += 1;
}
}
enc.flush().expect("flush");
eprintln!(
"vulkan H.264 GOP VideoEncoder ok: {idr_count} IDR + {p_count} P packets, \
cadence matched gop_size={GOP_SIZE}"
);
}
#[test]
fn push_frames_gop_with_rate_control_or_skip() {
const GOP_SIZE: u32 = 4;
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: None,
gop_size: GOP_SIZE,
rate_control: Some(RateControlConfig {
target_bitrate_bps: 500_000,
vbv_buffer_size_bytes: Some(125_000),
}),
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open (GOP+CBR) failed ({error:?}) — no encode-capable Vulkan device?"
);
return;
}
};
let uncompressed_frame_bytes = (WIDTH as usize) * (HEIGHT as usize) * 3 / 2;
let mut total_bytes = 0usize;
let mut packets = 0usize;
for i in 0..6i64 {
let frame = nv12_frame(i);
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame (GOP+CBR) failed at {i} ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (GOP+CBR)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (GOP+CBR) failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(
packet.payload.len() < uncompressed_frame_bytes * 4,
"packet {i} suspiciously large: {} bytes (uncompressed frame is {uncompressed_frame_bytes})",
packet.payload.len()
);
total_bytes += packet.payload.len();
packets += 1;
}
match enc.set_bitrate(250_000) {
Ok(()) => eprintln!("vulkan H.264 CBR set_bitrate: accepted (real CBR session)"),
Err(error) => {
eprintln!("vulkan H.264 CBR set_bitrate: {error:?} (fixed-QP fallback, expected)");
}
}
for i in 6..9i64 {
let frame = nv12_frame(i);
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame after set_bitrate failed at {i} ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (post-set_bitrate)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (post-set_bitrate) failed ({error:?})");
return;
}
};
assert!(
!packet.payload.is_empty(),
"post-set_bitrate packet {i} payload is empty"
);
total_bytes += packet.payload.len();
packets += 1;
}
enc.flush().expect("flush");
eprintln!(
"vulkan H.264 GOP+CBR VideoEncoder ok: {packets} packets, {total_bytes} total bytes \
(target_bitrate_bps=500000, driver-dependent actual rate not independently verified)"
);
}
fn has_slice_nal(headers: &[NalHeader]) -> bool {
headers.iter().any(|h| !matches!(h.nal_unit_type, 32..=34))
}
#[test]
fn push_seven_hevc_frames_gop_or_skip() {
const GOP_SIZE: u32 = 3;
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: None,
gop_size: GOP_SIZE,
rate_control: None,
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open (HEVC GOP) failed ({error:?}) — no encode-capable Vulkan device?"
);
return;
}
};
let expected_idr = [true, false, false, true, false, false, true];
let mut idr_count = 0usize;
let mut p_count = 0usize;
for (i, &want_idr) in expected_idr.iter().enumerate() {
let frame = nv12_frame_sized(i64::try_from(i).unwrap_or(0), WIDTH_HEVC, HEIGHT_HEVC);
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame (HEVC GOP) failed at {i} ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (HEVC GOP)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (HEVC GOP) failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
let headers = scan_nal_headers_hevc(&packet.payload);
let has_idr_slice = headers
.iter()
.any(|h| h.nal_unit_type == 19 || h.nal_unit_type == 20);
if want_idr != packet.is_keyframe {
eprintln!(
"skip: packet {i} keyframe flag ({}) doesn't match expected GOP cadence ({want_idr}) — \
this driver may report Capabilities::supports_p_frames == false, degrading to \
IDR-only per ADR-0002's capability-gating fallback",
packet.is_keyframe
);
return;
}
if want_idr {
assert!(
has_idr_slice,
"packet {i} expected IDR (NAL type 19/20); found {headers:?}"
);
idr_count += 1;
} else {
assert!(
has_slice_nal(&headers),
"packet {i} expected a real slice NAL; found {headers:?}"
);
assert!(
!has_idr_slice,
"packet {i} expected no IDR NAL; found {headers:?}"
);
p_count += 1;
}
eprintln!("HEVC GOP packet {i}: is_keyframe={want_idr} NALs={headers:?}");
}
enc.flush().expect("flush");
eprintln!(
"vulkan HEVC GOP VideoEncoder ok: {idr_count} IDR + {p_count} P packets, \
cadence matched gop_size={GOP_SIZE}"
);
}
#[test]
fn push_hevc_frames_gop_with_rate_control_requested_or_skip() {
const GOP_SIZE: u32 = 4;
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: None,
gop_size: GOP_SIZE,
rate_control: Some(RateControlConfig {
target_bitrate_bps: 500_000,
vbv_buffer_size_bytes: Some(125_000),
}),
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open (HEVC GOP+rate_control requested) failed ({error:?}) \
— no encode-capable Vulkan device?"
);
return;
}
};
let uncompressed_frame_bytes = (WIDTH_HEVC as usize) * (HEIGHT_HEVC as usize) * 3 / 2;
let mut total_bytes = 0usize;
let mut packets = 0usize;
for i in 0..6i64 {
let frame = nv12_frame_sized(i, WIDTH_HEVC, HEIGHT_HEVC);
if let Err(error) = enc.push_frame(&frame) {
eprintln!(
"skip: push_frame (HEVC GOP+rate_control requested) failed at {i} ({error:?})"
);
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (HEVC GOP+rate_control requested)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (HEVC GOP+rate_control requested) failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
assert!(
packet.payload.len() < uncompressed_frame_bytes * 4,
"packet {i} suspiciously large: {} bytes (uncompressed frame is {uncompressed_frame_bytes})",
packet.payload.len()
);
total_bytes += packet.payload.len();
packets += 1;
}
enc.flush().expect("flush");
eprintln!(
"vulkan HEVC GOP+rate_control-requested VideoEncoder ok: {packets} packets, \
{total_bytes} total bytes (rate_control silently ignored per ADR-0002, fixed-QP path used)"
);
}
#[test]
fn push_seven_av1_frames_gop_or_skip() {
const OBU_SEQUENCE_HEADER: u8 = 1;
const OBU_FRAME_HEADER: u8 = 3;
const OBU_FRAME: u8 = 6;
const GOP_SIZE: u32 = 3;
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: None,
gop_size: GOP_SIZE,
rate_control: None,
intra_refresh_period: None,
};
let mut enc = match VulkanVideoEncoder::open(&cfg) {
Ok(enc) => enc,
Err(error) => {
eprintln!(
"skip: VulkanVideoEncoder::open (AV1 GOP) failed ({error:?}) — no encode-capable Vulkan device, or this driver lacks AV1 encode?"
);
return;
}
};
let expected_key = [true, false, false, true, false, false, true];
let mut key_count = 0usize;
let mut inter_count = 0usize;
for (i, &want_key) in expected_key.iter().enumerate() {
let frame = nv12_frame_sized(i64::try_from(i).unwrap_or(0), WIDTH_AV1, HEIGHT_AV1);
if let Err(error) = enc.push_frame(&frame) {
eprintln!("skip: push_frame (AV1 GOP) failed at {i} ({error:?})");
return;
}
let packet = match enc.poll_packet() {
Ok(Some(packet)) => packet,
Ok(None) => {
eprintln!("skip: no packet after push_frame {i} (AV1 GOP)");
return;
}
Err(error) => {
eprintln!("skip: poll_packet (AV1 GOP) failed ({error:?})");
return;
}
};
assert!(!packet.payload.is_empty(), "packet {i} payload is empty");
if want_key != packet.is_keyframe {
eprintln!(
"skip: packet {i} keyframe flag ({}) doesn't match expected GOP cadence ({want_key}) — \
this driver may report Capabilities::supports_p_frames == false, degrading to \
key-frame-only per ADR-0002's capability-gating fallback",
packet.is_keyframe
);
return;
}
if want_key {
key_count += 1;
} else {
inter_count += 1;
}
let headers = scan_obu_headers(&packet.payload);
let has_sequence_header = headers.iter().any(|h| h.obu_type == OBU_SEQUENCE_HEADER);
assert!(
has_sequence_header,
"packet {i} missing OBU_SEQUENCE_HEADER (type 1); found {headers:?}"
);
let has_frame = headers
.iter()
.any(|h| h.obu_type == OBU_FRAME || h.obu_type == OBU_FRAME_HEADER);
if !has_frame {
eprintln!(
"skip: packet {i}'s own frame data is not a valid OBU (found {headers:?}) — \
known driver-maturity limitation on this hardware, same root cause as \
push_three_av1_frames_or_skip, see `adr/0001`'s AV1 addendum and \
`adr/vulkan/0002`'s AV1 follow-up section"
);
return;
}
}
enc.flush().expect("flush");
eprintln!(
"vulkan AV1 GOP VideoEncoder ok: {key_count} key + {inter_count} inter packets, all real \
OBU sequence header + frame OBUs, cadence matched gop_size={GOP_SIZE} — surprising result, \
does not by itself mean the known driver bug is fixed in general"
);
}