core: Use real Error enums for video backend
This commit is contained in:
parent
17f261fc1f
commit
1c7bfd8c5f
|
@ -4,6 +4,7 @@ use generational_arena::{Arena, Index};
|
||||||
use ruffle_render::backend::RenderBackend;
|
use ruffle_render::backend::RenderBackend;
|
||||||
use ruffle_render::bitmap::BitmapInfo;
|
use ruffle_render::bitmap::BitmapInfo;
|
||||||
use swf::{VideoCodec, VideoDeblocking};
|
use swf::{VideoCodec, VideoDeblocking};
|
||||||
|
use thiserror::Error;
|
||||||
|
|
||||||
mod software;
|
mod software;
|
||||||
|
|
||||||
|
@ -11,7 +12,35 @@ pub use crate::backend::video::software::SoftwareVideoBackend;
|
||||||
|
|
||||||
pub type VideoStreamHandle = Index;
|
pub type VideoStreamHandle = Index;
|
||||||
|
|
||||||
pub type Error = Box<dyn std::error::Error>;
|
#[derive(Error, Debug)]
|
||||||
|
pub enum Error {
|
||||||
|
#[error("Attempted to seek to omitted frame {0} without prior decoded frame")]
|
||||||
|
SeekingBeforeDecoding(u32),
|
||||||
|
|
||||||
|
#[error("Unsupported video codec type: {0:?}")]
|
||||||
|
UnsupportedCodec(VideoCodec),
|
||||||
|
|
||||||
|
#[error("Video stream is not registered")]
|
||||||
|
VideoStreamIsNotRegistered,
|
||||||
|
|
||||||
|
#[error("Couldn't create bitmap for video frame")]
|
||||||
|
BitmapError(#[from] ruffle_render::error::Error),
|
||||||
|
|
||||||
|
#[error("Video decoding isn't supported")]
|
||||||
|
DecodingNotSupported,
|
||||||
|
|
||||||
|
#[cfg(feature = "h263")]
|
||||||
|
#[error("H263 decoder error")]
|
||||||
|
H263Error(#[from] software::decoders::h263::H263Error),
|
||||||
|
|
||||||
|
#[cfg(feature = "vp6")]
|
||||||
|
#[error("VP6 decoder error")]
|
||||||
|
VP6Error(#[from] software::decoders::vp6::Vp6Error),
|
||||||
|
|
||||||
|
#[cfg(feature = "screenvideo")]
|
||||||
|
#[error("Screen Video decoder error")]
|
||||||
|
ScreenVideoError(#[from] software::decoders::screen::ScreenError),
|
||||||
|
}
|
||||||
|
|
||||||
/// An encoded video frame of some video codec.
|
/// An encoded video frame of some video codec.
|
||||||
pub struct EncodedFrame<'a> {
|
pub struct EncodedFrame<'a> {
|
||||||
|
@ -178,6 +207,6 @@ impl VideoBackend for NullVideoBackend {
|
||||||
_encoded_frame: EncodedFrame<'_>,
|
_encoded_frame: EncodedFrame<'_>,
|
||||||
_renderer: &mut dyn RenderBackend,
|
_renderer: &mut dyn RenderBackend,
|
||||||
) -> Result<BitmapInfo, Error> {
|
) -> Result<BitmapInfo, Error> {
|
||||||
Err("Video decoding not implemented".into())
|
Err(Error::DecodingNotSupported)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,21 @@ use h263_rs::parser::H263Reader;
|
||||||
use h263_rs::{DecoderOption, H263State, PictureTypeCode};
|
use h263_rs::{DecoderOption, H263State, PictureTypeCode};
|
||||||
use h263_rs_yuv::bt601::yuv420_to_rgba;
|
use h263_rs_yuv::bt601::yuv420_to_rgba;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum H263Error {
|
||||||
|
#[error("Picture wasn't found in the video stream")]
|
||||||
|
NoPictureInVideoStream,
|
||||||
|
|
||||||
|
#[error("Decoder error")]
|
||||||
|
DecoderError(#[from] h263_rs::Error),
|
||||||
|
|
||||||
|
#[error("Invalid picture type code: {0:?}")]
|
||||||
|
InvalidPictureType(PictureTypeCode),
|
||||||
|
|
||||||
|
#[error("Picture is missing width and height details")]
|
||||||
|
MissingWidthHeight,
|
||||||
|
}
|
||||||
|
|
||||||
/// H263 video decoder.
|
/// H263 video decoder.
|
||||||
pub struct H263Decoder(H263State);
|
pub struct H263Decoder(H263State);
|
||||||
|
|
||||||
|
@ -18,21 +33,24 @@ impl VideoDecoder for H263Decoder {
|
||||||
let mut reader = H263Reader::from_source(encoded_frame.data());
|
let mut reader = H263Reader::from_source(encoded_frame.data());
|
||||||
let picture = self
|
let picture = self
|
||||||
.0
|
.0
|
||||||
.parse_picture(&mut reader, None)?
|
.parse_picture(&mut reader, None)
|
||||||
.ok_or("Picture in video stream is not a picture")?;
|
.map_err(H263Error::DecoderError)?
|
||||||
|
.ok_or(H263Error::NoPictureInVideoStream)?;
|
||||||
|
|
||||||
match picture.picture_type {
|
match picture.picture_type {
|
||||||
PictureTypeCode::IFrame => Ok(FrameDependency::None),
|
PictureTypeCode::IFrame => Ok(FrameDependency::None),
|
||||||
PictureTypeCode::PFrame => Ok(FrameDependency::Past),
|
PictureTypeCode::PFrame => Ok(FrameDependency::Past),
|
||||||
PictureTypeCode::DisposablePFrame => Ok(FrameDependency::Past),
|
PictureTypeCode::DisposablePFrame => Ok(FrameDependency::Past),
|
||||||
_ => Err("Invalid picture type code!".into()),
|
code => Err(H263Error::InvalidPictureType(code).into()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn decode_frame(&mut self, encoded_frame: EncodedFrame<'_>) -> Result<DecodedFrame, Error> {
|
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());
|
||||||
|
|
||||||
self.0.decode_next_picture(&mut reader)?;
|
self.0
|
||||||
|
.decode_next_picture(&mut reader)
|
||||||
|
.map_err(H263Error::DecoderError)?;
|
||||||
|
|
||||||
let picture = self
|
let picture = self
|
||||||
.0
|
.0
|
||||||
|
@ -42,7 +60,7 @@ impl VideoDecoder for H263Decoder {
|
||||||
let (width, height) = picture
|
let (width, height) = picture
|
||||||
.format()
|
.format()
|
||||||
.into_width_and_height()
|
.into_width_and_height()
|
||||||
.ok_or("H.263 decoder error!")?;
|
.ok_or(H263Error::MissingWidthHeight)?;
|
||||||
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);
|
||||||
|
|
|
@ -6,6 +6,24 @@ use crate::backend::video::{DecodedFrame, EncodedFrame, Error, FrameDependency};
|
||||||
|
|
||||||
use flate2::Decompress;
|
use flate2::Decompress;
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum ScreenError {
|
||||||
|
#[error("Unexpected end of file")]
|
||||||
|
UnexpectedEOF,
|
||||||
|
|
||||||
|
#[error("Decompression error")]
|
||||||
|
DecompressionError(#[from] flate2::DecompressError),
|
||||||
|
|
||||||
|
#[error("Invalid frame type: {0}")]
|
||||||
|
InvalidFrameType(u8),
|
||||||
|
|
||||||
|
#[error("Missing reference frame")]
|
||||||
|
MissingReferenceFrame,
|
||||||
|
|
||||||
|
#[error("Not all blocks were updated by a supposed keyframe")]
|
||||||
|
KeyframeInvalid,
|
||||||
|
}
|
||||||
|
|
||||||
/// Screen Video (V1 only) decoder.
|
/// Screen Video (V1 only) decoder.
|
||||||
pub struct ScreenVideoDecoder {
|
pub struct ScreenVideoDecoder {
|
||||||
w: usize,
|
w: usize,
|
||||||
|
@ -28,24 +46,24 @@ impl<'a> ByteReader<'a> {
|
||||||
Self { data, pos: 0 }
|
Self { data, pos: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_byte(&mut self) -> Result<u8, Error> {
|
fn read_byte(&mut self) -> Result<u8, ScreenError> {
|
||||||
if self.pos >= self.data.len() {
|
if self.pos >= self.data.len() {
|
||||||
return Err(Error::from("Unexpected end of data"));
|
return Err(ScreenError::UnexpectedEOF);
|
||||||
}
|
}
|
||||||
let byte = self.data[self.pos];
|
let byte = self.data[self.pos];
|
||||||
self.pos += 1;
|
self.pos += 1;
|
||||||
Ok(byte)
|
Ok(byte)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_u16be(&mut self) -> Result<u16, Error> {
|
fn read_u16be(&mut self) -> Result<u16, ScreenError> {
|
||||||
let byte1 = self.read_byte()?;
|
let byte1 = self.read_byte()?;
|
||||||
let byte2 = self.read_byte()?;
|
let byte2 = self.read_byte()?;
|
||||||
Ok((byte1 as u16) << 8 | (byte2 as u16))
|
Ok((byte1 as u16) << 8 | (byte2 as u16))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn read_buf_ref(&mut self, length: usize) -> Result<&[u8], Error> {
|
fn read_buf_ref(&mut self, length: usize) -> Result<&[u8], ScreenError> {
|
||||||
if self.pos + length > self.data.len() {
|
if self.pos + length > self.data.len() {
|
||||||
return Err(Error::from("Unexpected end of data"));
|
return Err(ScreenError::UnexpectedEOF);
|
||||||
}
|
}
|
||||||
let result = &self.data[self.pos..self.pos + length];
|
let result = &self.data[self.pos..self.pos + length];
|
||||||
self.pos += length;
|
self.pos += length;
|
||||||
|
@ -85,7 +103,7 @@ impl ScreenVideoDecoder {
|
||||||
&mut self.tile[..cur_w * cur_h * 3],
|
&mut self.tile[..cur_w * cur_h * 3],
|
||||||
flate2::FlushDecompress::Finish,
|
flate2::FlushDecompress::Finish,
|
||||||
)
|
)
|
||||||
.map_err(|e| format!("Error while inflating block: {}", e))?;
|
.map_err(ScreenError::DecompressionError)?;
|
||||||
|
|
||||||
for (dst, src) in row[x * 3..]
|
for (dst, src) in row[x * 3..]
|
||||||
.chunks_mut(stride)
|
.chunks_mut(stride)
|
||||||
|
@ -118,10 +136,7 @@ impl VideoDecoder for ScreenVideoDecoder {
|
||||||
match encoded_frame.data[0] >> 4 {
|
match encoded_frame.data[0] >> 4 {
|
||||||
1 => Ok(FrameDependency::None),
|
1 => Ok(FrameDependency::None),
|
||||||
2 => Ok(FrameDependency::Past),
|
2 => Ok(FrameDependency::Past),
|
||||||
x => Err(Error::from(format!(
|
x => Err(ScreenError::InvalidFrameType(x).into()),
|
||||||
"Unexpected Screen Video frame type: {}",
|
|
||||||
x
|
|
||||||
))),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,7 +144,7 @@ impl VideoDecoder for ScreenVideoDecoder {
|
||||||
let is_keyframe = encoded_frame.data[0] >> 4 == 1;
|
let is_keyframe = encoded_frame.data[0] >> 4 == 1;
|
||||||
|
|
||||||
if !is_keyframe && self.last_frame.is_none() {
|
if !is_keyframe && self.last_frame.is_none() {
|
||||||
return Err(Error::from("Missing reference frame"));
|
return Err(ScreenError::MissingReferenceFrame.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Need to drop the extra preceding byte
|
// Need to drop the extra preceding byte
|
||||||
|
@ -164,9 +179,7 @@ impl VideoDecoder for ScreenVideoDecoder {
|
||||||
let is_intra = self.decode_v1(&mut br, data.as_mut_slice(), stride)?;
|
let is_intra = self.decode_v1(&mut br, data.as_mut_slice(), stride)?;
|
||||||
|
|
||||||
if is_intra != is_keyframe {
|
if is_intra != is_keyframe {
|
||||||
return Err(Error::from(
|
return Err(ScreenError::KeyframeInvalid.into());
|
||||||
"Not all blocks were updated by a supposed keyframe",
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut rgba = vec![0u8; w * h * 4];
|
let mut rgba = vec![0u8; w * h * 4];
|
||||||
|
|
|
@ -9,6 +9,25 @@ use nihav_core::codecs::NADecoderSupport;
|
||||||
use nihav_duck::codecs::vp6::{VP56Decoder, VP56Parser, VP6BR};
|
use nihav_duck::codecs::vp6::{VP56Decoder, VP56Parser, VP6BR};
|
||||||
use nihav_duck::codecs::vpcommon::{BoolCoder, VP_YUVA420_FORMAT};
|
use nihav_duck::codecs::vpcommon::{BoolCoder, VP_YUVA420_FORMAT};
|
||||||
|
|
||||||
|
#[derive(thiserror::Error, Debug)]
|
||||||
|
pub enum Vp6Error {
|
||||||
|
#[error("Decoder error: {0:?}")]
|
||||||
|
// DecoderError doesn't impl Error... so this is manual.
|
||||||
|
DecoderError(nihav_core::codecs::DecoderError),
|
||||||
|
|
||||||
|
#[error("Unexpected skip frame")]
|
||||||
|
UnexpectedSkipFrame,
|
||||||
|
|
||||||
|
#[error("Invalid buffer type")]
|
||||||
|
InvalidBufferType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<nihav_core::codecs::DecoderError> for Vp6Error {
|
||||||
|
fn from(error: nihav_core::codecs::DecoderError) -> Self {
|
||||||
|
Vp6Error::DecoderError(error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// VP6 video decoder.
|
/// VP6 video decoder.
|
||||||
pub struct Vp6Decoder {
|
pub struct Vp6Decoder {
|
||||||
with_alpha: bool,
|
with_alpha: bool,
|
||||||
|
@ -72,16 +91,12 @@ impl VideoDecoder for Vp6Decoder {
|
||||||
} else {
|
} else {
|
||||||
encoded_frame.data
|
encoded_frame.data
|
||||||
})
|
})
|
||||||
.map_err(|error| {
|
.map_err(Vp6Error::DecoderError)?;
|
||||||
Error::from(format!("Error constructing VP6 bool coder: {:?}", error))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let header = self
|
let header = self
|
||||||
.bitreader
|
.bitreader
|
||||||
.parse_header(&mut bool_coder)
|
.parse_header(&mut bool_coder)
|
||||||
.map_err(|error| {
|
.map_err(Vp6Error::DecoderError)?;
|
||||||
Error::from(format!("Error parsing VP6 frame header: {:?}", error))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
let video_info = NAVideoInfo::new(
|
let video_info = NAVideoInfo::new(
|
||||||
header.disp_w as usize * 16,
|
header.disp_w as usize * 16,
|
||||||
|
@ -96,9 +111,7 @@ impl VideoDecoder for Vp6Decoder {
|
||||||
|
|
||||||
self.decoder
|
self.decoder
|
||||||
.init(&mut self.support, video_info)
|
.init(&mut self.support, video_info)
|
||||||
.map_err(|error| {
|
.map_err(Vp6Error::DecoderError)?;
|
||||||
Error::from(format!("Error initializing VP6 decoder: {:?}", error))
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self.init_called = true;
|
self.init_called = true;
|
||||||
}
|
}
|
||||||
|
@ -110,11 +123,7 @@ impl VideoDecoder for Vp6Decoder {
|
||||||
|
|
||||||
match &self.last_frame {
|
match &self.last_frame {
|
||||||
Some(frame) => frame.clone(),
|
Some(frame) => frame.clone(),
|
||||||
None => {
|
None => return Err(Vp6Error::UnexpectedSkipFrame.into()),
|
||||||
return Err(Error::from(
|
|
||||||
"No previous frame found when encountering a skip frame",
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Actually decoding the frame and extracting the buffer it is stored in.
|
// Actually decoding the frame and extracting the buffer it is stored in.
|
||||||
|
@ -122,13 +131,11 @@ impl VideoDecoder for Vp6Decoder {
|
||||||
let decoded = self
|
let decoded = self
|
||||||
.decoder
|
.decoder
|
||||||
.decode_frame(&mut self.support, encoded_frame.data, &mut self.bitreader)
|
.decode_frame(&mut self.support, encoded_frame.data, &mut self.bitreader)
|
||||||
.map_err(|error| Error::from(format!("VP6 decoder error: {:?}", error)))?;
|
.map_err(Vp6Error::DecoderError)?;
|
||||||
|
|
||||||
let frame = match decoded {
|
let frame = match decoded {
|
||||||
(Video(buffer), _) => Ok(buffer),
|
(Video(buffer), _) => Ok(buffer),
|
||||||
_ => Err(Error::from(
|
_ => Err(Vp6Error::InvalidBufferType),
|
||||||
"Unexpected buffer type after decoding a VP6 frame",
|
|
||||||
)),
|
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
self.last_frame = Some(frame.clone());
|
self.last_frame = Some(frame.clone());
|
||||||
|
|
|
@ -8,7 +8,7 @@ use ruffle_render::backend::RenderBackend;
|
||||||
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle, BitmapInfo};
|
use ruffle_render::bitmap::{Bitmap, BitmapFormat, BitmapHandle, BitmapInfo};
|
||||||
use swf::{VideoCodec, VideoDeblocking};
|
use swf::{VideoCodec, VideoDeblocking};
|
||||||
|
|
||||||
mod decoders;
|
pub mod decoders;
|
||||||
|
|
||||||
#[cfg(feature = "h263")]
|
#[cfg(feature = "h263")]
|
||||||
use self::decoders::h263;
|
use self::decoders::h263;
|
||||||
|
@ -57,7 +57,7 @@ impl VideoBackend for SoftwareVideoBackend {
|
||||||
VideoCodec::Vp6WithAlpha => Box::new(vp6::Vp6Decoder::new(true, size)),
|
VideoCodec::Vp6WithAlpha => Box::new(vp6::Vp6Decoder::new(true, size)),
|
||||||
#[cfg(feature = "screenvideo")]
|
#[cfg(feature = "screenvideo")]
|
||||||
VideoCodec::ScreenVideo => Box::new(screen::ScreenVideoDecoder::new()),
|
VideoCodec::ScreenVideo => Box::new(screen::ScreenVideoDecoder::new()),
|
||||||
_ => return Err(format!("Unsupported video codec type {:?}", codec).into()),
|
other => return Err(Error::UnsupportedCodec(other)),
|
||||||
};
|
};
|
||||||
let stream = VideoStream::new(decoder);
|
let stream = VideoStream::new(decoder);
|
||||||
let stream_handle = self.streams.insert(stream);
|
let stream_handle = self.streams.insert(stream);
|
||||||
|
@ -72,7 +72,7 @@ impl VideoBackend for SoftwareVideoBackend {
|
||||||
let stream = self
|
let stream = self
|
||||||
.streams
|
.streams
|
||||||
.get_mut(stream)
|
.get_mut(stream)
|
||||||
.ok_or("Unregistered video stream")?;
|
.ok_or(Error::VideoStreamIsNotRegistered)?;
|
||||||
|
|
||||||
stream.decoder.preload_frame(encoded_frame)
|
stream.decoder.preload_frame(encoded_frame)
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ impl VideoBackend for SoftwareVideoBackend {
|
||||||
let stream = self
|
let stream = self
|
||||||
.streams
|
.streams
|
||||||
.get_mut(stream)
|
.get_mut(stream)
|
||||||
.ok_or("Unregistered video stream")?;
|
.ok_or(Error::VideoStreamIsNotRegistered)?;
|
||||||
|
|
||||||
let frame = stream.decoder.decode_frame(encoded_frame)?;
|
let frame = stream.decoder.decode_frame(encoded_frame)?;
|
||||||
let handle = if let Some(bitmap) = stream.bitmap {
|
let handle = if let Some(bitmap) = stream.bitmap {
|
||||||
|
|
|
@ -4,7 +4,7 @@ use crate::avm1::{Object as Avm1Object, StageObject as Avm1StageObject};
|
||||||
use crate::avm2::{
|
use crate::avm2::{
|
||||||
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
Activation as Avm2Activation, Object as Avm2Object, StageObject as Avm2StageObject,
|
||||||
};
|
};
|
||||||
use crate::backend::video::{EncodedFrame, VideoStreamHandle};
|
use crate::backend::video::{EncodedFrame, Error, VideoStreamHandle};
|
||||||
use crate::context::{RenderContext, UpdateContext};
|
use crate::context::{RenderContext, UpdateContext};
|
||||||
use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject};
|
use crate::display_object::{DisplayObjectBase, DisplayObjectPtr, TDisplayObject};
|
||||||
use crate::prelude::*;
|
use crate::prelude::*;
|
||||||
|
@ -257,10 +257,7 @@ impl<'gc> Video<'gc> {
|
||||||
if let Some((_old_id, old_frame)) = read.decoded_frame {
|
if let Some((_old_id, old_frame)) = read.decoded_frame {
|
||||||
Ok(old_frame)
|
Ok(old_frame)
|
||||||
} else {
|
} else {
|
||||||
Err(Box::from(format!(
|
Err(Error::SeekingBeforeDecoding(frame_id))
|
||||||
"Attempted to seek to omitted frame {} without prior decoded frame",
|
|
||||||
frame_id
|
|
||||||
)))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue