From 3f465ad12882942e66a3bf405727f7a535054d06 Mon Sep 17 00:00:00 2001 From: Brendan Allan Date: Wed, 9 Jul 2025 21:20:15 +0800 Subject: [PATCH] use D3D11VA decoder on windows + compatible pix fmt --- crates/ffmpeg-hw-device/src/lib.rs | 42 +++++++++++++++++--- crates/rendering/src/decoder/ffmpeg.rs | 2 +- crates/video-decode/examples/print_frames.rs | 40 +++++++++++++++++++ crates/video-decode/src/ffmpeg.rs | 14 +++++-- 4 files changed, 88 insertions(+), 10 deletions(-) create mode 100644 crates/video-decode/examples/print_frames.rs diff --git a/crates/ffmpeg-hw-device/src/lib.rs b/crates/ffmpeg-hw-device/src/lib.rs index 8b7bc921e..fc9d144ab 100644 --- a/crates/ffmpeg-hw-device/src/lib.rs +++ b/crates/ffmpeg-hw-device/src/lib.rs @@ -9,9 +9,10 @@ use ffmpeg::{ frame::{self, Video}, }; use ffmpeg_sys_next::{ - av_buffer_ref, av_buffer_unref, av_hwdevice_ctx_create, av_hwframe_transfer_data, - avcodec_get_hw_config, AVBufferRef, AVCodecContext, AVCodecHWConfig, AVHWDeviceType, - AVPixelFormat, AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, + av_buffer_ref, av_buffer_unref, av_hwdevice_ctx_create, av_hwdevice_get_hwframe_constraints, + av_hwframe_constraints_free, av_hwframe_transfer_data, avcodec_get_hw_config, AVBufferRef, + AVCodecContext, AVCodecHWConfig, AVHWDeviceType, AVHWFramesConstraints, AVPixelFormat, + AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX, }; thread_local! { @@ -77,9 +78,14 @@ impl CodecContextExt for codec::decoder::decoder::Decoder { let codec = self.codec().ok_or("no codec")?; unsafe { - let Some(hw_config) = codec.hw_configs().find(|&config| { - (*config).device_type == device_type + let Some(hw_config) = codec.hw_configs().find_map(|config| { + if (*config).device_type == device_type && (*config).methods & (AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX as i32) == 1 + { + Some(&*config) + } else { + None + } }) else { return Err("no hw config"); }; @@ -94,6 +100,13 @@ impl CodecContextExt for codec::decoder::decoder::Decoder { let context = self.as_mut_ptr(); + let mut constraints = av_hwdevice_get_hwframe_constraints(hw_device_ctx, null()); + if !constraints.is_null() { + (*context).pix_fmt = (*constraints).valid_sw_formats()[0]; + } + + av_hwframe_constraints_free(&raw mut constraints); + (*context).get_format = Some(get_format); (*context).hw_device_ctx = av_buffer_ref(hw_device_ctx); @@ -156,3 +169,22 @@ impl CodecExt for codec::codec::Codec { // } // } // } + +pub trait AVHWFramesConstraintsExt { + unsafe fn valid_sw_formats(&self) -> &[AVPixelFormat]; +} + +impl AVHWFramesConstraintsExt for AVHWFramesConstraints { + unsafe fn valid_sw_formats(&self) -> &[AVPixelFormat] { + let mut len = 0; + + loop { + if *self.valid_sw_formats.offset(len as isize) == AVPixelFormat::AV_PIX_FMT_NONE { + break; + } + len += 1; + } + + std::slice::from_raw_parts(self.valid_sw_formats, len) + } +} diff --git a/crates/rendering/src/decoder/ffmpeg.rs b/crates/rendering/src/decoder/ffmpeg.rs index 8d23341b3..b6469d729 100644 --- a/crates/rendering/src/decoder/ffmpeg.rs +++ b/crates/rendering/src/decoder/ffmpeg.rs @@ -85,7 +85,7 @@ impl FfmpegDecoder { Some(if cfg!(target_os = "macos") { AVHWDeviceType::AV_HWDEVICE_TYPE_VIDEOTOOLBOX } else { - AVHWDeviceType::AV_HWDEVICE_TYPE_D3D12VA + AVHWDeviceType::AV_HWDEVICE_TYPE_D3D11VA }), )?; diff --git a/crates/video-decode/examples/print_frames.rs b/crates/video-decode/examples/print_frames.rs new file mode 100644 index 000000000..fcaaafea0 --- /dev/null +++ b/crates/video-decode/examples/print_frames.rs @@ -0,0 +1,40 @@ +use std::{path::PathBuf, time::Instant}; + +#[cfg(target_os = "macos")] +use cap_video_decode::AVAssetReaderDecoder; + +#[cfg(windows)] +use cap_video_decode::FFmpegDecoder; +use ffmpeg_sys_next::AVHWDeviceType; + +#[tokio::main] +pub async fn main() { + let handle = tokio::runtime::Handle::current(); + + let path: PathBuf = std::env::args().collect::>().swap_remove(1).into(); + + let _ = std::thread::spawn(|| { + let mut decoder = + FFmpegDecoder::new(path, Some(AVHWDeviceType::AV_HWDEVICE_TYPE_D3D11VA)).unwrap(); + + let start = Instant::now(); + for frame in decoder.frames() { + let Ok(frame) = frame else { + return; + }; + + dbg!(frame.format()); + dbg!(frame.pts()); + dbg!(frame.planes()); + for i in 0..frame.planes() { + dbg!(frame.data(i).len()); + } + } + println!("Elapsed time: {:?}", start.elapsed()); + }) + .join(); +} + +pub fn pts_to_frame(pts: i64, time_base: (f64, f64), fps: u32) -> u32 { + (fps as f64 * ((pts as f64 * time_base.0) / (time_base.1))).round() as u32 +} diff --git a/crates/video-decode/src/ffmpeg.rs b/crates/video-decode/src/ffmpeg.rs index 401770527..df211324b 100644 --- a/crates/video-decode/src/ffmpeg.rs +++ b/crates/video-decode/src/ffmpeg.rs @@ -49,13 +49,14 @@ impl FFmpegDecoder { let exceeds_common_hw_limits = width > 4096 || height > 4096; + let codec = decoder.codec(); let hw_device = hw_device_type - .and_then(|_| { + .and_then(|v| { if exceeds_common_hw_limits{ debug!("Video dimensions {width}x{height} exceed common hardware decoder limits (4096x4096), not using hardware acceleration"); None } else { - None + Some(v) } }) .and_then(|hw_device_type| decoder.try_use_hw_device(hw_device_type).ok()); @@ -124,9 +125,14 @@ impl<'a> Iterator for FramesIter<'a> { match self.decoder.receive_frame(&mut frame) { Ok(()) => { return match &self.hw_device { - Some(hw_device) => Some(Ok(hw_device.get_hwframe(&frame).unwrap_or(frame))), + Some(hw_device) => { + let pts = frame.pts(); + let mut sw_frame = hw_device.get_hwframe(&frame).unwrap_or(frame); + sw_frame.set_pts(pts); + Some(Ok(sw_frame)) + } None => Some(Ok(frame)), - } + }; } Err(ffmpeg::Error::Eof) => return None, Err(ffmpeg::Error::Other { errno }) if errno == EAGAIN => {} pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy