Pass HW decoder choice to player
This commit is contained in:
parent
028fd83296
commit
e078fe05dd
6 changed files with 115 additions and 72 deletions
|
|
@ -8,7 +8,7 @@ use lexopt::prelude::*;
|
|||
use serde::{Deserialize, Serialize};
|
||||
use std::{path::PathBuf, process};
|
||||
|
||||
use crate::wrappers::HWDeviceType;
|
||||
use crate::hardware::DeviceType;
|
||||
|
||||
pub const CONFIG_VERSION: u64 = 1;
|
||||
|
||||
|
|
@ -32,14 +32,14 @@ impl AppTheme {
|
|||
#[derive(Clone, CosmicConfigEntry, Debug, Deserialize, Eq, PartialEq, Serialize)]
|
||||
pub struct Config {
|
||||
pub app_theme: AppTheme,
|
||||
pub hw_decoder: HWDeviceType,
|
||||
pub hw_decoder: DeviceType,
|
||||
}
|
||||
|
||||
impl Default for Config {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
app_theme: AppTheme::System,
|
||||
hw_decoder: HWDeviceType::default(),
|
||||
hw_decoder: DeviceType::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -54,7 +54,7 @@ impl Config {
|
|||
|
||||
pub struct Args {
|
||||
pub paths: Vec<PathBuf>,
|
||||
pub decoder: Option<HWDeviceType>,
|
||||
pub decoder: Option<DeviceType>,
|
||||
}
|
||||
|
||||
impl Args {
|
||||
|
|
@ -67,7 +67,7 @@ impl Args {
|
|||
match arg {
|
||||
Long("list-hwdec") => {
|
||||
println!("Supported hardware decoders:");
|
||||
for hwdec in HWDeviceType::supported_devices() {
|
||||
for hwdec in DeviceType::supported_devices() {
|
||||
println!("\t* [{}] {hwdec}", hwdec.short_name());
|
||||
}
|
||||
process::exit(0);
|
||||
|
|
|
|||
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;
|
||||
|
|
@ -1,16 +1,18 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-only
|
||||
|
||||
use std::{fmt, iter::FusedIterator, str::FromStr};
|
||||
use std::{fmt, str::FromStr};
|
||||
|
||||
use ffmpeg_next::ffi::{av_hwdevice_iterate_types, AVHWDeviceType};
|
||||
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 HWDeviceType {
|
||||
pub enum DeviceType {
|
||||
None,
|
||||
/// Compute Unified Device Architecture
|
||||
/// Nvidia only.
|
||||
|
|
@ -52,7 +54,7 @@ pub enum HWDeviceType {
|
|||
Vulkan,
|
||||
}
|
||||
|
||||
impl HWDeviceType {
|
||||
impl DeviceType {
|
||||
/// Hardware device names for user facing interfaces (logging, configs).
|
||||
pub const fn name(self) -> &'static str {
|
||||
match self {
|
||||
|
|
@ -97,7 +99,7 @@ impl HWDeviceType {
|
|||
}
|
||||
}
|
||||
|
||||
impl FromStr for HWDeviceType {
|
||||
impl FromStr for DeviceType {
|
||||
type Err = DeError;
|
||||
|
||||
// av_hwdevice_find_type_by_name returns None for invalid device type names, but this type
|
||||
|
|
@ -125,13 +127,13 @@ impl FromStr for HWDeviceType {
|
|||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for HWDeviceType {
|
||||
impl fmt::Display for DeviceType {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "{}", self.name())
|
||||
}
|
||||
}
|
||||
|
||||
impl From<AVHWDeviceType> for HWDeviceType {
|
||||
impl From<AVHWDeviceType> for DeviceType {
|
||||
fn from(value: AVHWDeviceType) -> Self {
|
||||
match value {
|
||||
AVHWDeviceType::AV_HWDEVICE_TYPE_NONE => Self::None,
|
||||
|
|
@ -152,59 +154,29 @@ impl From<AVHWDeviceType> for HWDeviceType {
|
|||
}
|
||||
}
|
||||
|
||||
impl Default for HWDeviceType {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
/// 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 = HWDeviceType;
|
||||
|
||||
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 HWDeviceType::supported_devices() {
|
||||
black_box(decoder);
|
||||
}
|
||||
|
||||
let _decoders: Vec<_> = black_box(HWDeviceType::supported_devices().collect());
|
||||
}
|
||||
}
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
|
@ -32,7 +32,7 @@ mod localize;
|
|||
use player::{PlayerMessage, VideoFrame, VideoQueue};
|
||||
mod player;
|
||||
|
||||
mod wrappers;
|
||||
mod hardware;
|
||||
|
||||
/// Runs application with these settings
|
||||
#[rustfmt::skip]
|
||||
|
|
@ -72,7 +72,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||
let Args { mut paths, .. } = args;
|
||||
let path = paths.pop().unwrap();
|
||||
|
||||
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();
|
||||
settings = settings.theme(config.app_theme.theme());
|
||||
|
|
|
|||
|
|
@ -28,6 +28,8 @@ use std::{
|
|||
time::{Duration, Instant},
|
||||
};
|
||||
|
||||
use crate::config::Config;
|
||||
|
||||
//TODO: calculate presentation time of end of queue
|
||||
pub struct AudioQueue {
|
||||
pub channels: usize,
|
||||
|
|
@ -207,6 +209,7 @@ fn ffmpeg_thread<P: AsRef<Path>>(
|
|||
path: P,
|
||||
player_rx: mpsc::Receiver<PlayerMessage>,
|
||||
video_queue_lock: Arc<Mutex<VideoQueue>>,
|
||||
config: Config,
|
||||
) -> Result<(), Box<dyn Error>> {
|
||||
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();
|
||||
unsafe {
|
||||
//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(
|
||||
&mut hw_device_ctx,
|
||||
hw_device_kind,
|
||||
hw_device_kind.into(),
|
||||
ptr::null(),
|
||||
ptr::null_mut(),
|
||||
0,
|
||||
) == 0
|
||||
{
|
||||
log::info!("using vaapi decoding");
|
||||
log::info!("using {hw_device_kind} decoding");
|
||||
(&mut *video_decoder_context.as_mut_ptr()).hw_device_ctx =
|
||||
ffi::av_buffer_ref(hw_device_ctx);
|
||||
} else {
|
||||
//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(())
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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()
|
||||
.name("ffmpeg".to_string())
|
||||
.spawn(move || {
|
||||
ffmpeg_thread(path, player_rx, video_queue_lock).unwrap();
|
||||
ffmpeg_thread(path, player_rx, video_queue_lock, config).unwrap();
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue