blob: 419c7448ba027e7ac0dfacc8679631da5e222f1d [file] [log] [blame]
// Copyright 2022 The ChromiumOS Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#[cfg(test)]
mod dummy;
#[cfg(feature = "vaapi")]
mod vaapi;
use std::os::fd::AsFd;
use std::os::fd::BorrowedFd;
use log::debug;
use crate::codec::vp9::parser::BitDepth;
use crate::codec::vp9::parser::Frame;
use crate::codec::vp9::parser::Header;
use crate::codec::vp9::parser::Parser;
use crate::codec::vp9::parser::Profile;
use crate::codec::vp9::parser::Segmentation;
use crate::codec::vp9::parser::MAX_SEGMENTS;
use crate::codec::vp9::parser::NUM_REF_FRAMES;
use crate::decoder::stateless::DecodeError;
use crate::decoder::stateless::DecodingState;
use crate::decoder::stateless::NewPictureError;
use crate::decoder::stateless::NewPictureResult;
use crate::decoder::stateless::PoolLayer;
use crate::decoder::stateless::StatelessBackendResult;
use crate::decoder::stateless::StatelessCodec;
use crate::decoder::stateless::StatelessDecoder;
use crate::decoder::stateless::StatelessDecoderBackend;
use crate::decoder::stateless::StatelessDecoderBackendPicture;
use crate::decoder::stateless::StatelessVideoDecoder;
use crate::decoder::stateless::TryFormat;
use crate::decoder::BlockingMode;
use crate::decoder::DecodedHandle;
use crate::decoder::DecoderEvent;
use crate::decoder::StreamInfo;
use crate::Resolution;
/// Stateless backend methods specific to VP9.
pub trait StatelessVp9DecoderBackend:
StatelessDecoderBackend + StatelessDecoderBackendPicture<Vp9>
{
/// Called when new stream parameters are found.
fn new_sequence(&mut self, header: &Header) -> StatelessBackendResult<()>;
/// Allocate all resources required to process a new picture.
fn new_picture(&mut self, timestamp: u64) -> NewPictureResult<Self::Picture>;
/// Called when the decoder wants the backend to finish the decoding
/// operations for `picture`.
///
/// This call will assign the ownership of the BackendHandle to the Picture
/// and then assign the ownership of the Picture to the Handle.
fn submit_picture(
&mut self,
picture: Self::Picture,
hdr: &Header,
reference_frames: &[Option<Self::Handle>; NUM_REF_FRAMES],
bitstream: &[u8],
segmentation: &[Segmentation; MAX_SEGMENTS],
) -> StatelessBackendResult<Self::Handle>;
}
pub struct Vp9DecoderState<H: DecodedHandle> {
/// VP9 bitstream parser.
parser: Parser,
/// The reference frames in use.
reference_frames: [Option<H>; NUM_REF_FRAMES],
/// Per-segment data.
segmentation: [Segmentation; MAX_SEGMENTS],
/// Keeps track of the last values seen for negotiation purposes.
negotiation_info: NegotiationInfo,
}
impl<H> Default for Vp9DecoderState<H>
where
H: DecodedHandle,
{
fn default() -> Self {
Self {
parser: Default::default(),
reference_frames: Default::default(),
segmentation: Default::default(),
negotiation_info: Default::default(),
}
}
}
/// Keeps track of the last values seen for negotiation purposes.
#[derive(Clone, Debug, Default, PartialEq, Eq)]
struct NegotiationInfo {
/// The current coded resolution
coded_resolution: Resolution,
/// Cached value for bit depth
bit_depth: BitDepth,
/// Cached value for profile
profile: Profile,
}
impl From<&Header> for NegotiationInfo {
fn from(hdr: &Header) -> Self {
NegotiationInfo {
coded_resolution: Resolution {
width: hdr.width,
height: hdr.height,
},
bit_depth: hdr.bit_depth,
profile: hdr.profile,
}
}
}
/// [`StatelessCodec`] structure to use in order to create a VP9 stateless decoder.
///
/// # Accepted input
///
/// the VP9 specification requires the last byte of the chunk to contain the superframe marker.
/// Thus, a decoder using this codec processes exactly one encoded chunk per call to
/// [`StatelessDecoder::decode`], and always returns the size of the passed input if successful.
pub struct Vp9;
impl StatelessCodec for Vp9 {
type FormatInfo = Header;
type DecoderState<H: DecodedHandle, P> = Vp9DecoderState<H>;
}
impl<B> StatelessDecoder<Vp9, B>
where
B: StatelessVp9DecoderBackend,
B::Handle: Clone,
{
fn update_references(
reference_frames: &mut [Option<B::Handle>; NUM_REF_FRAMES],
picture: &B::Handle,
mut refresh_frame_flags: u8,
) -> anyhow::Result<()> {
#[allow(clippy::needless_range_loop)]
for i in 0..NUM_REF_FRAMES {
if (refresh_frame_flags & 1) == 1 {
debug!("Replacing reference frame {}", i);
reference_frames[i] = Some(picture.clone());
}
refresh_frame_flags >>= 1;
}
Ok(())
}
/// Handle a frame which `show_existing_frame` flag is `true`.
fn handle_show_existing_frame(&mut self, frame_to_show_map_idx: u8) -> Result<(), DecodeError> {
// Frame to be shown. Because the spec mandates that frame_to_show_map_idx references a
// valid entry in the DPB, an non-existing index means that the stream is invalid.
let idx = usize::from(frame_to_show_map_idx);
let ref_frame = self
.codec
.reference_frames
.get(idx)
.ok_or_else(|| anyhow::anyhow!("invalid reference frame index in header"))?
.as_ref()
.ok_or_else(|| anyhow::anyhow!("empty reference frame referenced in frame header"))?;
// We are done, no further processing needed.
let decoded_handle = ref_frame.clone();
self.ready_queue.push(decoded_handle);
Ok(())
}
/// Decode a single frame.
fn handle_frame(&mut self, frame: &Frame, picture: B::Picture) -> Result<(), DecodeError> {
let refresh_frame_flags = frame.header.refresh_frame_flags;
Segmentation::update_segmentation(&mut self.codec.segmentation, &frame.header);
let decoded_handle = self.backend.submit_picture(
picture,
&frame.header,
&self.codec.reference_frames,
frame.as_ref(),
&self.codec.segmentation,
)?;
if self.blocking_mode == BlockingMode::Blocking {
decoded_handle.sync()?;
}
// Do DPB management
Self::update_references(
&mut self.codec.reference_frames,
&decoded_handle,
refresh_frame_flags,
)?;
if frame.header.show_frame {
self.ready_queue.push(decoded_handle);
}
Ok(())
}
fn negotiation_possible(&self, hdr: &Header, old_negotiation_info: &NegotiationInfo) -> bool {
let negotiation_info = NegotiationInfo::from(hdr);
if negotiation_info.coded_resolution.width == 0
|| negotiation_info.coded_resolution.height == 0
{
false
} else {
*old_negotiation_info != negotiation_info
}
}
}
impl<B> StatelessVideoDecoder for StatelessDecoder<Vp9, B>
where
B: StatelessVp9DecoderBackend + TryFormat<Vp9>,
B::Handle: Clone + 'static,
{
type Handle = B::Handle;
type FramePool = B::FramePool;
fn decode(&mut self, timestamp: u64, bitstream: &[u8]) -> Result<usize, DecodeError> {
let frames = self
.codec
.parser
.parse_chunk(bitstream)
.map_err(|err| DecodeError::ParseFrameError(err))?;
// With SVC, the first frame will usually be a key-frame, with
// inter-frames carrying the other layers.
//
// We do not want each of those to be considered as a separate DRC
// event. Not only that, allowing them to be will cause an infinite
// loop.
//
// Instead, negotiate based on the largest spatial layer. That will be
// enough to house the other layers in between.
let largest_in_superframe = frames.iter().max_by(|&a, &b| {
let a_res = Resolution::from((a.header.width, a.header.height));
let b_res = Resolution::from((b.header.width, b.header.height));
if a_res == b_res {
std::cmp::Ordering::Equal
} else if a_res.can_contain(b_res) {
std::cmp::Ordering::Greater
} else {
std::cmp::Ordering::Less
}
});
if let Some(frame) = largest_in_superframe {
if self.negotiation_possible(&frame.header, &self.codec.negotiation_info) {
self.backend.new_sequence(&frame.header)?;
self.await_format_change(frame.header.clone());
} else if matches!(self.decoding_state, DecodingState::Reset) {
// We can resume decoding since the decoding parameters have not changed.
self.decoding_state = DecodingState::Decoding;
}
}
match &mut self.decoding_state {
// Skip input until we get information from the stream.
DecodingState::AwaitingStreamInfo | DecodingState::Reset => (),
// Ask the client to confirm the format before we can process this.
DecodingState::AwaitingFormat(_) => return Err(DecodeError::CheckEvents),
DecodingState::Decoding => {
// First allocate all the pictures we need for this superframe.
let num_required_pictures = frames
.iter()
.filter(|f| !f.header.show_existing_frame)
.count();
let frames_with_pictures = frames
.into_iter()
.enumerate()
.map(|(i, frame)| {
if frame.header.show_existing_frame {
Ok((frame, None))
} else {
self.backend
.new_picture(timestamp)
.map_err(|e| match e {
NewPictureError::OutOfOutputBuffers => {
DecodeError::NotEnoughOutputBuffers(
num_required_pictures - i,
)
}
e => DecodeError::from(e),
})
.map(|picture| (frame, Some(picture)))
}
})
.collect::<Result<Vec<_>, _>>()?;
// Then process each frame.
for (frame, picture) in frames_with_pictures {
match picture {
None => {
self.handle_show_existing_frame(frame.header.frame_to_show_map_idx)?
}
Some(picture) => self.handle_frame(&frame, picture)?,
}
}
}
}
Ok(bitstream.len())
}
fn flush(&mut self) -> Result<(), DecodeError> {
// Note: all the submitted frames are already in the ready queue.
self.codec.reference_frames = Default::default();
self.decoding_state = DecodingState::Reset;
Ok(())
}
fn next_event(&mut self) -> Option<DecoderEvent<B::Handle>> {
self.query_next_event(|decoder, hdr| {
decoder.codec.negotiation_info = hdr.into();
})
}
fn frame_pool(&mut self, layer: PoolLayer) -> Vec<&mut B::FramePool> {
self.backend.frame_pool(layer)
}
fn stream_info(&self) -> Option<&StreamInfo> {
self.backend.stream_info()
}
fn poll_fd(&self) -> BorrowedFd {
self.epoll_fd.0.as_fd()
}
}
#[cfg(test)]
pub mod tests {
use crate::bitstream_utils::IvfIterator;
use crate::decoder::stateless::tests::test_decode_stream;
use crate::decoder::stateless::tests::TestStream;
use crate::decoder::stateless::vp9::Vp9;
use crate::decoder::stateless::StatelessDecoder;
use crate::decoder::BlockingMode;
use crate::utils::simple_playback_loop;
use crate::utils::simple_playback_loop_owned_frames;
use crate::DecodedFormat;
/// Run `test` using the dummy decoder, in both blocking and non-blocking modes.
fn test_decoder_dummy(test: &TestStream, blocking_mode: BlockingMode) {
let decoder = StatelessDecoder::<Vp9, _>::new_dummy(blocking_mode).unwrap();
test_decode_stream(
|d, s, c| {
simple_playback_loop(
d,
IvfIterator::new(s),
c,
&mut simple_playback_loop_owned_frames,
DecodedFormat::NV12,
blocking_mode,
)
},
decoder,
test,
false,
false,
);
}
/// Same as Chromium's test-25fps.vp8
pub const DECODE_TEST_25FPS: TestStream = TestStream {
stream: include_bytes!("../../codec/vp9/test_data/test-25fps.vp9"),
crcs: include_str!("../../codec/vp9/test_data/test-25fps.vp9.crc"),
};
#[test]
fn test_25fps_block() {
test_decoder_dummy(&DECODE_TEST_25FPS, BlockingMode::Blocking);
}
#[test]
fn test_25fps_nonblock() {
test_decoder_dummy(&DECODE_TEST_25FPS, BlockingMode::NonBlocking);
}
// Remuxed from the original matroska source in libvpx using ffmpeg:
// ffmpeg -i vp90-2-10-show-existing-frame.webm/vp90-2-10-show-existing-frame.webm -c:v copy /tmp/vp90-2-10-show-existing-frame.vp9.ivf
pub const DECODE_TEST_25FPS_SHOW_EXISTING_FRAME: TestStream = TestStream {
stream: include_bytes!("../../codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf"),
crcs: include_str!("../../codec/vp9/test_data/vp90-2-10-show-existing-frame.vp9.ivf.crc"),
};
#[test]
fn show_existing_frame_block() {
test_decoder_dummy(
&DECODE_TEST_25FPS_SHOW_EXISTING_FRAME,
BlockingMode::Blocking,
);
}
#[test]
fn show_existing_frame_nonblock() {
test_decoder_dummy(
&DECODE_TEST_25FPS_SHOW_EXISTING_FRAME,
BlockingMode::NonBlocking,
);
}
pub const DECODE_TEST_25FPS_SHOW_EXISTING_FRAME2: TestStream = TestStream {
stream: include_bytes!("../../codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf"),
crcs: include_str!("../../codec/vp9/test_data/vp90-2-10-show-existing-frame2.vp9.ivf.crc"),
};
#[test]
fn show_existing_frame2_block() {
test_decoder_dummy(
&DECODE_TEST_25FPS_SHOW_EXISTING_FRAME2,
BlockingMode::Blocking,
);
}
#[test]
fn show_existing_frame2_nonblock() {
test_decoder_dummy(
&DECODE_TEST_25FPS_SHOW_EXISTING_FRAME2,
BlockingMode::NonBlocking,
);
}
// Remuxed from the original matroska source in libvpx using ffmpeg:
// ffmpeg -i vp90-2-10-show-existing-frame.webm/vp90-2-10-show-existing-frame.webm -c:v copy /tmp/vp90-2-10-show-existing-frame.vp9.ivf
// There are some weird padding issues introduced by GStreamer for
// resolutions that are not multiple of 4, so we're ignoring CRCs for
// this one.
pub const DECODE_RESOLUTION_CHANGE_500FRAMES: TestStream = TestStream {
stream: include_bytes!("../../codec/vp9/test_data/resolution_change_500frames-vp9.ivf"),
crcs: include_str!("../../codec/vp9/test_data/resolution_change_500frames-vp9.ivf.crc"),
};
#[test]
fn test_resolution_change_500frames_block() {
test_decoder_dummy(&DECODE_RESOLUTION_CHANGE_500FRAMES, BlockingMode::Blocking);
}
#[test]
fn test_resolution_change_500frames_nonblock() {
test_decoder_dummy(&DECODE_RESOLUTION_CHANGE_500FRAMES, BlockingMode::Blocking);
}
}