blob: 860a31b9c155097e37c83bd2447fad4994cadd13 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use anyhow::format_err;
use async_trait::async_trait;
use fidl_fuchsia_media::*;
use fidl_fuchsia_sysmem as sysmem;
use fuchsia_zircon as zx;
use h264_stream::*;
use std::io::Write;
use std::rc::Rc;
use stream_processor_decoder_factory::*;
use stream_processor_encoder_factory::*;
use stream_processor_test::*;
use video_frame_stream::*;
pub struct H264NalValidator {
pub expected_nals: Option<Vec<H264NalKind>>,
pub output_file: Option<&'static str>,
}
impl H264NalValidator {
fn output_file(&self) -> Result<impl Write> {
Ok(if let Some(file) = self.output_file {
Box::new(std::fs::File::create(file)?) as Box<dyn Write>
} else {
Box::new(std::io::sink()) as Box<dyn Write>
})
}
}
#[async_trait(?Send)]
impl OutputValidator for H264NalValidator {
async fn validate(&self, output: &[Output]) -> Result<()> {
let packets: Vec<&OutputPacket> = output_packets(output).collect();
let mut file = self.output_file()?;
let mut stream = H264Stream::from(vec![]);
for packet in packets {
file.write_all(&packet.data)?;
stream.append(&mut packet.data.clone());
}
if let None = self.expected_nals {
return Ok(());
}
let expected = self.expected_nals.as_ref().unwrap();
let mut current = 0;
for nal in stream.nal_iter() {
if current >= expected.len() {
return Err(format_err!("Too many NAL received"));
}
if nal.kind != expected[current] {
return Err(format_err!(
"Expected NAL kind {:?} got {:?} at index {}",
expected[current],
nal.kind,
current
));
}
current += 1;
}
if current != expected.len() {
return Err(format_err!("Too few NAL received"));
}
Ok(())
}
}
pub struct H264DecoderValidator {
num_frames: usize,
input_stream: Rc<VideoFrameStream>,
normalized_sad_threshold: f64,
}
#[async_trait(?Send)]
impl OutputValidator for H264DecoderValidator {
async fn validate(&self, output: &[Output]) -> Result<()> {
let decoder_factory = Rc::new(DecoderFactory);
let packets: Vec<&OutputPacket> = output_packets(output).collect();
let mut stream = H264Stream::from(Vec::new());
for p in packets {
stream.append(&mut p.data.clone());
}
let stream = Rc::new(stream);
let decoder = decoder_factory
.connect_to_stream_processor(
stream.as_ref(),
/* format_details_version_ordinal */ 1,
)
.await?;
let mut stream_runner = StreamRunner::new(decoder);
let stream_options = None;
let decoded_output =
stream_runner.run_stream(stream, stream_options.unwrap_or_default()).await?;
let decoded_frames: Vec<&OutputPacket> = output_packets(&decoded_output).collect();
if decoded_frames.len() != self.num_frames {
return Err(format_err!(
"Wrong number of frames received {} {}",
decoded_frames.len(),
self.num_frames
));
}
let input_frames = self.input_stream.stream();
for (frame_number, (input_frame, output_frame)) in
input_frames.zip(decoded_frames).enumerate()
{
let sad = Self::normalized_sad(&input_frame.data, &output_frame.data);
if sad > self.normalized_sad_threshold {
return Err(format_err!(
"SAD threshold {} exceeded: {}, frame {}",
self.normalized_sad_threshold,
sad,
frame_number
));
}
}
Ok(())
}
}
impl H264DecoderValidator {
fn normalized_sad(input: &[u8], output: &[u8]) -> f64 {
let mut sad = 0.0;
for (i_p, o_p) in input.iter().zip(output.iter()) {
sad += (*i_p as f64 - *o_p as f64).abs();
}
sad / input.len() as f64
}
}
pub struct H264EncoderTestCase {
pub num_frames: usize,
pub input_format: sysmem::ImageFormat2,
// This is a function because FIDL unions are not Copy or Clone.
pub settings: Rc<dyn Fn() -> EncoderSettings>,
pub expected_nals: Option<Vec<H264NalKind>>,
// If set, computes the Sum of Absolute Differences of each input to output frame and fails
// validation of the normalized value is greater than this threshold.
pub normalized_sad_threshold: Option<f64>,
pub decode_output: bool,
pub output_file: Option<&'static str>,
}
impl H264EncoderTestCase {
pub async fn run(self) -> Result<()> {
// This threshold is not meant to be hit.
const MAX_NORMALIZED_SAD: f64 = 0xffff as f64;
let stream = self.create_test_stream()?;
let mut validators: Vec<Rc<dyn OutputValidator>> = vec![
Rc::new(H264NalValidator {
expected_nals: self.expected_nals.clone(),
output_file: self.output_file,
}),
Rc::new(TimestampValidator { generator: stream.timestamp_generator() }),
];
if self.decode_output {
validators.push(Rc::new(H264DecoderValidator {
num_frames: self.num_frames,
input_stream: stream.clone(),
normalized_sad_threshold: self
.normalized_sad_threshold
.unwrap_or(MAX_NORMALIZED_SAD),
}));
}
validators.push(Rc::new(TerminatesWithValidator {
expected_terminal_output: Output::Eos { stream_lifetime_ordinal: 1 },
}));
let case =
TestCase { name: "Terminates with EOS test", stream, validators, stream_options: None };
let spec = TestSpec {
cases: vec![case],
relation: CaseRelation::Serial,
stream_processor_factory: Rc::new(EncoderFactory),
};
spec.run().await
}
fn get_frame_rate(&self) -> usize {
const DEFAULT_FRAMERATE: usize = 30;
match (self.settings)() {
EncoderSettings::H264(H264EncoderSettings { frame_rate: Some(frame_rate), .. }) => {
frame_rate as usize
}
_ => DEFAULT_FRAMERATE,
}
}
fn get_timebase(&self) -> u64 {
zx::Duration::from_seconds(1).into_nanos() as u64
}
fn create_test_stream(&self) -> Result<Rc<VideoFrameStream>> {
Ok(Rc::new(VideoFrameStream::create(
self.input_format,
self.num_frames,
self.settings.clone(),
self.get_frame_rate(),
Some(self.get_timebase()),
/*mime_type=*/ "video/h264",
)?))
}
}