Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add VP6 video decoding by utilizing an external crate that bundles parts of FFmpeg #3004

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .github/workflows/release_nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,15 @@ jobs:
shell: bash -l {0}
run: conda install -c conda-forge binaryen

- name: Add msys to PATH for clang
if: matrix.os == 'Windows'
shell: bash -l {0}
run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH

- name: Setup Developer Command Prompt
if: matrix.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1

- name: Build web
env:
FIREFOX_EXTENSION_ID: ${{ secrets.FIREFOX_EXTENSION_ID }}
Expand Down
9 changes: 9 additions & 0 deletions .github/workflows/test_rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,15 @@ jobs:
sudo apt-get dist-upgrade
sudo apt install -y libasound2-dev libxcb-shape0-dev libxcb-xfixes0-dev mesa-vulkan-drivers

- name: Add msys to PATH for clang
if: matrix.os == 'windows-latest'
shell: bash -l {0}
run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH

- name: Setup Developer Command Prompt
if: matrix.os == 'windows-latest'
uses: ilammy/msvc-dev-cmd@v1

- name: Check formatting
uses: actions-rs/cargo@v1
with:
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/test_web.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,11 @@ jobs:
shell: bash -l {0}
run: conda install -c conda-forge binaryen

- name: Add msys to PATH for clang
if: matrix.os == 'windows-latest'
shell: bash -l {0}
run: echo "C:\msys64\mingw64\bin" >> $GITHUB_PATH

- name: Build
working-directory: web
shell: bash -l {0}
Expand Down
30 changes: 20 additions & 10 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ json = "0.12.4"
lzma-rs = {version = "0.2.0", optional = true }
dasp = { git = "https://github.com/RustAudio/dasp", rev = "f05a703", features = ["interpolate", "interpolate-linear", "signal"] }
symphonia = { version = "0.3.0", default-features = false, features = ["mp3"], optional = true }
vp6-dec-rs = { git = "https://github.com/torokati44/vp6-dec-rs", branch = "main", features = ["allow-lgpl"], optional = true }

[dependencies.jpeg-decoder]
version = "0.1.22"
Expand All @@ -54,6 +55,7 @@ approx = "0.5.0"
[features]
default = ["minimp3", "serde"]
h263 = ["h263-rs", "h263-rs-yuv"]
vp6 = ["vp6-dec-rs"]
lzma = ["lzma-rs", "swf/lzma"]
wasm-bindgen = [ "instant/wasm-bindgen" ]
avm_debug = []
89 changes: 88 additions & 1 deletion core/src/backend/video/software.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,17 @@ impl VideoBackend for SoftwareVideoBackend {
fn register_video_stream(
&mut self,
_num_frames: u32,
_size: (u16, u16),
size: (u16, u16),
codec: VideoCodec,
_filter: VideoDeblocking,
) -> Result<VideoStreamHandle, Error> {
let decoder: Box<dyn VideoDecoder> = match codec {
#[cfg(feature = "h263")]
VideoCodec::H263 => Box::new(h263::H263Decoder::new()),
#[cfg(feature = "vp6")]
VideoCodec::Vp6 => Box::new(vp6::Vp6Decoder::new(false, size)),
#[cfg(feature = "vp6")]
VideoCodec::Vp6WithAlpha => Box::new(vp6::Vp6Decoder::new(true, size)),
_ => return Err(format!("Unsupported video codec type {:?}", codec).into()),
};
let stream = VideoStream::new(decoder);
Expand Down Expand Up @@ -198,3 +202,86 @@ mod h263 {
}
}
}

#[cfg(feature = "vp6")]
mod vp6 {
use crate::backend::video::software::VideoDecoder;
use crate::backend::video::{DecodedFrame, EncodedFrame, Error, FrameDependency};
use vp6_dec_rs::Vp6State;

/// VP6 video decoder.
pub struct Vp6Decoder(Vp6State, (u16, u16));

impl Vp6Decoder {
pub fn new(with_alpha: bool, bounds: (u16, u16)) -> Self {
Self(Vp6State::new(with_alpha), bounds)
}
}

impl VideoDecoder for Vp6Decoder {
fn preload_frame(
&mut self,
encoded_frame: EncodedFrame<'_>,
) -> Result<FrameDependency, Error> {
// Luckily the very first bit of the encoded frames is exactly
// this flag, so we don't have to bother asking any "proper"
// decoder or parser.
Ok(
if !encoded_frame.data.is_empty() && (encoded_frame.data[0] & 0b_1000_0000) == 0 {
FrameDependency::None
} else {
FrameDependency::Past
},
)
}

fn decode_frame(&mut self, encoded_frame: EncodedFrame<'_>) -> Result<DecodedFrame, Error> {
let (mut rgba, (mut width, mut height)) = self.0.decode(encoded_frame.data);
let &bounds = &self.1;

if width < bounds.0 as usize || height < bounds.1 as usize {
log::warn!("A VP6 video frame is smaller than the bounds of the stream it belongs in. This is not supported.");
// Flash Player just produces a black image in this case!
}

if width > bounds.0 as usize {
// Removing the unwanted pixels on the right edge (most commonly: unused pieces of macroblocks)
// by squishing all the rows tightly next to each other.
// Even though the vp6 decoder in FFmpeg could do this trimming on its own (accepts parameters
// for it in the extradata field), the swscale colorspace conversion would still have to be done
// into a frame that has a stride which is a multiple of 32 or 64 bytes (for performance reasons;
// otherwise it leaves the right edge blank) and that is often greater than the actual width*4,
// so there will be gaps between the rows in these cases.
// And Bitmap at the moment does not allow these gaps, so we need to remove them.
// Also dropping any unwanted rows on the bottom while we're at it.
let new_width = bounds.0 as usize;
let new_height = usize::min(height, bounds.1 as usize);
// no need to move the first row, nor any rows on the bottom that will end up being cropped entirely
for row in 1..new_height {
rgba.copy_within(
row * width * 4..(row * width + new_width) * 4,
row * new_width * 4,
);
}
width = new_width;
height = new_height;
}

// Cropping the unwanted rows on the bottom, also dropping any unused space at the end left by the squish above
height = usize::min(height, bounds.1 as usize);
rgba.truncate(width * height * 4);

Ok(DecodedFrame {
width: width as u16,
height: height as u16,
rgba,
})
}
}

impl Default for Vp6Decoder {
fn default() -> Self {
Self::new(false, (0, 0))
}
}
}
3 changes: 2 additions & 1 deletion desktop/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,13 @@ winapi = "0.3.9"
embed-resource = "1"

[features]
default = ["h263"]
default = ["h263", "vp6"]

# core features
avm_debug = ["ruffle_core/avm_debug"]
h263 = ["ruffle_core/h263"]
lzma = ["ruffle_core/lzma"]
vp6 = ["ruffle_core/vp6"]

# wgpu features
render_debug_labels = ["ruffle_render_wgpu/render_debug_labels"]
Expand Down
2 changes: 1 addition & 1 deletion web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ base64 = "0.13.0"
[dependencies.ruffle_core]
path = "../core"
default-features = false
features = ["h263", "serde", "wasm-bindgen"]
features = ["h263", "vp6", "serde", "wasm-bindgen"]

[dependencies.web-sys]
version = "0.3.50"
Expand Down