Skip to content

Commit

Permalink
starting point for implementing hwaccel
Browse files Browse the repository at this point in the history
  • Loading branch information
gerwin3 committed Mar 14, 2024
1 parent 9db860c commit 7b1e361
Show file tree
Hide file tree
Showing 5 changed files with 283 additions and 0 deletions.
22 changes: 22 additions & 0 deletions src/decode.rs
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,21 @@ impl DecoderSplit {

match self.decoder_receive_frame()? {
Some(frame) => {
// TODO: if frame.format() == hw_pix_fmt
let frame = if frame.format() == todo!() {
// Copy frame from hardware acceleration device to host.

let mut frame_host = RawFrame::empty();
// TODO: ffi this
// if ((ret = av_hwframe_transfer_data(sw_frame, frame, 0)) < 0) {
// fprintf(stderr, "Error transferring the data to system memory\n");
// goto fail;
// }
frame_host
} else {
frame
};

let mut frame_scaled = RawFrame::empty();
self.scaler
.run(&frame, &mut frame_scaled)
Expand Down Expand Up @@ -348,6 +363,10 @@ impl DecoderSplit {
let mut decoder = AvContext::new();
set_decoder_context_time_base(&mut decoder, reader_stream.time_base());
decoder.set_parameters(reader_stream.parameters())?;

// TODO: let hardware_acceleration_context = crate::hwaccel::HardwareAccelerationContext::new(&mut decoder, todo!());
// TODO: self.hardare_acceleration_context = Some(todo!());

let decoder = decoder.decoder().video()?;
let decoder_time_base = decoder.time_base();

Expand Down Expand Up @@ -411,6 +430,9 @@ impl Drop for DecoderSplit {
}
}
}

// TODO: How are we going to do this?
// TODO: av_buffer_unref(&hw_device_ctx);
}
}

Expand Down
10 changes: 10 additions & 0 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ pub enum Error {
MissingCodecParameters,
UnsupportedCodecParameterSets,
InvalidResizeParameters,
UninitializedCodec,
UnsupportedCodecHardwareAccelerationDeviceType,
BackendError(FfmpegError),
}

Expand All @@ -26,6 +28,8 @@ impl std::error::Error for Error {
Error::MissingCodecParameters => None,
Error::UnsupportedCodecParameterSets => None,
Error::InvalidResizeParameters => None,
Error::UninitializedCodec => None,
Error::UnsupportedCodecHardwareAccelerationDeviceType => None,
Error::BackendError(ref internal) => Some(internal),
}
}
Expand All @@ -51,6 +55,12 @@ impl std::fmt::Display for Error {
Error::InvalidResizeParameters => {
write!(f, "cannot resize frame into provided dimensions")
}
Error::UninitializedCodec => {
write!(f, "codec context is not initialized properly")
}
Error::UnsupportedCodecHardwareAccelerationDeviceType => {
write!(f, "codec does not supported hardware acceleration device")
}
Error::BackendError(ref internal) => internal.fmt(f),
}
}
Expand Down
110 changes: 110 additions & 0 deletions src/ffi_hwaccel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
extern crate ffmpeg_next as ffmpeg;

use crate::hwaccel::HardwareAccelerationDeviceType;

pub struct HardwareDeviceContext {
ptr: *mut ffmpeg::ffi::AVBufferRef,
}

impl HardwareDeviceContext {
pub fn new(
device_type: HardwareAccelerationDeviceType,
) -> Result<HardwareDeviceContext, ffmpeg::error::Error> {
let mut ptr: *mut ffmpeg::ffi::AVBufferRef = std::ptr::null_mut();

unsafe {
match ffmpeg::ffi::av_hwdevice_ctx_create(
(&mut ptr) as *mut *mut ffmpeg::ffi::AVBufferRef,
device_type.into(),
std::ptr::null(),
std::ptr::null_mut(),
0,
) {
0 => Ok(HardwareDeviceContext { ptr }),
e => Err(ffmpeg::error::Error::from(e)),
}
}
}

unsafe fn ref_raw(&self) -> *mut ffmpeg::ffi::AVBufferRef {
ffmpeg::ffi::av_buffer_ref(self.ptr)
}
}

