use bytes::Bytes;
mod common;
use codec::encode::EncoderConfig;
use codec::frame::{ColorMetadata, ColorSpace, PixelFormat, TransferFn, VideoFrame};
use container::mux::Av1Mp4Muxer;
const W: u32 = 64;
const H: u32 = 64;
const FPS: f64 = 30.0;
fn make_yuv420p_frame(w: u32, h: u32, pts: u64) -> VideoFrame {
let wu = w as usize;
let hu = h as usize;
let y_size = wu * hu;
let uv_size = y_size / 4;
let mut buf = Vec::with_capacity(y_size + 2 * uv_size);
let t = pts as u8;
for r in 0..hu {
for c in 0..wu {
buf.push(((r + c) as u8).wrapping_add(t));
}
}
buf.extend(std::iter::repeat(128u8.wrapping_add(t / 3)).take(uv_size));
buf.extend(std::iter::repeat(128u8.wrapping_add(t / 5)).take(uv_size));
VideoFrame::new(
Bytes::from(buf),
w,
h,
PixelFormat::Yuv420p,
ColorSpace::Bt709,
pts,
)
}
fn make_yuv420p10le_hdr_frame(w: u32, h: u32, pts: u64) -> VideoFrame {
let wu = w as usize;
let hu = h as usize;
let y_samples = wu * hu;
let uv_samples = y_samples / 4;
let mut buf = Vec::with_capacity((y_samples + 2 * uv_samples) * 2);
for i in 0..y_samples {
let v: u16 = 64 + ((i + pts as usize) as u16 % 800);
buf.extend_from_slice(&v.to_le_bytes());
}
for _ in 0..(2 * uv_samples) {
buf.extend_from_slice(&512u16.to_le_bytes());
}
VideoFrame::new(
Bytes::from(buf),
w,
h,
PixelFormat::Yuv420p10le,
ColorSpace::Bt2020,
pts,
)
}
fn find_fourcc(data: &[u8], fourcc: &[u8; 4]) -> Option<usize> {
data.windows(4).position(|w| w == fourcc)
}
fn av1c_flags_byte(mp4: &[u8]) -> Option<u8> {
let p = find_fourcc(mp4, b"av1C")?;
let payload = p + 4; Some(mp4[payload + 2])
}
fn colr_nclx_transfer(mp4: &[u8]) -> Option<u16> {
let p = find_fourcc(mp4, b"colr")?;
let payload = p + 4; if &mp4[payload..payload + 4] != b"nclx" {
return None;
}
Some(u16::from_be_bytes([mp4[payload + 6], mp4[payload + 7]]))
}
#[test]
fn sanity_a_output_is_larger_than_skeleton() {
let config = EncoderConfig {
width: 256,
height: 256,
frame_rate: FPS,
quality: 60,
speed_preset: 10,
keyframe_interval: 4,
..EncoderConfig::default()
};
let Some(mut encoder) = common::try_av1_encoder(config) else {
return;
};
let mut muxer = Av1Mp4Muxer::new(256, 256, FPS).expect("muxer");
let mut packets = 0usize;
let mut mdat_bytes = 0usize;
for pts in 0..8 {
let f = make_yuv420p_frame(256, 256, pts);
encoder.send_frame(&f).expect("send_frame");
while let Some(p) = encoder.receive_packet().expect("receive") {
packets += 1;
mdat_bytes += p.data.len();
muxer.add_packet(p).expect("add_packet");
}
}
encoder.flush().expect("flush");
while let Some(p) = encoder.receive_packet().expect("receive after flush") {
packets += 1;
mdat_bytes += p.data.len();
muxer.add_packet(p).expect("add_packet");
}
let mp4 = muxer.finalize().expect("finalize");
assert!(packets > 0, "zero packets emitted by encoder");
assert!(
mdat_bytes > 256,
"encoder produced {} packets totaling only {} bytes — empty-mdat bug",
packets,
mdat_bytes
);
assert!(find_fourcc(&mp4, b"ftyp").is_some(), "no ftyp");
assert!(find_fourcc(&mp4, b"moov").is_some(), "no moov");
assert!(find_fourcc(&mp4, b"mdat").is_some(), "no mdat");
assert!(
mp4.len() as usize > mdat_bytes,
"mp4 {}B <= mdat payload {}B — mux failed to wrap the payload",
mp4.len(),
mdat_bytes
);
eprintln!(
"sanity_a: mp4 size = {} bytes, {} packets totaling {} mdat bytes",
mp4.len(),
packets,
mdat_bytes
);
}
#[test]
fn sanity_b_av1c_high_bitdepth_reflects_input_pixel_format() {
let cfg8 = EncoderConfig {
width: W,
height: H,
frame_rate: FPS,
quality: 200,
speed_preset: 10,
keyframe_interval: 4,
pixel_format: PixelFormat::Yuv420p,
..EncoderConfig::default()
};
let Some(mut e8) = common::try_av1_encoder(cfg8) else {
return;
};
let mut m8 = Av1Mp4Muxer::new(W, H, FPS).expect("muxer 8");
for pts in 0..4 {
e8.send_frame(&make_yuv420p_frame(W, H, pts))
.expect("send 8");
while let Some(p) = e8.receive_packet().expect("recv 8") {
m8.add_packet(p).expect("add 8");
}
}
e8.flush().expect("flush 8");
while let Some(p) = e8.receive_packet().expect("recv after flush 8") {
m8.add_packet(p).expect("add after flush 8");
}
let mp4_8 = m8.finalize().expect("finalize 8");
let flags8 = av1c_flags_byte(&mp4_8).expect("av1C flags byte for 8-bit");
let high_bitdepth_8 = (flags8 >> 6) & 1;
let twelve_bit_8 = (flags8 >> 5) & 1;
assert_eq!(
high_bitdepth_8, 0,
"8-bit input emitted high_bitdepth=1 (av1C flags byte = {:08b})",
flags8
);
assert_eq!(twelve_bit_8, 0, "8-bit input must have twelve_bit=0");
let meta_hdr = ColorMetadata {
transfer: TransferFn::St2084,
matrix_coefficients: 9,
colour_primaries: 9,
full_range: false,
mastering_display: None,
content_light_level: None,
};
let cfg10 = EncoderConfig {
width: W,
height: H,
frame_rate: FPS,
quality: 200,
speed_preset: 10,
keyframe_interval: 4,
pixel_format: PixelFormat::Yuv420p10le,
color_metadata: meta_hdr,
..EncoderConfig::default()
};
let Some(mut e10) = common::try_av1_encoder(cfg10) else {
return;
};
let mut m10 = Av1Mp4Muxer::new(W, H, FPS).expect("muxer 10");
m10.set_color_metadata(meta_hdr);
for pts in 0..4 {
e10.send_frame(&make_yuv420p10le_hdr_frame(W, H, pts))
.expect("send 10");
while let Some(p) = e10.receive_packet().expect("recv 10") {
m10.add_packet(p).expect("add 10");
}
}
e10.flush().expect("flush 10");
while let Some(p) = e10.receive_packet().expect("recv after flush 10") {
m10.add_packet(p).expect("add after flush 10");
}
let mp4_10 = m10.finalize().expect("finalize 10");
let flags10 = av1c_flags_byte(&mp4_10).expect("av1C flags byte for 10-bit");
let high_bitdepth_10 = (flags10 >> 6) & 1;
let twelve_bit_10 = (flags10 >> 5) & 1;
assert_eq!(
high_bitdepth_10, 1,
"10-bit input emitted high_bitdepth=0 (av1C flags byte = {:08b}) — \
encoder is ignoring pixel_format config and silently downsampling",
flags10
);
assert_eq!(
twelve_bit_10, 0,
"10-bit input must have twelve_bit=0 (would be set only for 12-bit)"
);
eprintln!(
"sanity_b: 8-bit flags=0b{:08b}, 10-bit flags=0b{:08b}",
flags8, flags10
);
}
#[test]
fn sanity_c_hdr_input_writes_pq_transfer_into_colr_nclx() {
let meta_hdr = ColorMetadata {
transfer: TransferFn::St2084, matrix_coefficients: 9, colour_primaries: 9, full_range: false,
mastering_display: None,
content_light_level: None,
};
let cfg = EncoderConfig {
width: W,
height: H,
frame_rate: FPS,
quality: 200,
speed_preset: 10,
keyframe_interval: 4,
pixel_format: PixelFormat::Yuv420p10le,
color_metadata: meta_hdr,
..EncoderConfig::default()
};
let Some(mut enc) = common::try_av1_encoder(cfg) else {
return;
};
let mut mux = Av1Mp4Muxer::new(W, H, FPS).expect("muxer");
mux.set_color_metadata(meta_hdr);
for pts in 0..4 {
enc.send_frame(&make_yuv420p10le_hdr_frame(W, H, pts))
.expect("send");
while let Some(p) = enc.receive_packet().expect("recv") {
mux.add_packet(p).expect("add");
}
}
enc.flush().expect("flush");
while let Some(p) = enc.receive_packet().expect("recv after flush") {
mux.add_packet(p).expect("add");
}
let mp4 = mux.finalize().expect("finalize");
let transfer = colr_nclx_transfer(&mp4).expect("colr nclx atom must be present for HDR output");
assert_eq!(
transfer, 16,
"HDR (PQ) input produced colr.transfer={} — expected 16 (ST2084 / PQ). \
ColorMetadata likely dropped on the floor between EncoderConfig and the muxer.",
transfer
);
eprintln!(
"sanity_c: colr nclx transfer = {} (PQ as expected)",
transfer
);
}