From fe2a264a9334c3470017e6fe005d0969077f6ca6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=96R=C3=96K=20Attila?= Date: Thu, 21 Dec 2023 12:58:57 +0100 Subject: [PATCH] video: Add "external" video backend --- Cargo.lock | 11 +++ Cargo.toml | 1 + video/external/Cargo.toml | 15 ++++ video/external/src/backend.rs | 137 ++++++++++++++++++++++++++++++++++ video/external/src/decoder.rs | 1 + video/external/src/lib.rs | 2 + 6 files changed, 167 insertions(+) create mode 100644 video/external/Cargo.toml create mode 100644 video/external/src/backend.rs create mode 100644 video/external/src/decoder.rs create mode 100644 video/external/src/lib.rs diff --git a/Cargo.lock b/Cargo.lock index 588df2b23..e277192ac 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4537,6 +4537,17 @@ dependencies = [ "thiserror", ] +[[package]] +name = "ruffle_video_external" +version = "0.1.0" +dependencies = [ + "ruffle_render", + "ruffle_video", + "ruffle_video_software", + "slotmap", + "swf", +] + [[package]] name = "ruffle_video_software" version = "0.1.0" diff --git a/Cargo.toml b/Cargo.toml index b96825c2b..bd46508e2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,6 +26,7 @@ members = [ "video", "video/software", + "video/external", "tests", "tests/input-format", diff --git a/video/external/Cargo.toml b/video/external/Cargo.toml new file mode 100644 index 000000000..daa1128c9 --- /dev/null +++ b/video/external/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "ruffle_video_external" +authors.workspace = true +edition.workspace = true +homepage.workspace = true +license.workspace = true +repository.workspace = true +version.workspace = true + +[dependencies] +ruffle_render = { path = "../../render" } +ruffle_video = { path = ".." } +swf = { path = "../../swf" } +slotmap = { workspace = true } +ruffle_video_software = { path = "../software" } diff --git a/video/external/src/backend.rs b/video/external/src/backend.rs new file mode 100644 index 000000000..c11683cd7 --- /dev/null +++ b/video/external/src/backend.rs @@ -0,0 +1,137 @@ +use crate::decoder::VideoDecoder; +use ruffle_render::backend::RenderBackend; +use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, PixelRegion}; +use ruffle_video::backend::VideoBackend; +use ruffle_video::error::Error; +use ruffle_video::frame::{EncodedFrame, FrameDependency}; +use ruffle_video::VideoStreamHandle; +use ruffle_video_software::backend::SoftwareVideoBackend; +use slotmap::SlotMap; +use swf::{VideoCodec, VideoDeblocking}; + +enum ProxyOrStream { + /// These streams are passed through to the wrapped software + /// backend, accessed using the stored ("inner") handle, + /// which is completely internal to this backend. + Proxied(VideoStreamHandle), + + /// These streams are handled by this backend directly. + Owned(VideoStream), +} + +/// A video backend that falls back to the software backend for most codecs, +/// except for H.264, for which it uses an external decoder. +pub struct ExternalVideoBackend { + streams: SlotMap, + software: SoftwareVideoBackend, +} + +impl Default for ExternalVideoBackend { + fn default() -> Self { + Self::new() + } +} + +impl ExternalVideoBackend { + pub fn new() -> Self { + Self { + streams: SlotMap::with_key(), + software: SoftwareVideoBackend::new(), + } + } +} + +// NOTE: The stream handles coming in through this API must not be +// conflated with the ones stored in `streams` as `Proxied`. +impl VideoBackend for ExternalVideoBackend { + fn register_video_stream( + &mut self, + num_frames: u32, + size: (u16, u16), + codec: VideoCodec, + filter: VideoDeblocking, + ) -> Result { + let proxy_or_stream = if codec == VideoCodec::H264 { + todo!(); + } else { + ProxyOrStream::Proxied( + self.software + .register_video_stream(num_frames, size, codec, filter)?, + ) + }; + + Ok(self.streams.insert(proxy_or_stream)) + } + + fn preload_video_stream_frame( + &mut self, + stream: VideoStreamHandle, + encoded_frame: EncodedFrame<'_>, + ) -> Result { + let stream = self + .streams + .get_mut(stream) + .ok_or(Error::VideoStreamIsNotRegistered)?; + + match stream { + ProxyOrStream::Proxied(handle) => self + .software + .preload_video_stream_frame(*handle, encoded_frame), + ProxyOrStream::Owned(stream) => stream.decoder.preload_frame(encoded_frame), + } + } + + fn decode_video_stream_frame( + &mut self, + stream: VideoStreamHandle, + encoded_frame: EncodedFrame<'_>, + renderer: &mut dyn RenderBackend, + ) -> Result { + let stream = self + .streams + .get_mut(stream) + .ok_or(Error::VideoStreamIsNotRegistered)?; + + match stream { + ProxyOrStream::Proxied(handle) => { + self.software + .decode_video_stream_frame(*handle, encoded_frame, renderer) + } + ProxyOrStream::Owned(stream) => { + let frame = stream.decoder.decode_frame(encoded_frame)?; + + let w = frame.width(); + let h = frame.height(); + + let handle = if let Some(bitmap) = stream.bitmap.clone() { + renderer.update_texture(&bitmap, frame, PixelRegion::for_whole_size(w, h))?; + bitmap + } else { + renderer.register_bitmap(frame)? + }; + stream.bitmap = Some(handle.clone()); + + Ok(BitmapInfo { + handle, + width: w as u16, + height: h as u16, + }) + } + } + } +} + +/// A single preloaded video stream. +pub struct VideoStream { + bitmap: Option, + decoder: Box, +} + +impl VideoStream { + fn new(decoder: Box) -> Self { + Self { + decoder, + bitmap: None, + } + } +} diff --git a/video/external/src/decoder.rs b/video/external/src/decoder.rs new file mode 100644 index 000000000..7b2d00b45 --- /dev/null +++ b/video/external/src/decoder.rs @@ -0,0 +1 @@ +pub use ruffle_video_software::decoder::VideoDecoder; diff --git a/video/external/src/lib.rs b/video/external/src/lib.rs new file mode 100644 index 000000000..27f7c4208 --- /dev/null +++ b/video/external/src/lib.rs @@ -0,0 +1,2 @@ +pub mod backend; +pub mod decoder;