use hisui::{
decoder::{VideoDecoder, VideoDecoderOptions},
media::MediaStreamId,
metadata::SourceId,
processor::{MediaProcessor, MediaProcessorInput, MediaProcessorOutput},
reader_mp4::Mp4VideoReader,
video::VideoFrame,
};
use orfail::OrFail;
use shiguredo_mp4::boxes::{Avc1Box, AvccBox, SampleEntry};
use shiguredo_openh264::Openh264Library;
const DECODER_INPUT_STREAM_ID: MediaStreamId = MediaStreamId::new(0);
const DECODER_OUTPUT_STREAM_ID: MediaStreamId = MediaStreamId::new(1);
#[test]
fn h264_multi_resolutions() -> orfail::Result<()> {
let source_id0 = SourceId::new("archive-blue-640x480-h264");
let source_id1 = SourceId::new("archive-blue-640x480-h264");
let reader0 = Mp4VideoReader::new(
source_id0,
"testdata/archive-blue-640x480-h264.mp4",
Default::default(),
)
.or_fail()?;
let reader1 = Mp4VideoReader::new(
source_id1,
"testdata/archive-red-320x320-h264.mp4",
Default::default(),
)
.or_fail()?;
multi_resolutions_test(reader0, reader1).or_fail()?;
Ok(())
}
#[test]
#[cfg(target_os = "macos")]
fn h265_multi_resolutions() -> orfail::Result<()> {
let source_id0 = SourceId::new("archive-blue-640x480-h265");
let source_id1 = SourceId::new("archive-red-320x320-h265");
let reader0 = Mp4VideoReader::new(
source_id0,
"testdata/archive-blue-640x480-h265.mp4",
Default::default(),
)
.or_fail()?;
let reader1 = Mp4VideoReader::new(
source_id1,
"testdata/archive-red-320x320-h265.mp4",
Default::default(),
)
.or_fail()?;
multi_resolutions_test(reader0, reader1).or_fail()?;
Ok(())
}
#[test]
#[cfg(feature = "libvpx")]
fn vp9_multi_resolutions() -> orfail::Result<()> {
let source_id0 = SourceId::new("archive-blue-640x480-vp9");
let source_id1 = SourceId::new("archive-red-320x320-vp9");
let reader0 = Mp4VideoReader::new(
source_id0,
"testdata/archive-blue-640x480-vp9.mp4",
Default::default(),
)
.or_fail()?;
let reader1 = Mp4VideoReader::new(
source_id1,
"testdata/archive-red-320x320-vp9.mp4",
Default::default(),
)
.or_fail()?;
multi_resolutions_test(reader0, reader1).or_fail()?;
Ok(())
}
#[test]
fn av1_multi_resolutions() -> orfail::Result<()> {
let source_id0 = SourceId::new("archive-blue-640x480-av1");
let source_id1 = SourceId::new("archive-red-320x320-av1");
let reader0 = Mp4VideoReader::new(
source_id0,
"testdata/archive-blue-640x480-av1.mp4",
Default::default(),
)
.or_fail()?;
let reader1 = Mp4VideoReader::new(
source_id1,
"testdata/archive-red-320x320-av1.mp4",
Default::default(),
)
.or_fail()?;
multi_resolutions_test(reader0, reader1).or_fail()?;
Ok(())
}
fn multi_resolutions_test<I>(reader0: I, reader1: I) -> orfail::Result<()>
where
I: Iterator<Item = orfail::Result<VideoFrame>>,
{
let options = VideoDecoderOptions {
openh264_lib: if let Ok(path) = std::env::var("OPENH264_PATH") {
Some(Openh264Library::load(path).or_fail()?)
} else if cfg!(target_os = "macos") {
None
} else {
eprintln!("no available H.264 decoder");
return Ok(());
},
decode_params: Default::default(),
engines: None,
};
let mut decoder = VideoDecoder::new(DECODER_INPUT_STREAM_ID, DECODER_OUTPUT_STREAM_ID, options);
let mut output_frames = Vec::new();
let mut blue_count = 0;
let mut red_count = 0;
for input_frame in reader0 {
let input = prepend_h264_sps_pps(input_frame.or_fail()?);
decoder.process_input(input).or_fail()?;
blue_count += 1;
}
for input_frame in reader1 {
let input = prepend_h264_sps_pps(input_frame.or_fail()?);
decoder.process_input(input).or_fail()?;
red_count += 1;
}
decoder
.process_input(MediaProcessorInput::eos(DECODER_INPUT_STREAM_ID))
.or_fail()?;
while let MediaProcessorOutput::Processed { sample, .. } = decoder.process_output().or_fail()? {
let output_frame = sample.expect_video_frame().or_fail()?;
output_frames.push(output_frame);
}
for output_frame in output_frames {
if blue_count > 0 {
blue_count -= 1;
assert_eq!(output_frame.width, 640);
assert_eq!(output_frame.height, 480);
let (y_plane, u_plane, v_plane) = output_frame.as_yuv_planes().or_fail()?;
y_plane.iter().for_each(|&y| assert_eq!(y, 41));
u_plane.iter().for_each(|&y| assert_eq!(y, 240));
v_plane.iter().for_each(|&y| assert_eq!(y, 110));
} else {
red_count -= 1;
assert_eq!(output_frame.width, 320);
assert_eq!(output_frame.height, 320);
let (y_plane, u_plane, v_plane) = output_frame.as_yuv_planes().or_fail()?;
y_plane.iter().for_each(|&y| assert_eq!(y, 81));
u_plane.iter().for_each(|&u| assert_eq!(u, 90));
v_plane.iter().for_each(|&v| assert_eq!(v, 240));
}
}
assert_eq!(blue_count, 0);
assert_eq!(red_count, 0);
Ok(())
}
fn prepend_h264_sps_pps(mut frame: VideoFrame) -> MediaProcessorInput {
if let Some(SampleEntry::Avc1(Avc1Box {
avcc_box: AvccBox {
sps_list, pps_list, ..
},
..
})) = frame.sample_entry.clone()
{
let mut data = Vec::new();
for nalu in sps_list.into_iter().chain(pps_list.into_iter()) {
data.extend_from_slice(&(nalu.len() as u32).to_be_bytes());
data.extend_from_slice(&nalu);
}
data.extend_from_slice(&frame.data);
frame.data = data;
};
MediaProcessorInput::video_frame(DECODER_INPUT_STREAM_ID, frame)
}