impl Drop for HardwareDeviceContext {
fn drop(&mut self) {
unsafe {
ffmpeg::ffi::av_buffer_unref(&mut self.ptr);
}
}
}

pub fn hwdevice_list_available_device_types() -> Vec<HardwareAccelerationDeviceType> {
let mut hwdevice_types = Vec::new();
let mut hwdevice_type = unsafe {
ffmpeg::ffi::av_hwdevice_iterate_types(ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_NONE)
};
while hwdevice_type != ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_NONE {
hwdevice_types.push(HardwareAccelerationDeviceType::from(hwdevice_type).unwrap());
hwdevice_type = unsafe { ffmpeg::ffi::av_hwdevice_iterate_types(hwdevice_type) };
}
hwdevice_types
}

pub fn codec_find_corresponding_hwaccel_pixfmt(
codec: &ffmpeg::codec::codec::Codec,
hwaccel_type: HardwareAccelerationDeviceType,
) -> Option<ffmpeg::format::pixel::Pixel> {
let mut i = 0;
loop {
unsafe {
let hw_config = ffmpeg::ffi::avcodec_get_hw_config(codec.as_ptr(), i);
if !hw_config.is_null() {
let hw_config_supports_codec = (((*hw_config).methods) as i32
& ffmpeg::ffi::AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX as i32)
!= 0;
if hw_config_supports_codec && (*hw_config).device_type == hwaccel_type.into() {
break Some((*hw_config).pix_fmt.into());
}
} else {
break None;
}
}
i += 1;
}
}

pub fn codec_context_hwaccel_set_get_format(
codec_context: &mut ffmpeg::codec::context::Context,
hw_pixfmt: ffmpeg::format::pixel::Pixel,
) {
unsafe {
(*codec_context.as_mut_ptr()).opaque =
ffmpeg::ffi::AVPixelFormat::from(hw_pixfmt) as i32 as _;
(*codec_context.as_mut_ptr()).get_format = Some(hwaccel_get_format);
}
}

pub fn codec_context_hwaccel_set_hw_device_ctx(
codec_context: &mut ffmpeg::codec::context::Context,
hardware_device_context: &HardwareDeviceContext,
) {
unsafe {
(*codec_context.as_mut_ptr()).hw_device_ctx = hardware_device_context.ref_raw();
}
}

#[no_mangle]
unsafe extern "C" fn hwaccel_get_format(
ctx: *mut ffmpeg::ffi::AVCodecContext,
pix_fmts: *const ffmpeg::ffi::AVPixelFormat,
) -> ffmpeg::ffi::AVPixelFormat {
let mut p = pix_fmts;
while *p != ffmpeg::ffi::AVPixelFormat::AV_PIX_FMT_NONE {
if *p == std::mem::transmute((*ctx).opaque as i32) {
return *p;
}
p = p.add(1);
}
ffmpeg::ffi::AVPixelFormat::AV_PIX_FMT_NONE
}
138 changes: 138 additions & 0 deletions src/hwaccel.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
extern crate ffmpeg_next as ffmpeg;

use crate::error::Error;
use crate::ffi_hwaccel;

type Result<T> = std::result::Result<T, Error>;

pub struct HardwareAccelerationContext {
hardware_device_context: ffi_hwaccel::HardwareDeviceContext,
pixel_format: ffmpeg::util::format::Pixel,
}

impl HardwareAccelerationContext {
pub fn new(
decoder: &mut ffmpeg::codec::Context,
device_type: HardwareAccelerationDeviceType,
) -> Result<Self> {
let codec = decoder.codec().ok_or(Error::UninitializedCodec)?;
let pixel_format =
ffi_hwaccel::codec_find_corresponding_hwaccel_pixfmt(&codec, device_type)
.ok_or(Error::UnsupportedCodecHardwareAccelerationDeviceType)?;

ffi_hwaccel::codec_context_hwaccel_set_get_format(decoder, pixel_format);

let hardware_device_context = ffi_hwaccel::HardwareDeviceContext::new(device_type)?;
ffi_hwaccel::codec_context_hwaccel_set_hw_device_ctx(decoder, &hardware_device_context);

Ok(HardwareAccelerationContext {
hardware_device_context,
pixel_format,
})
}
}

