video: Add VideoDecoder trait

Change VideoStream to use a VideoDecoder trait instead of an enum.
This will make it a little easier as more codecs are added, and
allow us to easily enable/disable codecs behind features.
This commit is contained in:
Mike Welsh 2021-04-20 23:56:48 -07:00 committed by kmeisthax
parent 48fa03b87d
commit a05f81b393
2 changed files with 136 additions and 61 deletions

View File

@ -32,6 +32,13 @@ impl<'a> EncodedFrame<'a> {
} }
} }
/// A decoded frame of video in RGBA format.
struct DecodedFrame {
width: u16,
height: u16,
rgba: Vec<u8>,
}
/// What dependencies a given video frame has on any previous frames. /// What dependencies a given video frame has on any previous frames.
#[derive(Copy, Clone, Debug)] #[derive(Copy, Clone, Debug)]
pub enum FrameDependency { pub enum FrameDependency {

View File

@ -2,20 +2,11 @@
use crate::backend::render::{BitmapHandle, BitmapInfo, RenderBackend}; use crate::backend::render::{BitmapHandle, BitmapInfo, RenderBackend};
use crate::backend::video::{ use crate::backend::video::{
EncodedFrame, Error, FrameDependency, VideoBackend, VideoStreamHandle, DecodedFrame, EncodedFrame, Error, FrameDependency, VideoBackend, VideoStreamHandle,
}; };
use generational_arena::Arena; use generational_arena::Arena;
use h263_rs::parser::{decode_picture, H263Reader};
use h263_rs::{DecoderOption, H263State, PictureTypeCode};
use h263_rs_yuv::bt601::yuv420_to_rgba;
use swf::{VideoCodec, VideoDeblocking}; use swf::{VideoCodec, VideoDeblocking};
/// A single preloaded video stream.
pub enum VideoStream {
/// An H.263 video stream.
H263(H263State, Option<BitmapHandle>),
}
/// Software video backend that proxies to CPU-only codec implementations that /// Software video backend that proxies to CPU-only codec implementations that
/// ship with Ruffle. /// ship with Ruffle.
pub struct SoftwareVideoBackend { pub struct SoftwareVideoBackend {
@ -44,13 +35,13 @@ impl VideoBackend for SoftwareVideoBackend {
codec: VideoCodec, codec: VideoCodec,
_filter: VideoDeblocking, _filter: VideoDeblocking,
) -> Result<VideoStreamHandle, Error> { ) -> Result<VideoStreamHandle, Error> {
match codec { let decoder: Box<dyn VideoDecoder> = match codec {
VideoCodec::H263 => Ok(self.streams.insert(VideoStream::H263( VideoCodec::H263 => Box::new(h263::H263Decoder::new()),
H263State::new(DecoderOption::SORENSON_SPARK_BITSTREAM), _ => return Err(format!("Unsupported video codec type {:?}", codec).into()),
None, };
))), let stream = VideoStream::new(decoder);
_ => Err(format!("Unsupported video codec type {:?}", codec).into()), let stream_handle = self.streams.insert(stream);
} Ok(stream_handle)
} }
fn preload_video_stream_frame( fn preload_video_stream_frame(
@ -63,21 +54,7 @@ impl VideoBackend for SoftwareVideoBackend {
.get_mut(stream) .get_mut(stream)
.ok_or("Unregistered video stream")?; .ok_or("Unregistered video stream")?;
match stream { stream.decoder.preload_frame(encoded_frame)
VideoStream::H263(_state, _last_bitmap) => {
let mut reader = H263Reader::from_source(encoded_frame.data());
let picture =
decode_picture(&mut reader, DecoderOption::SORENSON_SPARK_BITSTREAM, None)?
.ok_or("Picture in video stream is not a picture")?;
match picture.picture_type {
PictureTypeCode::IFrame => Ok(FrameDependency::None),
PictureTypeCode::PFrame => Ok(FrameDependency::Past),
PictureTypeCode::DisposablePFrame => Ok(FrameDependency::Past),
_ => Err("Invalid picture type code!".into()),
}
}
}
} }
fn decode_video_stream_frame( fn decode_video_stream_frame(
@ -91,38 +68,129 @@ impl VideoBackend for SoftwareVideoBackend {
.get_mut(stream) .get_mut(stream)
.ok_or("Unregistered video stream")?; .ok_or("Unregistered video stream")?;
match stream { let frame = stream.decoder.decode_frame(encoded_frame)?;
VideoStream::H263(state, last_bitmap) => { let handle = if let Some(bitmap) = stream.bitmap {
let mut reader = H263Reader::from_source(encoded_frame.data()); renderer.update_texture(bitmap, frame.width.into(), frame.height.into(), frame.rgba)?
} else {
renderer.register_bitmap_raw(frame.width.into(), frame.height.into(), frame.rgba)?
};
stream.bitmap = Some(handle);
state.decode_next_picture(&mut reader)?; Ok(BitmapInfo {
handle,
width: frame.width,
height: frame.height,
})
}
}
let picture = state /// A single preloaded video stream.
.get_last_picture() struct VideoStream {
.expect("Decoding a picture should let us grab that picture"); bitmap: Option<BitmapHandle>,
decoder: Box<dyn VideoDecoder>,
}
let (width, height) = picture impl VideoStream {
.format() fn new(decoder: Box<dyn VideoDecoder>) -> Self {
.into_width_and_height() Self {
.ok_or("H.263 decoder error!")?; decoder,
let chroma_width = picture.chroma_samples_per_row(); bitmap: None,
let (y, b, r) = picture.as_yuv(); }
let rgba = yuv420_to_rgba(y, b, r, width.into(), chroma_width); }
}
let handle = if let Some(lb) = last_bitmap {
renderer.update_texture(*lb, width.into(), height.into(), rgba)? /// Trait for video decoders.
} else { /// This should be implemented for each video codec.
renderer.register_bitmap_raw(width.into(), height.into(), rgba)? trait VideoDecoder {
}; /// Preload a frame.
///
*last_bitmap = Some(handle); /// No decoding is intended to happen at this point in time. Instead, the
/// video data should be inspected to determine inter-frame dependencies
Ok(BitmapInfo { /// between this and any previous frames in the stream.
handle, ///
width, /// Frames should be preloaded in the order that they are recieved.
height, ///
}) /// Any dependencies listed here are inherent to the video bitstream. The
} /// containing video stream is also permitted to introduce additional
/// interframe dependencies.
fn preload_frame(&mut self, encoded_frame: EncodedFrame<'_>) -> Result<FrameDependency, Error>;
/// Decode a frame of a given video stream.
///
/// This function is provided the external index of the frame, the codec
/// used to decode the data, and what codec to decode it with. The codec
/// provided here must match the one used to register the video stream.
///
/// Frames may be decoded in any order that does not violate the frame
/// dependencies declared by the output of `preload_video_stream_frame`.
///
/// The decoded frame should be returned. An `Error` can be returned if
/// a drawable bitmap can not be produced.
fn decode_frame(&mut self, encoded_frame: EncodedFrame<'_>) -> Result<DecodedFrame, Error>;
}
mod h263 {
use crate::backend::video::software::VideoDecoder;
use crate::backend::video::{DecodedFrame, EncodedFrame, Error, FrameDependency};
use h263_rs::parser::{decode_picture, H263Reader};
use h263_rs::{DecoderOption, H263State, PictureTypeCode};
use h263_rs_yuv::bt601::yuv420_to_rgba;
/// H263 video decoder.
pub struct H263Decoder(H263State);
impl H263Decoder {
pub fn new() -> Self {
Self(H263State::new(DecoderOption::SORENSON_SPARK_BITSTREAM))
}
}
impl VideoDecoder for H263Decoder {
fn preload_frame(
&mut self,
encoded_frame: EncodedFrame<'_>,
) -> Result<FrameDependency, Error> {
let mut reader = H263Reader::from_source(encoded_frame.data());
let picture =
decode_picture(&mut reader, DecoderOption::SORENSON_SPARK_BITSTREAM, None)?
.ok_or("Picture in video stream is not a picture")?;
match picture.picture_type {
PictureTypeCode::IFrame => Ok(FrameDependency::None),
PictureTypeCode::PFrame => Ok(FrameDependency::Past),
PictureTypeCode::DisposablePFrame => Ok(FrameDependency::Past),
_ => Err("Invalid picture type code!".into()),
}
}
fn decode_frame(&mut self, encoded_frame: EncodedFrame<'_>) -> Result<DecodedFrame, Error> {
let mut reader = H263Reader::from_source(encoded_frame.data());
self.0.decode_next_picture(&mut reader)?;
let picture = self
.0
.get_last_picture()
.expect("Decoding a picture should let us grab that picture");
let (width, height) = picture
.format()
.into_width_and_height()
.ok_or("H.263 decoder error!")?;
let chroma_width = picture.chroma_samples_per_row();
let (y, b, r) = picture.as_yuv();
let rgba = yuv420_to_rgba(y, b, r, width.into(), chroma_width);
Ok(DecodedFrame {
width,
height,
rgba,
})
}
}
impl Default for H263Decoder {
fn default() -> Self {
Self::new()
} }
} }
} }