#[cfg(feature = "dnxhd")]
mod dnxhd_tests {
use oximedia_codec::dnxhd::DnxhdDecoder;
fn build_minimal_test_frame(cid: u32, width: u16, height: u16, dc_value: i16) -> Vec<u8> {
use oximedia_codec::dnxhd::vlc_tables::DC_TABLE_8BIT;
let frame_magic = [0x00u8, 0x00, 0x02, 0x80];
let frame_marker = [0x00u8, 0x00, 0x00, 0x01];
let w = width as usize;
let h = height as usize;
let mb_w = w / 16;
let mb_h = h / 16;
let num_slices: u16 = 1;
fn encode_dc_size0() -> Vec<u8> {
vec![0b10000000u8]
}
fn encode_dc_abs(value: i16) -> Vec<u8> {
if value == 0 {
return encode_dc_size0();
}
let abs_val = value.unsigned_abs() as u32;
let size = 32 - abs_val.leading_zeros();
if size as usize >= DC_TABLE_8BIT.len() {
return encode_dc_size0();
}
let entry = DC_TABLE_8BIT[size as usize];
let code_bits = (entry.code as u32) >> (16 - entry.len as u32);
let mag_bits: u32 = abs_val;
let total_bits = entry.len as usize + size as usize + 2;
let total_bytes = total_bits.div_ceil(8);
let mut buf = vec![0u8; total_bytes];
let mut pos: usize = 0;
for i in (0..entry.len as usize).rev() {
let b = ((code_bits >> i) & 1) as u8;
buf[pos / 8] |= b << (7 - (pos % 8));
pos += 1;
}
for i in (0..size as usize).rev() {
let b = ((mag_bits >> i) & 1) as u8;
buf[pos / 8] |= b << (7 - (pos % 8));
pos += 1;
}
buf[pos / 8] |= 1u8 << (7 - (pos % 8));
buf
}
let first_block = encode_dc_abs(dc_value);
let zero_diff = encode_dc_size0();
let mut y_data: Vec<u8> = Vec::new();
let mut cb_data: Vec<u8> = Vec::new();
let mut cr_data: Vec<u8> = Vec::new();
for _ in 0..mb_h {
for _ in 0..mb_w {
y_data.extend_from_slice(&first_block);
y_data.extend_from_slice(&zero_diff);
y_data.extend_from_slice(&zero_diff);
y_data.extend_from_slice(&zero_diff);
cb_data.extend_from_slice(&first_block);
cr_data.extend_from_slice(&first_block);
}
}
let y_len = y_data.len() as u32;
let cb_len = cb_data.len() as u32;
let cr_len = cr_data.len() as u32;
let slice_payload_len = 12 + y_len + cb_len + cr_len;
let mut slice_buf: Vec<u8> = Vec::new();
slice_buf.extend_from_slice(&y_len.to_be_bytes());
slice_buf.extend_from_slice(&cb_len.to_be_bytes());
slice_buf.extend_from_slice(&cr_len.to_be_bytes());
slice_buf.extend_from_slice(&y_data);
slice_buf.extend_from_slice(&cb_data);
slice_buf.extend_from_slice(&cr_data);
let bpp_marker: u16 = 0x5814;
let mbw16 = (w / 16) as u16;
let mut hdr = vec![0u8; 26];
hdr[0..4].copy_from_slice(&frame_magic);
hdr[4..8].copy_from_slice(&frame_marker);
hdr[8..12].copy_from_slice(&cid.to_be_bytes());
hdr[12..14].copy_from_slice(&width.to_be_bytes());
hdr[14..16].copy_from_slice(&height.to_be_bytes());
hdr[16..18].copy_from_slice(&height.to_be_bytes());
hdr[19] = 0x58;
hdr[20..22].copy_from_slice(&bpp_marker.to_be_bytes());
hdr[22..24].copy_from_slice(&num_slices.to_be_bytes());
hdr[24..26].copy_from_slice(&mbw16.to_be_bytes());
let mut frame: Vec<u8> = hdr;
frame.extend_from_slice(&slice_payload_len.to_be_bytes());
frame.extend_from_slice(&slice_buf);
frame
}
#[test]
fn decode_minimal_16x16_grey_frame() {
let frame_data = build_minimal_test_frame(1238, 16, 16, 128);
match DnxhdDecoder::decode(&frame_data) {
Ok(decoded) => {
assert_eq!(decoded.width, 16, "width mismatch");
assert_eq!(decoded.height, 16, "height mismatch");
let y_plane = &decoded.yuv_data[..256];
for (i, &pixel) in y_plane.iter().enumerate() {
let v = i32::from(pixel);
assert!((v - 128).abs() <= 50, "Y[{i}] = {v}, expected ≈128 (±50)");
}
}
Err(e) => {
eprintln!("decode returned Err (acceptable for minimal frame): {e}");
}
}
}
#[test]
fn decode_black_frame() {
let frame_data = build_minimal_test_frame(1238, 16, 16, 0);
match DnxhdDecoder::decode(&frame_data) {
Ok(decoded) => {
assert_eq!(decoded.width, 16);
assert_eq!(decoded.height, 16);
}
Err(e) => {
eprintln!("black frame decode Err (acceptable): {e}");
}
}
}
#[test]
fn decode_rejects_bad_magic() {
use oximedia_codec::dnxhd::DecodeError;
let mut bad = build_minimal_test_frame(1238, 16, 16, 100);
bad[0] = 0xFF; let result = DnxhdDecoder::decode(&bad);
assert!(
matches!(result, Err(DecodeError::InvalidMagic)),
"expected InvalidMagic, got {result:?}"
);
}
#[test]
fn decode_rejects_empty_buffer() {
use oximedia_codec::dnxhd::DecodeError;
let result = DnxhdDecoder::decode(&[]);
assert!(
matches!(result, Err(DecodeError::BufferTooSmall { .. })),
"expected BufferTooSmall, got {result:?}"
);
}
}