#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum HardwareAccelerationDeviceType {
/// Video Decode and Presentation API for Unix (VDPAU)
Vdpau,
/// NVIDIA CUDA
Cuda,
/// Video Acceleration API (VA-API)
VaApi,
/// DirectX Video Acceleration 2.0
Dxva2,
/// Quick Sync Video
Qsv,
/// VideoToolbox
VideoToolbox,
/// Direct3D 11 Video Acceleration
D3D11Va,
/// Linux Direct Rendering Manager
Drm,
/// OpenCL
OpenCl,
/// MediaCodec
MeiaCodec,
/// Vulkan
Vulkan,
/// Direct3D 12 Video Acceleration
D3D12Va,
}

impl HardwareAccelerationDeviceType {
/// Whether or not the device type is available on this system.
pub fn is_available(self) -> bool {
Self::list_available().contains(&self)
}

/// List available hardware acceleration device types on this system.
///
/// Uses `av_hwdevice_iterate_types` internally.
pub fn list_available() -> Vec<HardwareAccelerationDeviceType> {
ffi_hwaccel::hwdevice_list_available_device_types()
}
}

impl HardwareAccelerationDeviceType {
pub fn from(value: ffmpeg::ffi::AVHWDeviceType) -> Option<HardwareAccelerationDeviceType> {
match value {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VDPAU => Some(Self::Vdpau),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA => Some(Self::Cuda),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI => Some(Self::VaApi),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_DXVA2 => Some(Self::Dxva2),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_QSV => Some(Self::Qsv),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VIDEOTOOLBOX => Some(Self::VideoToolbox),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_D3D11VA => Some(Self::D3D11Va),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_DRM => Some(Self::Drm),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_OPENCL => Some(Self::OpenCl),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_MEDIACODEC => Some(Self::MeiaCodec),
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VULKAN => Some(Self::Vulkan),
// ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_D3D12VA => Self::D3D12Va,
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_NONE => None,
}
}
}

impl From<HardwareAccelerationDeviceType> for ffmpeg::ffi::AVHWDeviceType {
fn from(value: HardwareAccelerationDeviceType) -> Self {
match value {
HardwareAccelerationDeviceType::Vdpau => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VDPAU
}
HardwareAccelerationDeviceType::Cuda => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA
}
HardwareAccelerationDeviceType::VaApi => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VAAPI
}
HardwareAccelerationDeviceType::Dxva2 => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_DXVA2
}
HardwareAccelerationDeviceType::Qsv => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_QSV
}
HardwareAccelerationDeviceType::VideoToolbox => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VIDEOTOOLBOX
}
HardwareAccelerationDeviceType::D3D11Va => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_D3D11VA
}
HardwareAccelerationDeviceType::Drm => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_DRM
}
HardwareAccelerationDeviceType::OpenCl => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_OPENCL
}
HardwareAccelerationDeviceType::MeiaCodec => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_MEDIACODEC
}
HardwareAccelerationDeviceType::Vulkan => {
ffmpeg::ffi::AVHWDeviceType::AV_HWDEVICE_TYPE_VULKAN
}
HardwareAccelerationDeviceType::D3D12Va => {
unimplemented!()
}
}
}
}
3 changes: 3 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ mod encode;
mod error;
mod extradata;
mod ffi;
mod ffi_hwaccel;
mod frame;
mod hwaccel;
mod init;
mod io;
mod mux;
Expand All @@ -20,6 +22,7 @@ pub use error::Error;
pub use extradata::{Pps, Sps};
pub use frame::PixelFormat;
pub use frame::RawFrame;
pub use hwaccel::HardwareAccelerationDeviceType;
pub use init::init;
pub use io::{Buf, Reader, Write, Writer};
pub use io::{Locator, Url};
Expand Down

0 comments on commit 7b1e361

Please sign in to comment.