mod support;
use mediadecode::{
Received, Sent, Timebase,
color::{ColorPrimaries, ColorTransfer},
decoder::VideoStreamDecoder,
};
use mediadecode_ffmpeg::{
DecoderLimits, OwnedVideoFrame, PacketLimits, empty_owned_video_frame, extras::ContentLightLevel,
owned_video_packet_from_ffmpeg_in,
};
fn first_video_frame(path: &std::path::Path) -> OwnedVideoFrame {
let mut input = ffmpeg_next::format::input(path).expect("open the minted clip");
let stream = input
.streams()
.best(ffmpeg_next::media::Type::Video)
.expect("the fixture has a video stream");
let stream_index = stream.index();
let tb = stream.time_base();
let time_base = Timebase::new(
tb.numerator(),
core::num::NonZeroI32::new(tb.denominator().max(1)).expect("a non-zero denominator"),
);
let mut decoder = mediadecode_ffmpeg::FfmpegOwnedVideoStreamDecoder::open(
stream.parameters(),
time_base,
DecoderLimits::default(),
)
.expect("open the video decoder");
let mut frame: OwnedVideoFrame = empty_owned_video_frame();
for (index, (s, av_packet)) in input.packets().enumerate() {
if s.index() != stream_index {
continue;
}
let Some(packet) =
owned_video_packet_from_ffmpeg_in(&av_packet, time_base, PacketLimits::default())
.expect("a wrappable video payload")
else {
continue;
};
if decoder.send_packet(&packet).expect("send packet") != Sent::Accepted {
continue;
}
if let Received::Frame = decoder.receive_frame(&mut frame).expect("receive frame") {
return frame;
}
assert!(index < 64, "no frame within the first 64 packets");
}
panic!("the input ended before a frame was produced");
}
#[test]
fn hdr10_hevc_decodes_pq_bt2020_and_static_hdr_metadata() {
let Some(corpus) = support::Corpus::new() else {
return;
};
let path = corpus.hdr10_hevc();
let frame = first_video_frame(&path);
let color = frame.color();
assert_eq!(color.transfer(), ColorTransfer::SmpteSt2084Pq);
assert_eq!(color.primaries(), ColorPrimaries::Bt2020);
let mastering_display = frame
.extra()
.mastering_display()
.expect("libx265 wrote a mastering-display SEI");
assert_eq!(
mastering_display.display_primaries(),
[(34_000, 16_000), (13_250, 34_500), (7_500, 3_000)]
);
assert_eq!(mastering_display.white_point(), (15_635, 16_450));
assert_eq!(mastering_display.max_luminance(), (10_000_000, 10_000));
assert_eq!(mastering_display.min_luminance(), (1, 10_000));
assert_eq!(
frame.extra().content_light_level(),
Some(ContentLightLevel::new(1000, 400)),
);
}
#[test]
fn hlg_hevc_decodes_hlg_transfer_with_absent_static_hdr_metadata() {
let Some(corpus) = support::Corpus::new() else {
return;
};
let path = corpus.hlg_hevc();
let frame = first_video_frame(&path);
assert_eq!(frame.color().transfer(), ColorTransfer::AribStdB67Hlg);
assert_eq!(frame.color().primaries(), ColorPrimaries::Bt2020);
assert!(
frame.extra().mastering_display().is_none(),
"the HLG fixture carries no mastering-display SEI"
);
assert!(
frame.extra().content_light_level().is_none(),
"the HLG fixture carries no content-light-level SEI"
);
}