From 5c3367b29967d357e1de51b2dae4889fdb8229f1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=96R=C3=96K=20Attila?= Date: Thu, 27 Jun 2024 20:37:14 +0200 Subject: [PATCH 1/3] video/external: Move OpenH264 related stuff into the `openh264` decoder module --- desktop/src/player.rs | 6 +- tests/framework/src/options.rs | 6 +- video/external/src/backend.rs | 136 +---------------------- video/external/src/decoder/openh264.rs | 142 ++++++++++++++++++++++++- 4 files changed, 151 insertions(+), 139 deletions(-) diff --git a/desktop/src/player.rs b/desktop/src/player.rs index 5599986a552f..da3384cbfb21 100644 --- a/desktop/src/player.rs +++ b/desktop/src/player.rs @@ -228,9 +228,11 @@ impl ActivePlayer { if cfg!(feature = "external_video") && preferences.openh264_enabled() { #[cfg(feature = "external_video")] { - use ruffle_video_external::backend::ExternalVideoBackend; + use ruffle_video_external::{ + backend::ExternalVideoBackend, decoder::openh264::OpenH264Codec, + }; let openh264 = tokio::task::block_in_place(|| { - ExternalVideoBackend::load_openh264(&opt.cache_directory.join("video")) + OpenH264Codec::load(&opt.cache_directory.join("video")) }); let openh264 = match openh264 { Ok(codec) => Some(codec), diff --git a/tests/framework/src/options.rs b/tests/framework/src/options.rs index 7258a75f740d..0880ad415de6 100644 --- a/tests/framework/src/options.rs +++ b/tests/framework/src/options.rs @@ -180,8 +180,10 @@ impl PlayerOptions { let current_exe = std::env::current_exe()?; let directory = current_exe.parent().expect("Executable parent dir"); - use ruffle_video_external::backend::ExternalVideoBackend; - let openh264 = ExternalVideoBackend::load_openh264(directory) + use ruffle_video_external::{ + backend::ExternalVideoBackend, decoder::openh264::OpenH264Codec, + }; + let openh264 = OpenH264Codec::load(directory) .map_err(|e| anyhow!("Couldn't load OpenH264: {}", e))?; player_builder = diff --git a/video/external/src/backend.rs b/video/external/src/backend.rs index 7751ebe1af85..7908ad9c5bca 100644 --- a/video/external/src/backend.rs +++ b/video/external/src/backend.rs @@ -1,6 +1,6 @@ use crate::decoder::openh264::OpenH264Codec; use crate::decoder::VideoDecoder; -use bzip2::read::BzDecoder; + use ruffle_render::backend::RenderBackend; use ruffle_render::bitmap::{BitmapHandle, BitmapInfo, PixelRegion}; use ruffle_video::backend::VideoBackend; @@ -8,11 +8,8 @@ use ruffle_video::error::Error; use ruffle_video::frame::{EncodedFrame, FrameDependency}; use ruffle_video::VideoStreamHandle; use ruffle_video_software::backend::SoftwareVideoBackend; -use sha2::{Digest, Sha256}; use slotmap::SlotMap; -use std::fs::File; -use std::io::copy; -use std::path::{Path, PathBuf}; + use swf::{VideoCodec, VideoDeblocking}; enum ProxyOrStream { @@ -25,12 +22,6 @@ enum ProxyOrStream { Owned(VideoStream), } -struct OpenH264Data { - local_filenames: Vec<&'static str>, - download_filename: &'static str, - download_sha256: &'static str, -} - /// 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 { @@ -46,129 +37,6 @@ impl Default for ExternalVideoBackend { } impl ExternalVideoBackend { - fn get_openh264_data() -> Result> { - const OS: &str = std::env::consts::OS; - const ARCH: &str = std::env::consts::ARCH; - - let local_filenames = match OS { - "linux" => vec!["libopenh264.so.7", "libopenh264.so.2.4.1", "libopenh264.so"], - // TODO: investigate other OSes - _ => vec![], - }; - - // Source: https://github.com/cisco/openh264/releases/tag/v2.4.1 - let (download_filename, download_sha256) = match (OS, ARCH) { - ("linux", "x86") => ( - "libopenh264-2.4.1-linux32.7.so", - "b7cf0e407f99056d90cbf62787a34820a7595b2129b165319d50766e00a66704", - ), - ("linux", "x86_64") => ( - "libopenh264-2.4.1-linux64.7.so", - "1392d21466bc638e68151b716d5b2086d54cd812afd43253f1adb5b6e0185f51", - ), - ("linux", "arm") => ( - "libopenh264-2.4.1-linux-arm.7.so", - "fd1dfb27d30bb72e903c9d2b4c650104a4369d2e7ffe8a4a533e8db2e7e9b19e", - ), - ("linux", "aarch64") => ( - "libopenh264-2.4.1-linux-arm64.7.so", - "e8ea7e42855ceb4a90e7bd0b3abeba0c58b5f97166e8b0a30eefd58e099557a4", - ), - ("macos", "x86_64") => ( - "libopenh264-2.4.1-mac-x64.dylib", - "cc0ba518a63791c37571f3c851f0aa03a4fbda5410acc214ecd4f24f8d1c478e", - ), - ("macos", "aarch64") => ( - "libopenh264-2.4.1-mac-arm64.dylib", - "213ff93831cfa3dd6d7ad0c3a3403a6ceedf4ac1341e1278b5b869d42fefb496", - ), - ("windows", "x86") => ( - "openh264-2.4.1-win32.dll", - "83270149640469c994a62cc32a6d8c0413cd7b802b7f1f2f532159f5bdc1cedd", - ), - ("windows", "x86_64") => ( - "openh264-2.4.1-win64.dll", - "081b0c081480d177cbfddfbc90b1613640e702f875897b30d8de195cde73dd34", - ), - (os, arch) => return Err(format!("Unsupported OS/arch: {}/{}", os, arch).into()), - }; - - Ok(OpenH264Data { - local_filenames, - download_filename, - download_sha256, - }) - } - - fn download_openh264( - openh264_data: &OpenH264Data, - directory: &Path, - ) -> Result> { - // See the license at: https://www.openh264.org/BINARY_LICENSE.txt - const URL_BASE: &str = "http://ciscobinary.openh264.org/"; - const URL_SUFFIX: &str = ".bz2"; - - let (filename, sha256sum) = ( - openh264_data.download_filename, - openh264_data.download_sha256, - ); - - std::fs::create_dir_all(directory)?; - let filepath = directory.join(filename); - - // If the binary doesn't exist in the expected location, download it. - if !filepath.is_file() { - let url = format!("{}{}{}", URL_BASE, filename, URL_SUFFIX); - let response = reqwest::blocking::get(url)?; - let mut bzip2_reader = BzDecoder::new(response); - - let mut tempfile = tempfile::NamedTempFile::with_prefix_in(filename, directory)?; - copy(&mut bzip2_reader, &mut tempfile)?; - // Let's assume that if this fails, it's because another process has already put it there - // and loaded it, therefore it can't be overwritten (on Windows at least), but in the end, - // all's fine - the hash will still be checked before attempting to load the library. - let _ = tempfile.persist(&filepath); - } - - // Regardless of whether the library was already there, or we just downloaded it, let's check the MD5 hash. - let mut sha256 = Sha256::new(); - copy(&mut File::open(filepath.clone())?, &mut sha256)?; - let sha256digest = sha256.finalize(); - let result: [u8; 32] = sha256digest.into(); - - if result[..] != hex::decode(sha256sum)?[..] { - let size = filepath.metadata().map(|f| f.len()).unwrap_or_default(); - return Err(format!( - "SHA256 checksum mismatch for {filename}; expected {sha256sum}, found {sha256digest:x} (with a size of {size} bytes)", - ) - .into()); - } - - Ok(filepath) - } - - pub fn load_openh264(directory: &Path) -> Result> { - let openh264_data = Self::get_openh264_data()?; - - for filename in &openh264_data.local_filenames { - match OpenH264Codec::new(filename) { - Ok(codec) => return Ok(codec), - Err(err) => { - tracing::warn!( - "Failed to load system OpenH264 library {}: {}", - filename, - err - ); - } - } - } - - tracing::info!("Downloading OpenH264 library"); - let filename = Self::download_openh264(&openh264_data, directory)?; - tracing::info!("Using OpenH264 at {:?}", filename); - Ok(OpenH264Codec::new(&filename)?) - } - pub fn new(openh264_codec: Option) -> Self { Self { streams: SlotMap::with_key(), diff --git a/video/external/src/decoder/openh264.rs b/video/external/src/decoder/openh264.rs index 3f06fdbc5f27..5091c6262853 100644 --- a/video/external/src/decoder/openh264.rs +++ b/video/external/src/decoder/openh264.rs @@ -1,6 +1,9 @@ use core::slice; use std::ffi::{c_int, c_uchar}; use std::fmt::Display; +use std::fs::File; +use std::io::copy; +use std::path::{Path, PathBuf}; use std::ptr; use std::sync::Arc; @@ -10,6 +13,9 @@ use crate::decoder::VideoDecoder; use ruffle_render::bitmap::BitmapFormat; use ruffle_video::error::Error; use ruffle_video::frame::{DecodedFrame, EncodedFrame, FrameDependency}; + +use bzip2::read::BzDecoder; +use sha2::{Digest, Sha256}; use thiserror::Error; #[derive(Debug, PartialEq, Eq)] @@ -39,7 +45,111 @@ pub struct OpenH264Codec { impl OpenH264Codec { const VERSION: OpenH264Version = OpenH264Version(2, 4, 1); - pub fn new

(filename: P) -> Result + /// Returns the OpenH264 library data for the current platform. + fn get_data() -> Result> { + const OS: &str = std::env::consts::OS; + const ARCH: &str = std::env::consts::ARCH; + + let local_filenames = match OS { + "linux" => vec!["libopenh264.so.7", "libopenh264.so.2.4.1", "libopenh264.so"], + // TODO: investigate other OSes + _ => vec![], + }; + + // Source: https://github.com/cisco/openh264/releases/tag/v2.4.1 + let (download_filename, download_sha256) = match (OS, ARCH) { + ("linux", "x86") => ( + "libopenh264-2.4.1-linux32.7.so", + "b7cf0e407f99056d90cbf62787a34820a7595b2129b165319d50766e00a66704", + ), + ("linux", "x86_64") => ( + "libopenh264-2.4.1-linux64.7.so", + "1392d21466bc638e68151b716d5b2086d54cd812afd43253f1adb5b6e0185f51", + ), + ("linux", "arm") => ( + "libopenh264-2.4.1-linux-arm.7.so", + "fd1dfb27d30bb72e903c9d2b4c650104a4369d2e7ffe8a4a533e8db2e7e9b19e", + ), + ("linux", "aarch64") => ( + "libopenh264-2.4.1-linux-arm64.7.so", + "e8ea7e42855ceb4a90e7bd0b3abeba0c58b5f97166e8b0a30eefd58e099557a4", + ), + ("macos", "x86_64") => ( + "libopenh264-2.4.1-mac-x64.dylib", + "cc0ba518a63791c37571f3c851f0aa03a4fbda5410acc214ecd4f24f8d1c478e", + ), + ("macos", "aarch64") => ( + "libopenh264-2.4.1-mac-arm64.dylib", + "213ff93831cfa3dd6d7ad0c3a3403a6ceedf4ac1341e1278b5b869d42fefb496", + ), + ("windows", "x86") => ( + "openh264-2.4.1-win32.dll", + "83270149640469c994a62cc32a6d8c0413cd7b802b7f1f2f532159f5bdc1cedd", + ), + ("windows", "x86_64") => ( + "openh264-2.4.1-win64.dll", + "081b0c081480d177cbfddfbc90b1613640e702f875897b30d8de195cde73dd34", + ), + (os, arch) => return Err(format!("Unsupported OS/arch: {}/{}", os, arch).into()), + }; + + Ok(OpenH264Data { + local_filenames, + download_filename, + download_sha256, + }) + } + + /// Downloads the OpenH264 library if it doesn't exist yet, and verifies its SHA256 hash. + fn fetch_and_verify( + openh264_data: &OpenH264Data, + directory: &Path, + ) -> Result> { + // See the license at: https://www.openh264.org/BINARY_LICENSE.txt + const URL_BASE: &str = "http://ciscobinary.openh264.org/"; + const URL_SUFFIX: &str = ".bz2"; + + let (filename, sha256sum) = ( + openh264_data.download_filename, + openh264_data.download_sha256, + ); + + std::fs::create_dir_all(directory)?; + let filepath = directory.join(filename); + + // If the binary doesn't exist in the expected location, download it. + if !filepath.is_file() { + let url = format!("{}{}{}", URL_BASE, filename, URL_SUFFIX); + let response = reqwest::blocking::get(url)?; + let mut bzip2_reader = BzDecoder::new(response); + + let mut tempfile = tempfile::NamedTempFile::with_prefix_in(filename, directory)?; + copy(&mut bzip2_reader, &mut tempfile)?; + // Let's assume that if this fails, it's because another process has already put it there + // and loaded it, therefore it can't be overwritten (on Windows at least), but in the end, + // all's fine - the hash will still be checked before attempting to load the library. + let _ = tempfile.persist(&filepath); + } + + // Regardless of whether the library was already there, or we just downloaded it, let's check the MD5 hash. + let mut sha256 = Sha256::new(); + copy(&mut File::open(filepath.clone())?, &mut sha256)?; + let sha256digest = sha256.finalize(); + let result: [u8; 32] = sha256digest.into(); + + if result[..] != hex::decode(sha256sum)?[..] { + let size = filepath.metadata().map(|f| f.len()).unwrap_or_default(); + return Err(format!( + "SHA256 checksum mismatch for {filename}; expected {sha256sum}, found {sha256digest:x} (with a size of {size} bytes)", + ) + .into()); + } + + Ok(filepath) + } + + /// Loads an existing OpenH264 library from the given path. + fn load_existing

(filename: P) -> Result where P: AsRef<::std::ffi::OsStr>, { @@ -56,6 +166,30 @@ impl OpenH264Codec { openh264: Arc::new(openh264), }) } + + /// Loads the OpenH264 library - first trying one installed on the system (on supported platforms), + /// then falling back to a local file in `directory`, downloading it into there if necessary. + pub fn load(directory: &Path) -> Result> { + let openh264_data = Self::get_data()?; + + for filename in &openh264_data.local_filenames { + match OpenH264Codec::load_existing(filename) { + Ok(codec) => return Ok(codec), + Err(err) => { + tracing::warn!( + "Failed to load system OpenH264 library {}: {}", + filename, + err + ); + } + } + } + + tracing::info!("Downloading OpenH264 library"); + let filename = Self::fetch_and_verify(&openh264_data, directory)?; + tracing::info!("Using OpenH264 at {:?}", filename); + Ok(OpenH264Codec::load_existing(&filename)?) + } } /// H264 video decoder. @@ -67,6 +201,12 @@ pub struct H264Decoder { decoder: *mut ISVCDecoder, } +struct OpenH264Data { + local_filenames: Vec<&'static str>, + download_filename: &'static str, + download_sha256: &'static str, +} + impl H264Decoder { /// `extradata` should hold "AVCC (MP4) format" decoder configuration, including PPS and SPS. /// Make sure it has any start code emulation prevention "three bytes" removed. From 9559671d6f96b70d25c8dcf657990083689254ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=96R=C3=96K=20Attila?= Date: Thu, 12 Sep 2024 19:02:40 +0200 Subject: [PATCH 2/3] video/external: Make OpenH264 support optional by adding the `openh264` feature And enable it in desktop and tests. --- desktop/Cargo.toml | 2 +- desktop/src/player.rs | 9 ++++---- tests/framework/Cargo.toml | 2 +- tests/framework/src/options.rs | 2 +- video/external/Cargo.toml | 15 ++++++++------ video/external/src/backend.rs | 38 +++++++++++++++++++++++++--------- video/external/src/decoder.rs | 2 ++ 7 files changed, 46 insertions(+), 24 deletions(-) diff --git a/desktop/Cargo.toml b/desktop/Cargo.toml index 0f8967131f2e..23956aca115c 100644 --- a/desktop/Cargo.toml +++ b/desktop/Cargo.toml @@ -23,7 +23,7 @@ ruffle_core = { path = "../core", features = ["audio", "clap", "mp3", "nellymose ruffle_render = { path = "../render", features = ["clap"] } ruffle_render_wgpu = { path = "../render/wgpu", features = ["clap"] } ruffle_video_software = { path = "../video/software", optional = true } -ruffle_video_external = { path = "../video/external", optional = true } +ruffle_video_external = { path = "../video/external", features = ["openh264"], optional = true } ruffle_frontend_utils = { path = "../frontend-utils", features = ["cpal"] } tracing = { workspace = true } tracing-subscriber = { workspace = true } diff --git a/desktop/src/player.rs b/desktop/src/player.rs index da3384cbfb21..9f4b724e7ec9 100644 --- a/desktop/src/player.rs +++ b/desktop/src/player.rs @@ -234,15 +234,14 @@ impl ActivePlayer { let openh264 = tokio::task::block_in_place(|| { OpenH264Codec::load(&opt.cache_directory.join("video")) }); - let openh264 = match openh264 { - Ok(codec) => Some(codec), + let backend = match openh264 { + Ok(codec) => ExternalVideoBackend::new_with_openh264(codec), Err(e) => { tracing::error!("Failed to load OpenH264: {}", e); - None + ExternalVideoBackend::new() } }; - - builder = builder.with_video(ExternalVideoBackend::new(openh264)); + builder = builder.with_video(backend); } } else { #[cfg(feature = "software_video")] diff --git a/tests/framework/Cargo.toml b/tests/framework/Cargo.toml index 63dbc2d4cb7c..164726fdda63 100644 --- a/tests/framework/Cargo.toml +++ b/tests/framework/Cargo.toml @@ -16,7 +16,7 @@ ruffle_render = { path = "../../render", features = ["serde"] } ruffle_input_format = { path = "../input-format" } ruffle_socket_format = { path = "../socket-format" } ruffle_video_software = { path = "../../video/software", optional = true } -ruffle_video_external = { path = "../../video/external", optional = true } +ruffle_video_external = { path = "../../video/external", features = ["openh264"], optional = true } image = { workspace = true, features = ["png"] } regex = "1.10.6" url = { workspace = true } diff --git a/tests/framework/src/options.rs b/tests/framework/src/options.rs index 0880ad415de6..44f1102bd7fd 100644 --- a/tests/framework/src/options.rs +++ b/tests/framework/src/options.rs @@ -187,7 +187,7 @@ impl PlayerOptions { .map_err(|e| anyhow!("Couldn't load OpenH264: {}", e))?; player_builder = - player_builder.with_video(ExternalVideoBackend::new(Some(openh264))); + player_builder.with_video(ExternalVideoBackend::new_with_openh264(openh264)); } #[cfg(all( diff --git a/video/external/Cargo.toml b/video/external/Cargo.toml index 1af1d2866641..ec20b89f1142 100644 --- a/video/external/Cargo.toml +++ b/video/external/Cargo.toml @@ -17,9 +17,12 @@ ruffle_video_software = { path = "../software" } thiserror = { workspace = true } # Needed for OpenH264: -libloading = "0.8.5" -reqwest = { version = "0.12.7", default-features = false, features = ["blocking"] } -hex = "0.4.3" -bzip2 = { version = "0.4.4", features = ["static"] } -tempfile = "3.12.0" -sha2 = "0.10.8" +libloading = { version = "0.8.5", optional = true } +reqwest = { version = "0.12.7", default-features = false, features = ["blocking"], optional = true } +hex = { version = "0.4.3", optional = true } +bzip2 = { version = "0.4.4", features = ["static"], optional = true } +tempfile = { version = "3.12.0", optional = true } +sha2 = { version = "0.10.8", optional = true } + +[features] +openh264 = ["libloading", "reqwest", "hex", "bzip2", "tempfile", "sha2"] diff --git a/video/external/src/backend.rs b/video/external/src/backend.rs index 7908ad9c5bca..e20bb56dc231 100644 --- a/video/external/src/backend.rs +++ b/video/external/src/backend.rs @@ -1,3 +1,4 @@ +#[cfg(feature = "openh264")] use crate::decoder::openh264::OpenH264Codec; use crate::decoder::VideoDecoder; @@ -26,21 +27,42 @@ enum ProxyOrStream { /// except for H.264, for which it uses an external decoder. pub struct ExternalVideoBackend { streams: SlotMap, + #[cfg(feature = "openh264")] openh264_codec: Option, software: SoftwareVideoBackend, } impl Default for ExternalVideoBackend { fn default() -> Self { - Self::new(None) + Self::new() } } impl ExternalVideoBackend { - pub fn new(openh264_codec: Option) -> Self { + fn make_decoder(&mut self) -> Result, Error> { + #[cfg(feature = "openh264")] + if let Some(h264_codec) = self.openh264_codec.as_ref() { + let decoder = Box::new(crate::decoder::openh264::H264Decoder::new(h264_codec)); + return Ok(decoder); + } + + Err(Error::DecoderError("No OpenH264".into())) + } + + pub fn new() -> Self { Self { streams: SlotMap::with_key(), - openh264_codec, + #[cfg(feature = "openh264")] + openh264_codec: None, + software: SoftwareVideoBackend::new(), + } + } + + #[cfg(feature = "openh264")] + pub fn new_with_openh264(openh264_codec: OpenH264Codec) -> Self { + Self { + streams: SlotMap::with_key(), + openh264_codec: Some(openh264_codec), software: SoftwareVideoBackend::new(), } } @@ -57,13 +79,9 @@ impl VideoBackend for ExternalVideoBackend { filter: VideoDeblocking, ) -> Result { let proxy_or_stream = if codec == VideoCodec::H264 { - if let Some(h264_codec) = self.openh264_codec.as_ref() { - let decoder = Box::new(crate::decoder::openh264::H264Decoder::new(h264_codec)); - let stream = VideoStream::new(decoder); - ProxyOrStream::Owned(stream) - } else { - return Err(Error::DecoderError("No OpenH264".into())); - } + let decoder = self.make_decoder()?; + let stream = VideoStream::new(decoder); + ProxyOrStream::Owned(stream) } else { ProxyOrStream::Proxied( self.software diff --git a/video/external/src/decoder.rs b/video/external/src/decoder.rs index 7f62962e090b..d7169e5b894f 100644 --- a/video/external/src/decoder.rs +++ b/video/external/src/decoder.rs @@ -1,11 +1,13 @@ // bindgen ../openh264/codec/api/wels/codec_api.h --no-prepend-enum-name \ // --dynamic-loading OpenH264 -o openh264_sys.rs +#[cfg(feature = "openh264")] #[allow(non_upper_case_globals)] #[allow(non_camel_case_types)] #[allow(non_snake_case)] #[allow(dead_code)] mod openh264_sys; +#[cfg(feature = "openh264")] pub mod openh264; pub use ruffle_video_software::decoder::VideoDecoder; From e9c0145b2e0b46cadf8626bd24c55e2b05df2c35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=96R=C3=96K=20Attila?= Date: Thu, 18 Jul 2024 04:21:33 +0200 Subject: [PATCH 3/3] chore: Make web-sys a workspace-level dependency Thereby deduplicating the version number of it. --- Cargo.toml | 1 + render/canvas/Cargo.toml | 2 +- render/webgl/Cargo.toml | 2 +- render/wgpu/Cargo.toml | 2 +- web/Cargo.toml | 2 +- 5 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index eb43abf4aa92..62ece68e2a72 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -64,6 +64,7 @@ flate2 = "1.0.33" futures = "0.3.30" image = { version = "0.25.2", default-features = false } js-sys = "0.3.70" +web-sys = "0.3.70" log = "0.4" num-derive = "0.4.2" num-traits = "0.2.19" diff --git a/render/canvas/Cargo.toml b/render/canvas/Cargo.toml index 15a228225b2d..3f3877ad1d33 100644 --- a/render/canvas/Cargo.toml +++ b/render/canvas/Cargo.toml @@ -19,7 +19,7 @@ ruffle_render = { path = "..", features = ["web"] } swf = { path = "../../swf" } [dependencies.web-sys] -version = "0.3.69" +workspace = true features = [ "CanvasGradient", "CanvasPattern", "CanvasRenderingContext2d", "CanvasWindingRule", "CssStyleDeclaration", "Document", "DomMatrix", "Element", "HtmlCanvasElement", "ImageData", "Navigator", "Path2d", "SvgMatrix", diff --git a/render/webgl/Cargo.toml b/render/webgl/Cargo.toml index 9887fe1eb3f6..0bcb12f834f1 100644 --- a/render/webgl/Cargo.toml +++ b/render/webgl/Cargo.toml @@ -21,7 +21,7 @@ swf = { path = "../../swf" } thiserror = { workspace = true } [dependencies.web-sys] -version = "0.3.70" +workspace = true features = [ "HtmlCanvasElement", "OesVertexArrayObject", "WebGl2RenderingContext", "WebGlBuffer", "WebglDebugRendererInfo", "WebGlFramebuffer", "WebGlProgram", "WebGlRenderbuffer", "WebGlRenderingContext", "WebGlShader", "WebGlTexture", diff --git a/render/wgpu/Cargo.toml b/render/wgpu/Cargo.toml index ad7370dcff03..1771ee04293d 100644 --- a/render/wgpu/Cargo.toml +++ b/render/wgpu/Cargo.toml @@ -33,7 +33,7 @@ workspace = true # wasm [target.'cfg(target_family = "wasm")'.dependencies.web-sys] -version = "0.3.70" +workspace = true features = ["HtmlCanvasElement"] [features] diff --git a/web/Cargo.toml b/web/Cargo.toml index 4cb15596188c..4a7a1f7927d3 100644 --- a/web/Cargo.toml +++ b/web/Cargo.toml @@ -66,7 +66,7 @@ path = "../core" features = ["audio", "mp3", "nellymoser", "default_compatibility_rules", "default_font", "serde"] [dependencies.web-sys] -version = "0.3.70" +workspace = true features = [ "AddEventListenerOptions", "AudioBuffer", "AudioBufferSourceNode", "AudioContext", "AudioDestinationNode", "AudioNode", "AudioParam", "Blob", "BlobPropertyBag",