use ffmpeg_next::ffi;
use super::{audio_frame_bytes, video_frame_bytes};
fn actual_video(format_raw: i32, width: i32, height: i32) -> usize {
unsafe {
let f = ffi::av_frame_alloc();
assert!(!f.is_null());
(*f).format = format_raw;
(*f).width = width;
(*f).height = height;
if ffi::av_frame_get_buffer(f, 0) != 0 {
ffi::av_frame_free(&mut (f as *mut _));
return 0;
}
let mut total = 0usize;
for i in 0..8 {
if !(*f).buf[i].is_null() {
total += (*(*f).buf[i]).size;
}
}
ffi::av_frame_free(&mut (f as *mut _));
total
}
}
fn actual_audio(format_raw: i32, nb_samples: i32, channels: i32) -> usize {
unsafe {
let f = ffi::av_frame_alloc();
assert!(!f.is_null());
(*f).format = format_raw;
(*f).nb_samples = nb_samples;
ffi::av_channel_layout_default(core::ptr::addr_of_mut!((*f).ch_layout), channels);
if ffi::av_frame_get_buffer(f, 0) != 0 {
ffi::av_frame_free(&mut (f as *mut _));
return 0;
}
let mut total = 0usize;
for i in 0..8 {
if !(*f).buf[i].is_null() {
total += (*(*f).buf[i]).size;
}
}
if !(*f).extended_buf.is_null() {
for i in 0..(*f).nb_extended_buf as isize {
let b = *(*f).extended_buf.offset(i);
if !b.is_null() {
total += (*b).size;
}
}
}
ffi::av_frame_free(&mut (f as *mut _));
total
}
}
const CHANNEL_SPREAD: [i32; 5] = [1, 2, 8, 32, 255];
const SAMPLE_SPREAD: [i32; 3] = [1, 1024, 65_535];
#[test]
fn the_audio_pricer_dominates_every_format_the_build_names() {
ffmpeg_next::init().expect("ffmpeg init");
let mut cells = 0usize;
let mut skipped = 0usize;
for format_raw in 0..64i32 {
let width = unsafe { crate::decoder::c_shims::av_get_bytes_per_sample(format_raw) };
if width <= 0 {
continue;
}
for channels in CHANNEL_SPREAD {
for nb_samples in SAMPLE_SPREAD {
let actual = actual_audio(format_raw, nb_samples, channels);
if actual == 0 {
skipped += 1;
continue;
}
let estimate = audio_frame_bytes(format_raw, nb_samples as usize, channels as usize)
.unwrap_or_else(|| {
panic!("format {format_raw} ch={channels} nb={nb_samples}: priced as unpriceable")
});
assert!(
estimate >= actual,
"format {format_raw} ch={channels} nb={nb_samples}: \
priced {estimate} against an actual allocation of {actual}",
);
cells += 1;
}
}
}
assert!(
cells >= 150,
"the matrix collapsed to {cells} cells ({skipped} skipped)",
);
assert!(
audio_frame_bytes(ffi::AVSampleFormat::AV_SAMPLE_FMT_NONE as i32, 1024, 2).is_none(),
"a format with no byte width must fail closed",
);
}
const VIDEO_SHAPES: [(i32, i32); 8] = [
(1, 1),
(1, 64),
(64, 1),
(16, 16),
(17, 17),
(33, 17),
(129, 129),
(640, 480),
];
#[test]
fn the_video_pricer_dominates_every_format_the_build_names() {
ffmpeg_next::init().expect("ffmpeg init");
let mut cells = 0usize;
let mut desc: *const ffi::AVPixFmtDescriptor = core::ptr::null();
loop {
desc = unsafe { ffi::av_pix_fmt_desc_next(desc) };
if desc.is_null() {
break;
}
let format_raw = unsafe { crate::decoder::c_shims::av_pix_fmt_desc_get_id(desc) };
for (w, h) in VIDEO_SHAPES {
let actual = actual_video(format_raw, w, h);
if actual == 0 {
continue;
}
let estimate = video_frame_bytes(format_raw, w, h)
.unwrap_or_else(|| panic!("format {format_raw} {w}x{h}: priced as unpriceable"));
assert!(
estimate >= actual,
"format {format_raw} {w}x{h}: priced {estimate} against an actual allocation of {actual}",
);
cells += 1;
}
}
assert!(cells >= 800, "the matrix collapsed to {cells} cells");
for (format_raw, w, h) in [
(ffi::AVPixelFormat::AV_PIX_FMT_GRAY8 as i32, 65_536, 1),
(ffi::AVPixelFormat::AV_PIX_FMT_GRAY8 as i32, 1, 65_536),
(ffi::AVPixelFormat::AV_PIX_FMT_YUV420P as i32, 1920, 1080),
(ffi::AVPixelFormat::AV_PIX_FMT_NV12 as i32, 1920, 1080),
] {
let actual = actual_video(format_raw, w, h);
let estimate = video_frame_bytes(format_raw, w, h).expect("priceable");
assert!(
estimate >= actual,
"format {format_raw} {w}x{h}: priced {estimate} against {actual}",
);
}
assert!(video_frame_bytes(ffi::AVPixelFormat::AV_PIX_FMT_NV12 as i32, 0, 16).is_none());
assert!(video_frame_bytes(-1, 16, 16).is_none());
}
#[test]
fn the_unpriceable_bound_dominates_the_priceable_estimate() {
ffmpeg_next::init().expect("ffmpeg init");
use super::video_frame_bytes_upper_bound;
for (w, h) in [
(1, 1),
(16, 16),
(65, 65),
(65_536, 1),
(1, 65_536),
(1920, 1080),
] {
let bound = video_frame_bytes_upper_bound(w, h).expect("a picture");
let bare = (w as usize) * (h as usize) * 16;
assert!(
bound >= bare,
"{w}x{h}: the bound {bound} is below the bare multiply {bare} it replaces",
);
let mut desc: *const ffi::AVPixFmtDescriptor = core::ptr::null();
loop {
desc = unsafe { ffi::av_pix_fmt_desc_next(desc) };
if desc.is_null() {
break;
}
let id = unsafe { crate::decoder::c_shims::av_pix_fmt_desc_get_id(desc) };
if let Some(priced) = video_frame_bytes(id, w, h) {
assert!(
bound >= priced,
"{w}x{h}: bound {bound} below format {id} priced at {priced}",
);
}
}
}
assert!(video_frame_bytes_upper_bound(0, 16).is_none());
}