#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::print_stderr,
reason = "test modules may unwrap / print"
)]
use crate::{VideoEncoder, VideoEncoderConfig, VideoInputPreference};
use mediaway_common::{Bytes, CodecKind, PixelFormat, Rational, VideoFrame, VideoFrameStorage};
use crate::vulkan::VulkanVideoEncoder;
use crate::vulkan::nal::{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,
};
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,
};
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,
};
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"
);
}