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:
parent
48fa03b87d
commit
a05f81b393
|
@ -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.
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub enum FrameDependency {
|
||||
|
|
|
@ -2,20 +2,11 @@
|
|||
|
||||
use crate::backend::render::{BitmapHandle, BitmapInfo, RenderBackend};
|
||||
use crate::backend::video::{
|
||||
EncodedFrame, Error, FrameDependency, VideoBackend, VideoStreamHandle,
|
||||
DecodedFrame, EncodedFrame, Error, FrameDependency, VideoBackend, VideoStreamHandle,
|
||||
};
|
||||
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};
|
||||
|
||||
/// 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
|
||||
/// ship with Ruffle.
|
||||
pub struct SoftwareVideoBackend {
|
||||
|
@ -44,13 +35,13 @@ impl VideoBackend for SoftwareVideoBackend {
|
|||
codec: VideoCodec,
|
||||
_filter: VideoDeblocking,
|
||||
) -> Result<VideoStreamHandle, Error> {
|
||||
match codec {
|
||||
VideoCodec::H263 => Ok(self.streams.insert(VideoStream::H263(
|
||||
H263State::new(DecoderOption::SORENSON_SPARK_BITSTREAM),
|
||||
None,
|
||||
))),
|
||||
_ => Err(format!("Unsupported video codec type {:?}", codec).into()),
|
||||
}
|
||||
let decoder: Box<dyn VideoDecoder> = match codec {
|
||||
VideoCodec::H263 => Box::new(h263::H263Decoder::new()),
|
||||
_ => return Err(format!("Unsupported video codec type {:?}", codec).into()),
|
||||
};
|
||||
let stream = VideoStream::new(decoder);
|
||||
let stream_handle = self.streams.insert(stream);
|
||||
Ok(stream_handle)
|
||||
}
|
||||
|
||||
fn preload_video_stream_frame(
|
||||
|
@ -63,21 +54,7 @@ impl VideoBackend for SoftwareVideoBackend {
|
|||
.get_mut(stream)
|
||||
.ok_or("Unregistered video stream")?;
|
||||
|
||||
match stream {
|
||||
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()),
|
||||
}
|
||||
}
|
||||
}
|
||||
stream.decoder.preload_frame(encoded_frame)
|
||||
}
|
||||
|
||||
fn decode_video_stream_frame(
|
||||
|
@ -91,13 +68,108 @@ impl VideoBackend for SoftwareVideoBackend {
|
|||
.get_mut(stream)
|
||||
.ok_or("Unregistered video stream")?;
|
||||
|
||||
match stream {
|
||||
VideoStream::H263(state, last_bitmap) => {
|
||||
let frame = stream.decoder.decode_frame(encoded_frame)?;
|
||||
let handle = if let Some(bitmap) = stream.bitmap {
|
||||
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);
|
||||
|
||||
Ok(BitmapInfo {
|
||||
handle,
|
||||
width: frame.width,
|
||||
height: frame.height,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A single preloaded video stream.
|
||||
struct VideoStream {
|
||||
bitmap: Option<BitmapHandle>,
|
||||
decoder: Box<dyn VideoDecoder>,
|
||||
}
|
||||
|
||||
impl VideoStream {
|
||||
fn new(decoder: Box<dyn VideoDecoder>) -> Self {
|
||||
Self {
|
||||
decoder,
|
||||
bitmap: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait for video decoders.
|
||||
/// This should be implemented for each video codec.
|
||||
trait VideoDecoder {
|
||||
/// Preload a frame.
|
||||
///
|
||||
/// No decoding is intended to happen at this point in time. Instead, the
|
||||
/// video data should be inspected to determine inter-frame dependencies
|
||||
/// between this and any previous frames in the stream.
|
||||
///
|
||||
/// Frames should be preloaded in the order that they are recieved.
|
||||
///
|
||||
/// 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());
|
||||
|
||||
state.decode_next_picture(&mut reader)?;
|
||||
self.0.decode_next_picture(&mut reader)?;
|
||||
|
||||
let picture = state
|
||||
let picture = self
|
||||
.0
|
||||
.get_last_picture()
|
||||
.expect("Decoding a picture should let us grab that picture");
|
||||
|
||||
|
@ -108,21 +180,17 @@ impl VideoBackend for SoftwareVideoBackend {
|
|||
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);
|
||||
|
||||
let handle = if let Some(lb) = last_bitmap {
|
||||
renderer.update_texture(*lb, width.into(), height.into(), rgba)?
|
||||
} else {
|
||||
renderer.register_bitmap_raw(width.into(), height.into(), rgba)?
|
||||
};
|
||||
|
||||
*last_bitmap = Some(handle);
|
||||
|
||||
Ok(BitmapInfo {
|
||||
handle,
|
||||
Ok(DecodedFrame {
|
||||
width,
|
||||
height,
|
||||
rgba,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for H263Decoder {
|
||||
fn default() -> Self {
|
||||
Self::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue