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.
|
/// 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 {
|
||||||
|
|
|
@ -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,13 +68,108 @@ 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 {
|
||||||
|
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());
|
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()
|
.get_last_picture()
|
||||||
.expect("Decoding a picture should let us grab that 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 chroma_width = picture.chroma_samples_per_row();
|
||||||
let (y, b, r) = picture.as_yuv();
|
let (y, b, r) = picture.as_yuv();
|
||||||
let rgba = yuv420_to_rgba(y, b, r, width.into(), chroma_width);
|
let rgba = yuv420_to_rgba(y, b, r, width.into(), chroma_width);
|
||||||
|
Ok(DecodedFrame {
|
||||||
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,
|
|
||||||
width,
|
width,
|
||||||
height,
|
height,
|
||||||
|
rgba,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Default for H263Decoder {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::new()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue