Merge pull request #2 from joshuamegnauth54/choose-hw-decoder
Choose hardware decoder
This commit is contained in:
commit
6681775fd4
8 changed files with 341 additions and 20 deletions
7
Cargo.lock
generated
7
Cargo.lock
generated
|
|
@ -1065,6 +1065,7 @@ dependencies = [
|
||||||
"i18n-embed",
|
"i18n-embed",
|
||||||
"i18n-embed-fl",
|
"i18n-embed-fl",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
|
"lexopt",
|
||||||
"libcosmic",
|
"libcosmic",
|
||||||
"log",
|
"log",
|
||||||
"paste",
|
"paste",
|
||||||
|
|
@ -2840,6 +2841,12 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "lexopt"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "baff4b617f7df3d896f97fe922b64817f6cd9a756bb81d40f8883f2f66dcb401"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libc"
|
name = "libc"
|
||||||
version = "0.2.153"
|
version = "0.2.153"
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ rust-embed = "6"
|
||||||
# Logging
|
# Logging
|
||||||
env_logger = "0.10"
|
env_logger = "0.10"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
|
lexopt = "0.3"
|
||||||
|
|
||||||
[dependencies.libcosmic]
|
[dependencies.libcosmic]
|
||||||
git = "https://github.com/pop-os/libcosmic.git"
|
git = "https://github.com/pop-os/libcosmic.git"
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,11 @@ use cosmic::{
|
||||||
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
|
cosmic_config::{self, cosmic_config_derive::CosmicConfigEntry, CosmicConfigEntry},
|
||||||
theme,
|
theme,
|
||||||
};
|
};
|
||||||
|
use lexopt::prelude::*;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
use std::{path::PathBuf, process};
|
||||||
|
|
||||||
|
use crate::hardware::DeviceType;
|
||||||
|
|
||||||
pub const CONFIG_VERSION: u64 = 1;
|
pub const CONFIG_VERSION: u64 = 1;
|
||||||
|
|
||||||
|
|
@ -28,12 +32,63 @@ impl AppTheme {
|
||||||
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub app_theme: AppTheme,
|
pub app_theme: AppTheme,
|
||||||
|
pub hw_decoder: DeviceType,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
app_theme: AppTheme::System,
|
app_theme: AppTheme::System,
|
||||||
|
hw_decoder: DeviceType::default(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Config {
|
||||||
|
pub fn with_args(&mut self, args: &mut Args) {
|
||||||
|
if let Some(decoder) = args.decoder {
|
||||||
|
self.hw_decoder = decoder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct Args {
|
||||||
|
pub paths: Vec<PathBuf>,
|
||||||
|
pub decoder: Option<DeviceType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Args {
|
||||||
|
pub fn parse_args() -> Result<Self, lexopt::Error> {
|
||||||
|
let mut paths = Vec::new();
|
||||||
|
let mut decoder = None;
|
||||||
|
|
||||||
|
let mut parser = lexopt::Parser::from_env();
|
||||||
|
while let Some(arg) = parser.next()? {
|
||||||
|
match arg {
|
||||||
|
Long("list-hwdec") => {
|
||||||
|
println!("Supported hardware decoders:");
|
||||||
|
for hwdec in DeviceType::supported_devices() {
|
||||||
|
println!("\t* [{}] {hwdec}", hwdec.short_name());
|
||||||
|
}
|
||||||
|
process::exit(0);
|
||||||
|
}
|
||||||
|
Long("hwdec") => {
|
||||||
|
decoder = Some(parser.value()?.parse()?);
|
||||||
|
}
|
||||||
|
Value(path) => {
|
||||||
|
let path = path.parse()?;
|
||||||
|
paths.push(path);
|
||||||
|
}
|
||||||
|
_ => return Err(arg.unexpected()),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if paths.is_empty() {
|
||||||
|
return Err(lexopt::Error::MissingValue {
|
||||||
|
option: Some("missing video path".into()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(Self { paths, decoder })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
7
src/hardware.rs
Normal file
7
src/hardware.rs
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
pub mod device_type;
|
||||||
|
pub mod iter;
|
||||||
|
|
||||||
|
pub use device_type::DeviceType;
|
||||||
|
pub use iter::SupportedDeviceIter;
|
||||||
182
src/hardware/device_type.rs
Normal file
182
src/hardware/device_type.rs
Normal file
|
|
@ -0,0 +1,182 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use std::{fmt, str::FromStr};
|
||||||
|
|
||||||
|
use ffmpeg_next::ffi::AVHWDeviceType;
|
||||||
|
use serde::{
|
||||||
|
de::{value::Error as DeError, Error as DeErrorTrait, Unexpected},
|
||||||
|
Deserialize, Serialize,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::iter::SupportedDeviceIter;
|
||||||
|
|
||||||
|
/// Delegate type for [`ffmpeg_next::ffi::AVHWDeviceType`] for configs.
|
||||||
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||||
|
pub enum DeviceType {
|
||||||
|
None,
|
||||||
|
/// Compute Unified Device Architecture
|
||||||
|
/// Nvidia only.
|
||||||
|
/// https://developer.nvidia.com/video-codec-sdk
|
||||||
|
Cuda,
|
||||||
|
/// Direct3D 11 Video API
|
||||||
|
/// https://learn.microsoft.com/en-us/windows/win32/medfound/direct3d-11-video-apis
|
||||||
|
D3d11va,
|
||||||
|
/// Direct3D 12 Video API
|
||||||
|
/// https://learn.microsoft.com/en-us/windows/win32/medfound/direct3d-12-video-overview
|
||||||
|
D3d12va,
|
||||||
|
/// DirectX Video Acceleration 2.0
|
||||||
|
/// https://learn.microsoft.com/en-us/windows/win32/medfound/about-dxva-2-0
|
||||||
|
Dxva2,
|
||||||
|
/// Direct Rendering Manager
|
||||||
|
/// https://dri.freedesktop.org/wiki/DRM/
|
||||||
|
Drm,
|
||||||
|
/// MediaCodec
|
||||||
|
/// Android only
|
||||||
|
/// https://developer.android.com/reference/android/media/MediaCodec
|
||||||
|
MediaCodec,
|
||||||
|
/// OpenCL
|
||||||
|
/// Only used in filters
|
||||||
|
/// https://www.khronos.org/opencl/
|
||||||
|
OpenCl,
|
||||||
|
/// Intel Quick Sync Video
|
||||||
|
/// https://www.intel.com/content/www/us/en/developer/tools/vpl/overview.html
|
||||||
|
Qsv,
|
||||||
|
/// Video Acceleration API
|
||||||
|
/// https://www.intel.com/content/www/us/en/developer/articles/technical/linuxmedia-vaapi.html
|
||||||
|
Vaapi,
|
||||||
|
/// Video Decode and Presentation API for Unix
|
||||||
|
/// https://www.freedesktop.org/wiki/Software/VDPAU/
|
||||||
|
Vdpau,
|
||||||
|
/// Video Toolbox
|
||||||
|
/// https://developer.apple.com/documentation/videotoolbox
|
||||||
|
VideoToolbox,
|
||||||
|
/// Vulkan
|
||||||
|
Vulkan,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DeviceType {
|
||||||
|
/// Hardware device names for user facing interfaces (logging, configs).
|
||||||
|
pub const fn name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::None => "None",
|
||||||
|
Self::Cuda => "CUDA",
|
||||||
|
Self::Dxva2 => "DirectX Video Acceleration 2.0",
|
||||||
|
Self::D3d11va => "DirectX 11 Video Acceleration",
|
||||||
|
Self::D3d12va => "DirectX 12 Video Acceleration",
|
||||||
|
Self::Drm => "Direct Rendering Manager (DRM)",
|
||||||
|
Self::MediaCodec => "MediaCodec",
|
||||||
|
Self::OpenCl => "OpenCL",
|
||||||
|
Self::Qsv => "Intel Quick Video Sync",
|
||||||
|
Self::Vaapi => "VA-API",
|
||||||
|
Self::Vdpau => "VDPAU",
|
||||||
|
Self::VideoToolbox => "VideoToolbox",
|
||||||
|
Self::Vulkan => "Vulkan",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Short name for CLI arguments
|
||||||
|
pub const fn short_name(self) -> &'static str {
|
||||||
|
match self {
|
||||||
|
Self::None => "none",
|
||||||
|
Self::Cuda => "cuda",
|
||||||
|
Self::Dxva2 => "dxva2",
|
||||||
|
Self::D3d11va => "d3d11va",
|
||||||
|
Self::D3d12va => "d3d12va",
|
||||||
|
Self::Drm => "drm",
|
||||||
|
Self::MediaCodec => "mediacodec",
|
||||||
|
Self::OpenCl => "opencl",
|
||||||
|
Self::Qsv => "qsv",
|
||||||
|
Self::Vaapi => "vaapi",
|
||||||
|
Self::Vdpau => "vdpau",
|
||||||
|
Self::VideoToolbox => "videotoolbox",
|
||||||
|
Self::Vulkan => "vulkan",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// System's supported hardware decoders
|
||||||
|
pub fn supported_devices() -> SupportedDeviceIter {
|
||||||
|
SupportedDeviceIter::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for DeviceType {
|
||||||
|
type Err = DeError;
|
||||||
|
|
||||||
|
// av_hwdevice_find_type_by_name returns None for invalid device type names, but this type
|
||||||
|
// is used for deserializing configs (etc.) so the error is preserved.
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s {
|
||||||
|
"none" => Ok(Self::None),
|
||||||
|
"cuda" => Ok(Self::Cuda),
|
||||||
|
"dxva2" => Ok(Self::Dxva2),
|
||||||
|
"d3d11va" => Ok(Self::D3d11va),
|
||||||
|
"d3d12va" => Ok(Self::D3d12va),
|
||||||
|
"drm" => Ok(Self::Drm),
|
||||||
|
"mediacodec" => Ok(Self::MediaCodec),
|
||||||
|
"opencl" => Ok(Self::OpenCl),
|
||||||
|
"qsv" => Ok(Self::Qsv),
|
||||||
|
"vaapi" => Ok(Self::Vaapi),
|
||||||
|
"vdpau" => Ok(Self::Vdpau),
|
||||||
|
"videotoolbox" => Ok(Self::VideoToolbox),
|
||||||
|
"vulkan" => Ok(Self::Vulkan),
|
||||||
|
_ => Err(DeError::invalid_value(
|
||||||
|
Unexpected::Str(s),
|
||||||
|
&"valid hardware decoder",
|
||||||
|
)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for DeviceType {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "{}", self.name())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<AVHWDeviceType> for DeviceType {
|
||||||
|
fn from(value: AVHWDeviceType) -> Self {
|
||||||
|
match value {
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_NONE => Self::None,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA => Self::Cuda,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_DXVA2 => Self::Dxva2,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_D3D11VA => Self::D3d11va,
|
||||||
|
// This variant exists in ffmpeg's C lib but not in Rust's crate yet.
|
||||||
|
// AVHWDeviceType::AV_HWDEVICE_TYPE_D3D12VA => Self::D3d12va
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_DRM => Self::Drm,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_MEDIACODEC => Self::MediaCodec,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_OPENCL => Self::OpenCl,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_QSV => Self::Qsv,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI => Self::Vaapi,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_VDPAU => Self::Vdpau,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_VIDEOTOOLBOX => Self::VideoToolbox,
|
||||||
|
AVHWDeviceType::AV_HWDEVICE_TYPE_VULKAN => Self::Vulkan,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<DeviceType> for AVHWDeviceType {
|
||||||
|
fn from(value: DeviceType) -> Self {
|
||||||
|
match value {
|
||||||
|
DeviceType::None => Self::AV_HWDEVICE_TYPE_NONE,
|
||||||
|
DeviceType::Cuda => Self::AV_HWDEVICE_TYPE_CUDA,
|
||||||
|
DeviceType::D3d11va => Self::AV_HWDEVICE_TYPE_D3D11VA,
|
||||||
|
// NOTE: Next FFmpeg release
|
||||||
|
DeviceType::D3d12va => Self::AV_HWDEVICE_TYPE_NONE,
|
||||||
|
DeviceType::Dxva2 => Self::AV_HWDEVICE_TYPE_DXVA2,
|
||||||
|
DeviceType::Drm => Self::AV_HWDEVICE_TYPE_DRM,
|
||||||
|
DeviceType::MediaCodec => Self::AV_HWDEVICE_TYPE_MEDIACODEC,
|
||||||
|
DeviceType::OpenCl => Self::AV_HWDEVICE_TYPE_OPENCL,
|
||||||
|
DeviceType::Qsv => Self::AV_HWDEVICE_TYPE_QSV,
|
||||||
|
DeviceType::Vaapi => Self::AV_HWDEVICE_TYPE_VAAPI,
|
||||||
|
DeviceType::Vdpau => Self::AV_HWDEVICE_TYPE_VDPAU,
|
||||||
|
DeviceType::VideoToolbox => Self::AV_HWDEVICE_TYPE_VIDEOTOOLBOX,
|
||||||
|
DeviceType::Vulkan => Self::AV_HWDEVICE_TYPE_VULKAN,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DeviceType {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Vaapi
|
||||||
|
}
|
||||||
|
}
|
||||||
58
src/hardware/iter.rs
Normal file
58
src/hardware/iter.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
// SPDX-License-Identifier: GPL-3.0-only
|
||||||
|
|
||||||
|
use std::iter::FusedIterator;
|
||||||
|
|
||||||
|
use ffmpeg_next::ffi::{av_hwdevice_iterate_types, AVHWDeviceType};
|
||||||
|
|
||||||
|
use super::device_type::DeviceType;
|
||||||
|
|
||||||
|
/// Iterator over system's supported hardware decoders.
|
||||||
|
pub struct SupportedDeviceIter {
|
||||||
|
current: AVHWDeviceType,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for SupportedDeviceIter {
|
||||||
|
fn default() -> Self {
|
||||||
|
// SAFETY: FFmpeg's documentation states that the iterator is delimited by AV_HWDEVICE_TYPE_NONE.
|
||||||
|
let current = unsafe { av_hwdevice_iterate_types(AVHWDeviceType::AV_HWDEVICE_TYPE_NONE) };
|
||||||
|
Self { current }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Iterator for SupportedDeviceIter {
|
||||||
|
type Item = DeviceType;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
// None is a sentinel value that indicates the iterator is exhausted
|
||||||
|
if self.current == AVHWDeviceType::AV_HWDEVICE_TYPE_NONE {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
let prev = self.current;
|
||||||
|
// SAFETY: The docs and examples state that the iterator yields the next value
|
||||||
|
// when the previous is passed in.
|
||||||
|
self.current = unsafe { av_hwdevice_iterate_types(prev) };
|
||||||
|
|
||||||
|
Some(prev.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FusedIterator for SupportedDeviceIter {}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use std::hint::black_box;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
// The iterator's yielded values aren't important since hardware decoders vary by system
|
||||||
|
// This is just a sanity check to ensure the iterator works
|
||||||
|
#[test]
|
||||||
|
fn supported_device_iter_doesnt_seg_fault() {
|
||||||
|
for decoder in DeviceType::supported_devices() {
|
||||||
|
black_box(decoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
let _decoders: Vec<_> = black_box(DeviceType::supported_devices().collect());
|
||||||
|
}
|
||||||
|
}
|
||||||
34
src/main.rs
34
src/main.rs
|
|
@ -16,14 +16,12 @@ use cosmic::{
|
||||||
use std::{
|
use std::{
|
||||||
any::TypeId,
|
any::TypeId,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
env,
|
env, process,
|
||||||
path::PathBuf,
|
|
||||||
process,
|
|
||||||
sync::{mpsc, Arc, Mutex},
|
sync::{mpsc, Arc, Mutex},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
use config::{AppTheme, Config, CONFIG_VERSION};
|
use config::{AppTheme, Args, Config, CONFIG_VERSION};
|
||||||
mod config;
|
mod config;
|
||||||
|
|
||||||
use key_bind::{key_binds, KeyBind};
|
use key_bind::{key_binds, KeyBind};
|
||||||
|
|
@ -34,22 +32,34 @@ mod localize;
|
||||||
use player::{PlayerMessage, VideoFrame, VideoQueue};
|
use player::{PlayerMessage, VideoFrame, VideoQueue};
|
||||||
mod player;
|
mod player;
|
||||||
|
|
||||||
|
mod hardware;
|
||||||
|
|
||||||
/// Runs application with these settings
|
/// Runs application with these settings
|
||||||
#[rustfmt::skip]
|
#[rustfmt::skip]
|
||||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
env_logger::Builder::from_env(env_logger::Env::default().default_filter_or("warn")).init();
|
||||||
|
|
||||||
localize::localize();
|
localize::localize();
|
||||||
|
|
||||||
|
let mut args = match Args::parse_args() {
|
||||||
|
Ok(args) => args,
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("{e}");
|
||||||
|
process::exit(1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
let (config_handler, config) = match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) {
|
let (config_handler, config) = match cosmic_config::Config::new(App::APP_ID, CONFIG_VERSION) {
|
||||||
Ok(config_handler) => {
|
Ok(config_handler) => {
|
||||||
let config = match Config::get_entry(&config_handler) {
|
let mut config = match Config::get_entry(&config_handler) {
|
||||||
Ok(ok) => ok,
|
Ok(ok) => ok,
|
||||||
Err((errs, config)) => {
|
Err((errs, config)) => {
|
||||||
log::info!("errors loading config: {:?}", errs);
|
log::info!("errors loading config: {:?}", errs);
|
||||||
config
|
config
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
// Update config with command line args
|
||||||
|
config.with_args(&mut args);
|
||||||
(Some(config_handler), config)
|
(Some(config_handler), config)
|
||||||
}
|
}
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
|
|
@ -58,16 +68,12 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
//TODO: support multiple paths
|
//TODO: support using multiple paths
|
||||||
let path = match env::args().skip(1).next() {
|
let Args { mut paths, .. } = args;
|
||||||
Some(arg) => PathBuf::from(arg),
|
let path = paths.pop().unwrap();
|
||||||
None => {
|
|
||||||
log::error!("no argument provided");
|
|
||||||
process::exit(1);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let (player_tx, video_queue_lock) = player::run(path);
|
// TODO: Update video player config when it's updated via the app
|
||||||
|
let (player_tx, video_queue_lock) = player::run(path, config.clone());
|
||||||
|
|
||||||
let mut settings = Settings::default();
|
let mut settings = Settings::default();
|
||||||
settings = settings.theme(config.app_theme.theme());
|
settings = settings.theme(config.app_theme.theme());
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,8 @@ use std::{
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use crate::config::Config;
|
||||||
|
|
||||||
//TODO: calculate presentation time of end of queue
|
//TODO: calculate presentation time of end of queue
|
||||||
pub struct AudioQueue {
|
pub struct AudioQueue {
|
||||||
pub channels: usize,
|
pub channels: usize,
|
||||||
|
|
@ -207,6 +209,7 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
path: P,
|
path: P,
|
||||||
player_rx: mpsc::Receiver<PlayerMessage>,
|
player_rx: mpsc::Receiver<PlayerMessage>,
|
||||||
video_queue_lock: Arc<Mutex<VideoQueue>>,
|
video_queue_lock: Arc<Mutex<VideoQueue>>,
|
||||||
|
config: Config,
|
||||||
) -> Result<(), Box<dyn Error>> {
|
) -> Result<(), Box<dyn Error>> {
|
||||||
let (audio_config, cpal_stream, audio_queue_lock) = cpal();
|
let (audio_config, cpal_stream, audio_queue_lock) = cpal();
|
||||||
|
|
||||||
|
|
@ -227,21 +230,23 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
let mut hw_device_ctx = ptr::null_mut();
|
let mut hw_device_ctx = ptr::null_mut();
|
||||||
unsafe {
|
unsafe {
|
||||||
//TODO: support other types
|
//TODO: support other types
|
||||||
let hw_device_kind = ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI;
|
let hw_device_kind = config.hw_decoder;
|
||||||
if ffi::av_hwdevice_ctx_create(
|
if ffi::av_hwdevice_ctx_create(
|
||||||
&mut hw_device_ctx,
|
&mut hw_device_ctx,
|
||||||
hw_device_kind,
|
hw_device_kind.into(),
|
||||||
ptr::null(),
|
ptr::null(),
|
||||||
ptr::null_mut(),
|
ptr::null_mut(),
|
||||||
0,
|
0,
|
||||||
) == 0
|
) == 0
|
||||||
{
|
{
|
||||||
log::info!("using vaapi decoding");
|
log::info!("using {hw_device_kind} decoding");
|
||||||
(&mut *video_decoder_context.as_mut_ptr()).hw_device_ctx =
|
(&mut *video_decoder_context.as_mut_ptr()).hw_device_ctx =
|
||||||
ffi::av_buffer_ref(hw_device_ctx);
|
ffi::av_buffer_ref(hw_device_ctx);
|
||||||
} else {
|
} else {
|
||||||
//TODO: support other hardware devices
|
//TODO: support other hardware devices
|
||||||
log::warn!("failed to use vaapi decoding, falling back to software decoding");
|
log::warn!(
|
||||||
|
"failed to use {hw_device_kind} decoding, falling back to software decoding"
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -630,7 +635,7 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn run(path: PathBuf) -> (mpsc::Sender<PlayerMessage>, Arc<Mutex<VideoQueue>>) {
|
pub fn run(path: PathBuf, config: Config) -> (mpsc::Sender<PlayerMessage>, Arc<Mutex<VideoQueue>>) {
|
||||||
ffmpeg::init().unwrap();
|
ffmpeg::init().unwrap();
|
||||||
|
|
||||||
let (player_tx, player_rx) = mpsc::channel();
|
let (player_tx, player_rx) = mpsc::channel();
|
||||||
|
|
@ -640,7 +645,7 @@ pub fn run(path: PathBuf) -> (mpsc::Sender<PlayerMessage>, Arc<Mutex<VideoQueue>
|
||||||
thread::Builder::new()
|
thread::Builder::new()
|
||||||
.name("ffmpeg".to_string())
|
.name("ffmpeg".to_string())
|
||||||
.spawn(move || {
|
.spawn(move || {
|
||||||
ffmpeg_thread(path, player_rx, video_queue_lock).unwrap();
|
ffmpeg_thread(path, player_rx, video_queue_lock, config).unwrap();
|
||||||
})
|
})
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue