blob: 1a07afce64197b703258c940acd5b156585fe494 [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 log::*;
use std::io::Write;
use std::rc::Rc;
use stream_processor_encoder_factory::*;
use stream_processor_test::*;
use video_frame_stream::*;
use crate::h265::*;
/// Validates that the expected number of frames and key frames are received,
/// and that all received NAL's are valid.
pub struct H265NalValidator {
pub expected_frames: Option<usize>,
pub expected_key_frames: Option<usize>,
pub output_file: Option<&'static str>,
}
impl H265NalValidator {
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 H265NalValidator {
async fn validate(&self, output: &[Output]) -> Result<()> {
let packets: Vec<&OutputPacket> = output_packets(output).collect();
let mut file = self.output_file()?;
let mut stream = H265Stream::from(vec![]);
for packet in packets {
file.write_all(&packet.data)?;
stream.append(&mut packet.data.clone());
}
let nals: Vec<H265NalKind> = stream.nal_iter().map(|n| n.kind).collect();
info!("nals {:?}", nals);
let mut seen_frames = 0;
let mut seen_key_frames = 0;
for nal in stream.nal_iter() {
if nal.kind == H265NalKind::IRAP || nal.kind == H265NalKind::NonIRAP {
seen_frames += 1;
if nal.kind == H265NalKind::IRAP {
seen_key_frames += 1;
}
}
if nal.kind == H265NalKind::Unknown {
return Err(format_err!("unknown NAL received"));
}
}
if let Some(expected_frames) = self.expected_frames {
if seen_frames < expected_frames {
return Err(format_err!(
"Wrong number of frames received {} {}",
seen_frames,
expected_frames
));
}
}
if let Some(expected_key_frames) = self.expected_key_frames {
if seen_key_frames < expected_key_frames {
return Err(format_err!(
"Wrong number of key frames received {} {}",
seen_key_frames,
expected_key_frames
));
}
}
Ok(())
}
}
pub struct H265EncoderTestCase {
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_key_frames: Option<usize>,
pub output_file: Option<&'static str>,
}
impl H265EncoderTestCase {
pub async fn run(self) -> Result<()> {
let stream = self.create_test_stream()?;
let mut validators: Vec<Rc<dyn OutputValidator>> = vec![Rc::new(H265NalValidator {
expected_frames: Some(self.num_frames),
expected_key_frames: self.expected_key_frames,
output_file: self.output_file,
})];
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 create_test_stream(&self) -> Result<Rc<VideoFrameStream>> {
Ok(Rc::new(VideoFrameStream::create(
self.input_format,
self.num_frames,
self.settings.clone(),
/*frames_per_second=*/ 30,
/*timebase=*/ Some(zx::Duration::from_seconds(1).into_nanos() as u64),
/*mime_type=*/ "video/h265",
)?))
}
}