use std::ptr;
use ffmpeg_next::ffi as ff;
use super::av::{BufferRef, Dict, check, cstr};
use crate::{Device, Result};
#[derive(Debug)]
pub(crate) struct Devices {
pub cuda: BufferRef,
pub vulkan: BufferRef,
pub vulkan_encode: bool,
}
impl Devices {
pub(crate) fn new(device: Device) -> Result<Self> {
match device {
Device::Vulkan(n) => Self::from_vulkan(&n.to_string()),
Device::Cuda(n) => Self::from_cuda(&n.to_string()),
_ => Self::from_vulkan("0").or_else(|_| Self::from_cuda("0")),
}
}
fn from_vulkan(name: &str) -> Result<Self> {
let vulkan = create(ff::AVHWDeviceType::AV_HWDEVICE_TYPE_VULKAN, name, &[])?;
let cuda = derive(&vulkan, ff::AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA)?;
Ok(Self {
cuda,
vulkan,
vulkan_encode: true,
})
}
fn from_cuda(name: &str) -> Result<Self> {
let cuda = create(ff::AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA, name, &[])?;
let vulkan = derive(&cuda, ff::AVHWDeviceType::AV_HWDEVICE_TYPE_VULKAN)?;
Ok(Self {
cuda,
vulkan,
vulkan_encode: false,
})
}
}
fn create(kind: ff::AVHWDeviceType, name: &str, opts: &[(&str, &str)]) -> Result<BufferRef> {
let mut dict = Dict::default();
for (k, v) in opts {
dict.set(k, v)?;
}
let name = cstr(name)?;
let mut p = ptr::null_mut();
check(
unsafe {
ff::av_hwdevice_ctx_create(&raw mut p, kind, name.as_ptr(), *dict.as_mut_ptr(), 0)
},
"av_hwdevice_ctx_create",
)?;
unsafe { BufferRef::from_raw(p, "av_hwdevice_ctx_create") }
}
fn derive(src: &BufferRef, kind: ff::AVHWDeviceType) -> Result<BufferRef> {
let mut p = ptr::null_mut();
check(
unsafe { ff::av_hwdevice_ctx_create_derived(&raw mut p, kind, src.as_ptr(), 0) },
"av_hwdevice_ctx_create_derived",
)?;
unsafe { BufferRef::from_raw(p, "av_hwdevice_ctx_create_derived") }
}
pub(crate) const VK_USAGE_RENDER: i32 = 0x10;
pub(crate) fn frames(
device: &BufferRef,
sw_format: ff::AVPixelFormat,
width: u32,
height: u32,
vk_usage: i32,
cuda: bool,
) -> Result<BufferRef> {
unsafe {
let r = BufferRef::from_raw(
ff::av_hwframe_ctx_alloc(device.as_ptr()),
"av_hwframe_ctx_alloc",
)?;
let fc = r.data::<ff::AVHWFramesContext>();
let dev = device.data::<ff::AVHWDeviceContext>();
(*fc).format = match (*dev).type_ {
ff::AVHWDeviceType::AV_HWDEVICE_TYPE_CUDA => ff::AVPixelFormat::AV_PIX_FMT_CUDA,
_ => ff::AVPixelFormat::AV_PIX_FMT_VULKAN,
};
(*fc).sw_format = sw_format;
(*fc).width = i32::try_from(width).unwrap_or(i32::MAX);
(*fc).height = i32::try_from(height).unwrap_or(i32::MAX);
if (*fc).format == ff::AVPixelFormat::AV_PIX_FMT_VULKAN {
let vk = (*fc).hwctx.cast::<ff::AVVulkanFramesContext>();
(*vk).usage |= vk_usage;
if cuda {
let flags = (&raw mut (*vk).flags).cast::<u32>();
*flags |= ff::AVVkFrameFlags::AV_VK_FRAME_FLAG_DISABLE_MULTIPLANE as u32;
}
}
check(ff::av_hwframe_ctx_init(r.as_ptr()), "av_hwframe_ctx_init")?;
Ok(r)
}
}