desktop: Add a preference to enable the OpenH264 decoder

This commit is contained in:
TÖRÖK Attila 2024-02-23 19:22:20 +01:00 committed by Nathan Adams
parent 381b77cda0
commit 76da9621c9
10 changed files with 158 additions and 11 deletions

1
Cargo.lock generated
View File

@ -4363,6 +4363,7 @@ dependencies = [
"ruffle_frontend_utils",
"ruffle_render",
"ruffle_render_wgpu",
"ruffle_video_external",
"ruffle_video_software",
"sys-locale",
"tokio",

View File

@ -23,6 +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_frontend_utils = { path = "../frontend-utils" }
tracing = { workspace = true }
tracing-subscriber = { workspace = true }
@ -57,13 +58,14 @@ embed-resource = "2"
vergen = { version = "8.3.1", features = ["build", "git", "gitcl", "cargo"] }
[features]
default = ["software_video"]
default = ["software_video", "external_video"]
jpegxr = ["ruffle_core/jpegxr"]
# core features
avm_debug = ["ruffle_core/avm_debug"]
lzma = ["ruffle_core/lzma"]
software_video = ["ruffle_video_software"]
external_video = ["ruffle_video_external"]
tracy = ["tracing-tracy", "ruffle_render_wgpu/profile-with-tracy"]
# wgpu features

View File

@ -14,6 +14,10 @@ language = Language
audio-output-device = Audio Output Device
audio-output-device-default = System Default
enable-openh264 = Enable OpenH264
show-license = Show License
openh264-license = OpenH264 License
log-filename-pattern = Log Filename
log-filename-pattern-single-file = Single File (ruffle.log)
log-filename-pattern-with-timestamp = With Timestamp

View File

@ -2,7 +2,7 @@ use crate::gui::{available_languages, optional_text, text};
use crate::log::FilenamePattern;
use crate::preferences::{storage::StorageBackend, GlobalPreferences};
use cpal::traits::{DeviceTrait, HostTrait};
use egui::{Align2, Button, ComboBox, DragValue, Grid, Ui, Widget, Window};
use egui::{Align2, Button, Checkbox, ComboBox, DragValue, Grid, Ui, Widget, Window};
use ruffle_render_wgpu::clap::{GraphicsBackend, PowerPreference};
use std::borrow::Cow;
use unic_langid::LanguageIdentifier;
@ -26,6 +26,10 @@ pub struct PreferencesDialog {
available_output_devices: Vec<String>,
output_device_changed: bool,
enable_openh264: bool,
enable_openh264_changed: bool,
openh264_license_visible: bool,
recent_limit: usize,
recent_limit_changed: bool,
@ -68,6 +72,10 @@ impl PreferencesDialog {
available_output_devices,
output_device_changed: false,
enable_openh264: preferences.openh264_enabled(),
enable_openh264_changed: false,
openh264_license_visible: false,
recent_limit: preferences.recent_limit(),
recent_limit_changed: false,
@ -104,6 +112,8 @@ impl PreferencesDialog {
self.show_audio_preferences(locale, ui);
self.show_video_preferences(egui_ctx, locale, ui);
self.show_log_preferences(locale, ui);
self.show_storage_preferences(locale, &locked_text, ui);
@ -136,6 +146,7 @@ impl PreferencesDialog {
self.graphics_backend != self.preferences.graphics_backends()
|| self.power_preference != self.preferences.graphics_power_preference()
|| self.output_device != self.preferences.output_device_name()
|| self.enable_openh264 != self.preferences.openh264_enabled()
|| self.log_filename_pattern != self.preferences.log_filename_pattern()
|| self.storage_backend != self.preferences.storage_backend()
}
@ -261,6 +272,44 @@ impl PreferencesDialog {
ui.end_row();
}
fn show_video_preferences(
&mut self,
egui_ctx: &egui::Context,
locale: &LanguageIdentifier,
ui: &mut Ui,
) {
#[cfg(feature = "external_video")]
{
ui.label(text(locale, "enable-openh264"));
let previous = self.enable_openh264;
ui.add(Checkbox::without_text(&mut self.enable_openh264));
ui.end_row();
ui.small("OpenH264 Video Codec provided by Cisco Systems, Inc.");
if self.enable_openh264 != previous {
self.enable_openh264_changed = true;
}
if ui.small_button(text(locale, "show-license")).clicked() {
self.openh264_license_visible = true;
};
let available_size = egui_ctx.available_rect().size();
egui::Window::new(text(locale, "openh264-license"))
.collapsible(false)
.resizable(false)
.anchor(Align2::CENTER_CENTER, egui::Vec2::ZERO)
.scroll(true)
.open(&mut self.openh264_license_visible)
.min_size(available_size * 0.8)
.max_size(available_size * 0.9)
.show(egui_ctx, |ui| {
// Source: https://www.openh264.org/BINARY_LICENSE.txt
ui.monospace(include_str!("../../../assets/OpenH264-license.txt"));
});
ui.end_row();
}
}
fn show_log_preferences(&mut self, locale: &LanguageIdentifier, ui: &mut Ui) {
ui.label(text(locale, "log-filename-pattern"));
@ -359,6 +408,9 @@ impl PreferencesDialog {
preferences.set_output_device(self.output_device.clone());
// [NA] TODO: Inform the running player that the device changed
}
if self.enable_openh264_changed {
preferences.set_enable_openh264(self.enable_openh264);
}
if self.log_filename_pattern_changed {
preferences.set_log_filename_pattern(self.log_filename_pattern);
}

View File

@ -220,9 +220,27 @@ impl ActivePlayer {
RfdNavigatorInterface,
);
if cfg!(feature = "software_video") {
builder =
builder.with_video(ruffle_video_software::backend::SoftwareVideoBackend::new());
if cfg!(feature = "external_video") && preferences.openh264_enabled() {
#[cfg(feature = "external_video")]
{
use ruffle_video_external::backend::ExternalVideoBackend;
let path = tokio::task::block_in_place(ExternalVideoBackend::get_openh264);
let openh264_path = match path {
Ok(path) => Some(path),
Err(e) => {
tracing::error!("Couldn't get OpenH264: {}", e);
None
}
};
builder = builder.with_video(ExternalVideoBackend::new(openh264_path));
}
} else {
#[cfg(feature = "software_video")]
{
builder =
builder.with_video(ruffle_video_software::backend::SoftwareVideoBackend::new());
}
}
let renderer = WgpuRenderBackend::new(descriptors, movie_view)

View File

@ -145,6 +145,13 @@ impl GlobalPreferences {
})
}
pub fn openh264_enabled(&self) -> bool {
self.preferences
.lock()
.expect("Preferences is not reentrant")
.enable_openh264
}
pub fn log_filename_pattern(&self) -> FilenamePattern {
self.preferences
.lock()
@ -229,6 +236,7 @@ pub struct SavedGlobalPreferences {
pub output_device: Option<String>,
pub mute: bool,
pub volume: f32,
pub enable_openh264: bool,
pub recent_limit: usize,
pub log: LogPreferences,
pub storage: StoragePreferences,
@ -247,6 +255,7 @@ impl Default for SavedGlobalPreferences {
output_device: None,
mute: false,
volume: 1.0,
enable_openh264: true,
recent_limit: 10,
log: Default::default(),
storage: Default::default(),

View File

@ -51,6 +51,10 @@ pub fn read_preferences(input: &str) -> ParseDetails<SavedGlobalPreferences> {
result.mute = value;
};
if let Some(value) = document.get_bool(&mut cx, "enable_openh264") {
result.enable_openh264 = value;
};
if let Some(value) = document.get_integer(&mut cx, "recent_limit") {
result.recent_limit = value as usize;
}
@ -327,6 +331,46 @@ mod tests {
assert_eq!(Vec::<ParseWarning>::new(), result.warnings);
}
#[test]
fn enable_openh264() {
let result = read_preferences("enable_openh264 = \"true\"");
assert_eq!(
&SavedGlobalPreferences {
enable_openh264: true,
..Default::default()
},
result.values()
);
assert_eq!(
vec![ParseWarning::UnexpectedType {
expected: "boolean",
actual: "string",
path: "enable_openh264".to_string()
}],
result.warnings
);
let result = read_preferences("enable_openh264 = false");
assert_eq!(
&SavedGlobalPreferences {
enable_openh264: false,
..Default::default()
},
result.values()
);
assert_eq!(Vec::<ParseWarning>::new(), result.warnings);
let result = read_preferences("");
assert_eq!(
&SavedGlobalPreferences {
enable_openh264: true,
..Default::default()
},
result.values()
);
assert_eq!(Vec::<ParseWarning>::new(), result.warnings);
}
#[test]
fn log_filename() {
let result = read_preferences("log = {filename_pattern = 5}");

View File

@ -59,6 +59,13 @@ impl<'a> PreferencesWriter<'a> {
})
}
pub fn set_enable_openh264(&mut self, enable: bool) {
self.0.edit(|values, toml_document| {
toml_document["enable_openh264"] = value(enable);
values.enable_openh264 = enable;
})
}
pub fn set_log_filename_pattern(&mut self, pattern: FilenamePattern) {
self.0.edit(|values, toml_document| {
toml_document["log"]["filename_pattern"] = value(pattern.as_str());
@ -168,6 +175,20 @@ mod tests {
);
}
#[test]
fn set_enable_openh264() {
test(
"",
|writer| writer.set_enable_openh264(false),
"enable_openh264 = false\n",
);
test(
"enable_openh264 = false",
|writer| writer.set_enable_openh264(true),
"enable_openh264 = true\n",
);
}
#[test]
fn set_log_filename_pattern() {
test(

View File

@ -10,7 +10,7 @@ use ruffle_video::VideoStreamHandle;
use ruffle_video_software::backend::SoftwareVideoBackend;
use slotmap::SlotMap;
use std::fs::File;
use std::io::{copy, Write};
use std::io::copy;
use std::path::PathBuf;
use swf::{VideoCodec, VideoDeblocking};
@ -38,9 +38,6 @@ impl Default for ExternalVideoBackend {
}
}
/// Source: https://www.openh264.org/BINARY_LICENSE.txt
const BINARY_LICENSE: &[u8] = include_bytes!("BINARY_LICENSE.txt");
impl ExternalVideoBackend {
fn get_openh264_data() -> Result<(&'static str, &'static str), Box<dyn std::error::Error>> {
// Source: https://github.com/cisco/openh264/releases/tag/v2.4.1
@ -82,6 +79,7 @@ impl ExternalVideoBackend {
}
pub fn get_openh264() -> Result<PathBuf, Box<dyn std::error::Error>> {
// See the license at: https://www.openh264.org/BINARY_LICENSE.txt
const URL_BASE: &str = "http://ciscobinary.openh264.org/";
const URL_SUFFIX: &str = ".bz2";
@ -94,8 +92,6 @@ impl ExternalVideoBackend {
// If the binary doesn't exist in the expected location, download it.
if !filepath.is_file() {
File::create("OpenH264-license.txt")?.write_all(BINARY_LICENSE)?;
let url = format!("{}{}{}", URL_BASE, filename, URL_SUFFIX);
let response = reqwest::blocking::get(url)?;
let bytes = response.bytes()?;