// 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(any(test, fuzzing))] mod dummy; #[cfg(feature = "v4l2")] mod v4l2; #[cfg(feature = "vaapi")] mod vaapi; use std::os::fd::AsFd; use std::os::fd::BorrowedFd; use crate::codec::vp8::parser::Frame; use crate::codec::vp8::parser::Header; use crate::codec::vp8::parser::MbLfAdjustments; use crate::codec::vp8::parser::Parser; use crate::codec::vp8::parser::Segmentation; use crate::decoder::stateless::DecodeError; use crate::decoder::stateless::DecodingState; use crate::decoder::stateless::NewPictureResult; 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::BlockingMode; use crate::decoder::DecodedHandle; use crate::decoder::DecoderEvent; use crate::decoder::StreamInfo; use crate::Resolution; /// Stateless backend methods specific to VP8. pub trait StatelessVp8DecoderBackend: StatelessDecoderBackend + StatelessDecoderBackendPicture { /// Called when new stream parameters are found. fn new_sequence(&mut self, header: &Header) -> StatelessBackendResult<()>; /// Called when the decoder determines that a frame or field was found. fn new_picture( &mut self, timestamp: u64, alloc_cb: &mut dyn FnMut() -> Option< <::Handle as DecodedHandle>::Frame, >, ) -> NewPictureResult; /// 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. #[allow(clippy::too_many_arguments)] fn submit_picture( &mut self, picture: Self::Picture, hdr: &Header, last_ref: &Option, golden_ref: &Option, alt_ref: &Option, bitstream: &[u8], segmentation: &Segmentation, mb_lf_adjust: &MbLfAdjustments, ) -> StatelessBackendResult; } pub struct Vp8DecoderState { /// VP8 bitstream parser. parser: Parser, /// The picture used as the last reference picture. last_picture: Option, /// The picture used as the golden reference picture. golden_ref_picture: Option, /// The picture used as the alternate reference picture. alt_ref_picture: Option, } impl Default for Vp8DecoderState { fn default() -> Self { Self { parser: Default::default(), last_picture: Default::default(), golden_ref_picture: Default::default(), alt_ref_picture: Default::default(), } } } /// [`StatelessCodec`] structure to use in order to create a VP8 stateless decoder. /// /// # Accepted input /// /// A decoder using this codec processes exactly one encoded frame per call to /// [`StatelessDecoder::decode`], and returns the number of bytes actually taken by the frame data. /// If the frame was properly encapsulated in its container, the returned value should be equal to /// the length of the submitted input. pub struct Vp8; impl StatelessCodec for Vp8 { type FormatInfo = Header; type DecoderState = Vp8DecoderState; } impl Vp8DecoderState where H: DecodedHandle + Clone, { /// Replace a reference frame with `handle`. fn replace_reference(reference: &mut Option, handle: &H) { *reference = Some(handle.clone()); } pub(crate) fn update_references( &mut self, header: &Header, decoded_handle: &H, ) -> anyhow::Result<()> { if header.key_frame { Self::replace_reference(&mut self.last_picture, decoded_handle); Self::replace_reference(&mut self.golden_ref_picture, decoded_handle); Self::replace_reference(&mut self.alt_ref_picture, decoded_handle); } else { if header.refresh_alternate_frame { Self::replace_reference(&mut self.alt_ref_picture, decoded_handle); } else { match header.copy_buffer_to_alternate { 0 => { /* do nothing */ } 1 => { if let Some(last_picture) = &self.last_picture { Self::replace_reference(&mut self.alt_ref_picture, last_picture); } } 2 => { if let Some(golden_ref) = &self.golden_ref_picture { Self::replace_reference(&mut self.alt_ref_picture, golden_ref); } } other => anyhow::bail!("invalid copy_buffer_to_alternate value: {}", other), } } if header.refresh_golden_frame { Self::replace_reference(&mut self.golden_ref_picture, decoded_handle); } else { match header.copy_buffer_to_golden { 0 => { /* do nothing */ } 1 => { if let Some(last_picture) = &self.last_picture { Self::replace_reference(&mut self.golden_ref_picture, last_picture); } } 2 => { if let Some(alt_ref) = &self.alt_ref_picture { Self::replace_reference(&mut self.golden_ref_picture, alt_ref); } } other => anyhow::bail!("invalid copy_buffer_to_golden value: {}", other), } } if header.refresh_last { Self::replace_reference(&mut self.last_picture, decoded_handle); } } Ok(()) } } impl StatelessDecoder where B: StatelessVp8DecoderBackend, B::Handle: Clone + DecodedHandle, { /// Handle a single frame. fn handle_frame( &mut self, frame: Frame, timestamp: u64, alloc_cb: &mut dyn FnMut() -> Option< <::Handle as DecodedHandle>::Frame, >, ) -> Result<(), DecodeError> { let show_frame = frame.header.show_frame; let picture = self.backend.new_picture(timestamp, alloc_cb)?; let decoded_handle = self.backend.submit_picture( picture, &frame.header, &self.codec.last_picture, &self.codec.golden_ref_picture, &self.codec.alt_ref_picture, frame.as_ref(), self.codec.parser.segmentation(), self.codec.parser.mb_lf_adjust(), )?; if self.blocking_mode == BlockingMode::Blocking { decoded_handle.sync()?; } // Do DPB management self.codec.update_references(&frame.header, &decoded_handle)?; if show_frame { self.ready_queue.push(decoded_handle); } Ok(()) } fn negotiation_possible(&self, frame: &Frame) -> bool { let coded_resolution = self.coded_resolution; let hdr = &frame.header; let width = u32::from(hdr.width); let height = u32::from(hdr.height); width != coded_resolution.width || height != coded_resolution.height } } impl StatelessVideoDecoder for StatelessDecoder where B: StatelessVp8DecoderBackend, B::Handle: Clone + 'static, { type Handle = B::Handle; fn decode( &mut self, timestamp: u64, bitstream: &[u8], alloc_cb: &mut dyn FnMut() -> Option< <::Handle as DecodedHandle>::Frame, >, ) -> Result { let frame = self .codec .parser .parse_frame(bitstream) .map_err(|err| DecodeError::ParseFrameError(err.to_string()))?; self.wait_for_drc_flush()?; if frame.header.key_frame { if self.negotiation_possible(&frame) { if matches!(self.decoding_state, DecodingState::Decoding) { // DRC occurs when a key frame is seen, the format is different, // and the decoder is already decoding frames. self.flush()?; self.decoding_state = DecodingState::FlushingForDRC; // Start signaling the awaiting format event to process a format change. self.awaiting_format_event.write(1).unwrap(); return Err(DecodeError::CheckEvents); } 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 => Ok(bitstream.len()), // Ask the client to confirm the format before we can process this. DecodingState::FlushingForDRC | DecodingState::AwaitingFormat(_) => { Err(DecodeError::CheckEvents) } DecodingState::Decoding => { let len = frame.header.frame_len(); self.handle_frame(frame, timestamp, alloc_cb)?; Ok(len) } } } fn flush(&mut self) -> Result<(), DecodeError> { // Note: all the submitted frames are already in the ready queue. self.codec.last_picture = Default::default(); self.codec.golden_ref_picture = Default::default(); self.codec.alt_ref_picture = Default::default(); self.decoding_state = DecodingState::Reset; Ok(()) } fn next_event(&mut self) -> Option> { self.query_next_event(|decoder, hdr| { decoder.coded_resolution = Resolution { width: hdr.width as u32, height: hdr.height as u32 }; }) } 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::vp8::Vp8; 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::::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/vp8/test_data/test-25fps.vp8"), crcs: include_str!("../../codec/vp8/test_data/test-25fps.vp8.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); } }