#![allow(unsafe_code)]
use std::env;
use std::ffi::c_void;
use std::path::PathBuf;
use libloading::Library;
use thiserror::Error;
use crate::consts::{MFX_ERR_NONE, mfx_succeeded};
use crate::raw::{
mfxBitstream, mfxEncodeCtrl, mfxFrameSurface1, mfxHandleType, mfxIMPL, mfxInitParam,
mfxSession, mfxStatus, mfxSyncPoint, mfxVersion, mfxVideoParam,
};
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum VplError {
#[error("no oneVPL implementation library found (tried: {tried})")]
NotFound {
tried: String,
},
#[error("required oneVPL entry point {symbol} missing from the loaded library")]
MissingSymbol {
symbol: &'static str,
},
#[error("oneVPL call {call} failed: mfxStatus {status}")]
Status {
call: &'static str,
status: mfxStatus,
},
}
type PfnMfxInitEx = unsafe extern "C" fn(par: mfxInitParam, session: *mut mfxSession) -> mfxStatus;
type PfnMfxClose = unsafe extern "C" fn(session: mfxSession) -> mfxStatus;
type PfnMfxQueryVersion =
unsafe extern "C" fn(session: mfxSession, version: *mut mfxVersion) -> mfxStatus;
type PfnMfxQueryImpl = unsafe extern "C" fn(session: mfxSession, r#impl: *mut mfxIMPL) -> mfxStatus;
type PfnMfxVideoEncodeQuery = unsafe extern "C" fn(
session: mfxSession,
r#in: *mut mfxVideoParam,
out: *mut mfxVideoParam,
) -> mfxStatus;
type PfnMfxVideoEncodeInit =
unsafe extern "C" fn(session: mfxSession, par: *mut mfxVideoParam) -> mfxStatus;
type PfnMfxVideoEncodeClose = unsafe extern "C" fn(session: mfxSession) -> mfxStatus;
type PfnMfxVideoEncodeEncodeFrameAsync = unsafe extern "C" fn(
session: mfxSession,
ctrl: *mut mfxEncodeCtrl,
surface: *mut mfxFrameSurface1,
bs: *mut mfxBitstream,
syncp: *mut mfxSyncPoint,
) -> mfxStatus;
type PfnMfxVideoCoreSyncOperation =
unsafe extern "C" fn(session: mfxSession, syncp: mfxSyncPoint, wait: u32) -> mfxStatus;
type PfnMfxVideoCoreSetHandle = unsafe extern "C" fn(
session: mfxSession,
handle_type: mfxHandleType,
hdl: *mut c_void,
) -> mfxStatus;
pub struct Loader {
_lib: Library,
fn_init_ex: PfnMfxInitEx,
fn_close: PfnMfxClose,
fn_query_version: PfnMfxQueryVersion,
fn_query_impl: PfnMfxQueryImpl,
fn_encode_query: PfnMfxVideoEncodeQuery,
fn_encode_init: PfnMfxVideoEncodeInit,
fn_encode_close: PfnMfxVideoEncodeClose,
fn_encode_frame_async: PfnMfxVideoEncodeEncodeFrameAsync,
fn_core_sync_operation: PfnMfxVideoCoreSyncOperation,
#[allow(
dead_code,
reason = "Stage 1 is CPU-upload only; wired for the D3D11 ZC follow-up"
)]
fn_core_set_handle: PfnMfxVideoCoreSetHandle,
}
#[cfg(windows)]
const DEFAULT_CANDIDATES: &[&str] = &["libmfxhw64.dll"];
#[cfg(not(windows))]
const DEFAULT_CANDIDATES: &[&str] = &[];
impl Loader {
pub fn open() -> Result<Self, VplError> {
let mut tried = Vec::new();
let search_dir = env::var_os("ONEVPL_SEARCH_PATH").map(PathBuf::from);
for candidate in DEFAULT_CANDIDATES {
if let Some(dir) = &search_dir {
let full = dir.join(candidate);
tried.push(full.display().to_string());
if let Ok(lib) = unsafe { Library::new(&full) } {
return Self::resolve(lib);
}
}
tried.push((*candidate).to_string());
if let Ok(lib) = unsafe { Library::new(candidate) } {
return Self::resolve(lib);
}
}
Err(VplError::NotFound {
tried: tried.join(", "),
})
}
fn resolve(lib: Library) -> Result<Self, VplError> {
macro_rules! sym {
($name:literal) => {{
match unsafe { lib.get(concat!($name, "\0").as_bytes()) } {
Ok(sym) => *sym,
Err(_) => {
return Err(VplError::MissingSymbol { symbol: $name });
}
}
}};
}
let fn_init_ex = sym!("MFXInitEx");
let fn_close = sym!("MFXClose");
let fn_query_version = sym!("MFXQueryVersion");
let fn_query_impl = sym!("MFXQueryIMPL");
let fn_encode_query = sym!("MFXVideoENCODE_Query");
let fn_encode_init = sym!("MFXVideoENCODE_Init");
let fn_encode_close = sym!("MFXVideoENCODE_Close");
let fn_encode_frame_async = sym!("MFXVideoENCODE_EncodeFrameAsync");
let fn_core_sync_operation = sym!("MFXVideoCORE_SyncOperation");
let fn_core_set_handle = sym!("MFXVideoCORE_SetHandle");
Ok(Self {
_lib: lib,
fn_init_ex,
fn_close,
fn_query_version,
fn_query_impl,
fn_encode_query,
fn_encode_init,
fn_encode_close,
fn_encode_frame_async,
fn_core_sync_operation,
fn_core_set_handle,
})
}
pub fn create_session(self, impl_hint: mfxIMPL) -> Result<Session, VplError> {
let par = mfxInitParam {
Implementation: impl_hint,
..Default::default()
};
let mut session: mfxSession = std::ptr::null_mut();
let status = unsafe { (self.fn_init_ex)(par, &raw mut session) };
if !mfx_succeeded(status) {
return Err(VplError::Status {
call: "MFXInitEx",
status,
});
}
Ok(Session {
loader: self,
session,
})
}
}
pub struct Session {
loader: Loader,
session: mfxSession,
}
unsafe impl Send for Session {}
impl Session {
pub fn query_version(&mut self) -> Result<mfxVersion, VplError> {
let mut version = mfxVersion::default();
let status = unsafe { (self.loader.fn_query_version)(self.session, &raw mut version) };
Self::check("MFXQueryVersion", status)?;
Ok(version)
}
pub fn query_impl(&mut self) -> Result<mfxIMPL, VplError> {
let mut out: mfxIMPL = 0;
let status = unsafe { (self.loader.fn_query_impl)(self.session, &raw mut out) };
Self::check("MFXQueryIMPL", status)?;
Ok(out)
}
pub fn encode_query(&mut self, par: &mut mfxVideoParam) -> Result<mfxStatus, VplError> {
let mut out = *par;
let status =
unsafe { (self.loader.fn_encode_query)(self.session, &raw mut *par, &raw mut out) };
if status < MFX_ERR_NONE {
return Err(VplError::Status {
call: "MFXVideoENCODE_Query",
status,
});
}
*par = out;
Ok(status)
}
pub fn encode_init(&mut self, par: &mut mfxVideoParam) -> Result<(), VplError> {
let status = unsafe { (self.loader.fn_encode_init)(self.session, &raw mut *par) };
Self::check("MFXVideoENCODE_Init", status)
}
pub fn encode_close(&mut self) -> Result<(), VplError> {
let status = unsafe { (self.loader.fn_encode_close)(self.session) };
Self::check("MFXVideoENCODE_Close", status)
}
pub fn encode_frame_async(
&mut self,
surface: Option<&mut mfxFrameSurface1>,
bs: &mut mfxBitstream,
) -> Result<(mfxStatus, mfxSyncPoint), VplError> {
let surface_ptr = surface.map_or(std::ptr::null_mut(), std::ptr::from_mut);
let mut syncp: mfxSyncPoint = std::ptr::null_mut();
let status = unsafe {
(self.loader.fn_encode_frame_async)(
self.session,
std::ptr::null_mut(),
surface_ptr,
std::ptr::from_mut(bs),
&raw mut syncp,
)
};
Ok((status, syncp))
}
#[allow(
clippy::not_unsafe_ptr_arg_deref,
reason = "syncp (mfxSyncPoint) is an opaque handle this crate never dereferences itself \
— it is only ever forwarded verbatim to MFXVideoCORE_SyncOperation, which owns \
it; the raw pointer type just mirrors oneVPL's own opaque-handle C API"
)]
pub fn sync_operation(&mut self, syncp: mfxSyncPoint, wait_ms: u32) -> Result<(), VplError> {
let status = unsafe { (self.loader.fn_core_sync_operation)(self.session, syncp, wait_ms) };
Self::check("MFXVideoCORE_SyncOperation", status)
}
const fn check(call: &'static str, status: mfxStatus) -> Result<(), VplError> {
if mfx_succeeded(status) {
Ok(())
} else {
Err(VplError::Status { call, status })
}
}
}
impl Drop for Session {
fn drop(&mut self) {
let _status: mfxStatus = unsafe { (self.loader.fn_close)(self.session) };
}
}
#[cfg(test)]
#[path = "dispatcher_tests.rs"]
mod tests;