gstreamer/subprojects/gst-plugins-bad/sys/qsv/libmfx/dispatcher/linux/mfxloader.cpp
Seungha Yang 81d63948d1 qsv: Import libmfx API and dispatch code from oneVPL project
Copied from oneVPL project (https://github.com/oneapi-src/oneVPL)
v2022.0.3 tag at the commit of efc259f8b7ee5c334bca1a904a503186038bbbdd

This is corresponding to MFX API version 2.6

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/1408>
2022-02-08 10:05:35 +00:00

699 lines
22 KiB
C++

/*############################################################################
# Copyright (C) 2017-2020 Intel Corporation
#
# SPDX-License-Identifier: MIT
############################################################################*/
#include <assert.h>
#include <dlfcn.h>
#include <string.h>
#include <unistd.h>
#include <algorithm>
#include <list>
#include <memory>
#include <mutex>
#include <utility>
#include <vector>
#include "vpl/mfxvideo.h"
#include "linux/device_ids.h"
#include "linux/mfxloader.h"
namespace MFX {
#if defined(__x86_64__)
#define LIBMFXSW "libmfxsw64.so.1"
#define LIBMFXHW "libmfxhw64.so.1"
#define ONEVPLSW "libvplswref64.so.1"
#define ONEVPLHW "libmfx-gen.so.1.2"
#else
#error Unsupported architecture
#endif
#undef FUNCTION
#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) e##func_name,
enum Function {
eMFXInit,
eMFXInitEx,
eMFXClose,
eMFXJoinSession,
#include "linux/mfxvideo_functions.h"
eFunctionsNum,
eNoMoreFunctions = eFunctionsNum
};
// new functions for API 2.x
enum Function2 {
// 2.0
eMFXQueryImplsDescription = 0,
eMFXReleaseImplDescription,
eMFXMemory_GetSurfaceForVPP,
eMFXMemory_GetSurfaceForEncode,
eMFXMemory_GetSurfaceForDecode,
eMFXInitialize,
// 2.1
eMFXMemory_GetSurfaceForVPPOut,
eMFXVideoDECODE_VPP_Init,
eMFXVideoDECODE_VPP_DecodeFrameAsync,
eMFXVideoDECODE_VPP_Reset,
eMFXVideoDECODE_VPP_GetChannelParam,
eMFXVideoDECODE_VPP_Close,
eMFXVideoVPP_ProcessFrameAsync,
eFunctionsNum2,
};
struct FunctionsTable {
Function id;
const char *name;
mfxVersion version;
};
struct FunctionsTable2 {
Function2 id;
const char *name;
mfxVersion version;
};
#define VERSION(major, minor) \
{ \
{ minor, major } \
}
#undef FUNCTION
#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \
{ e##func_name, #func_name, API_VERSION },
static const FunctionsTable g_mfxFuncTable[] = {
{ eMFXInit, "MFXInit", VERSION(1, 0) },
{ eMFXInitEx, "MFXInitEx", VERSION(1, 14) },
{ eMFXClose, "MFXClose", VERSION(1, 0) },
{ eMFXJoinSession, "MFXJoinSession", VERSION(1, 1) },
#include "linux/mfxvideo_functions.h" // NOLINT(build/include)
{ eNoMoreFunctions }
};
static const FunctionsTable2 g_mfxFuncTable2[] = {
{ eMFXQueryImplsDescription, "MFXQueryImplsDescription", VERSION(2, 0) },
{ eMFXReleaseImplDescription, "MFXReleaseImplDescription", VERSION(2, 0) },
{ eMFXMemory_GetSurfaceForVPP, "MFXMemory_GetSurfaceForVPP", VERSION(2, 0) },
{ eMFXMemory_GetSurfaceForEncode, "MFXMemory_GetSurfaceForEncode", VERSION(2, 0) },
{ eMFXMemory_GetSurfaceForDecode, "MFXMemory_GetSurfaceForDecode", VERSION(2, 0) },
{ eMFXInitialize, "MFXInitialize", VERSION(2, 0) },
{ eMFXMemory_GetSurfaceForVPPOut, "MFXMemory_GetSurfaceForVPPOut", VERSION(2, 1) },
{ eMFXVideoDECODE_VPP_Init, "MFXVideoDECODE_VPP_Init", VERSION(2, 1) },
{ eMFXVideoDECODE_VPP_DecodeFrameAsync, "MFXVideoDECODE_VPP_DecodeFrameAsync", VERSION(2, 1) },
{ eMFXVideoDECODE_VPP_Reset, "MFXVideoDECODE_VPP_Reset", VERSION(2, 1) },
{ eMFXVideoDECODE_VPP_GetChannelParam, "MFXVideoDECODE_VPP_GetChannelParam", VERSION(2, 1) },
{ eMFXVideoDECODE_VPP_Close, "MFXVideoDECODE_VPP_Close", VERSION(2, 1) },
{ eMFXVideoVPP_ProcessFrameAsync, "MFXVideoVPP_ProcessFrameAsync", VERSION(2, 1) },
};
class LoaderCtx {
public:
mfxStatus Init(mfxInitParam &par,
mfxInitializationParam &vplParam,
mfxU16 *pDeviceID,
char *dllName);
mfxStatus Close();
inline void *getFunction(Function func) const {
return m_table[func];
}
inline void *getFunction2(Function2 func) const {
return m_table2[func];
}
inline mfxSession getSession() const {
return m_session;
}
inline mfxIMPL getImpl() const {
return m_implementation;
}
inline mfxVersion getVersion() const {
return m_version;
}
private:
std::shared_ptr<void> m_dlh;
mfxVersion m_version{};
mfxIMPL m_implementation{};
mfxSession m_session = nullptr;
void *m_table[eFunctionsNum]{};
void *m_table2[eFunctionsNum2]{};
};
std::shared_ptr<void> make_dlopen(const char *filename, int flags) {
return std::shared_ptr<void>(dlopen(filename, flags), [](void *handle) {
if (handle)
dlclose(handle);
});
}
mfxStatus LoaderCtx::Init(mfxInitParam &par,
mfxInitializationParam &vplParam,
mfxU16 *pDeviceID,
char *dllName) {
mfxStatus mfx_res = MFX_ERR_NONE;
std::vector<std::string> libs;
std::vector<Device> devices;
eMFXHWType msdk_platform;
// query graphics device_id
// if it is found on list of legacy devices, load MSDK RT
// otherwise load oneVPL RT
mfxU16 deviceID = 0;
mfx_res = get_devices(devices);
if (mfx_res == MFX_ERR_NOT_FOUND) {
// query failed
msdk_platform = MFX_HW_UNKNOWN;
}
else {
// query succeeded:
// may be a valid platform from listLegalDevIDs[] or MFX_HW_UNKNOWN
// if underlying device_id is unrecognized (i.e. new platform)
msdk_platform = devices[0].platform;
deviceID = devices[0].device_id;
}
if (pDeviceID)
*pDeviceID = deviceID;
if (dllName) {
// attempt to load only this DLL, fail if unsuccessful
std::string libToLoad(dllName);
libs.emplace_back(libToLoad);
}
else {
mfxIMPL implType = MFX_IMPL_BASETYPE(par.Implementation);
// add HW lib
if (implType == MFX_IMPL_AUTO || implType == MFX_IMPL_AUTO_ANY ||
(implType & MFX_IMPL_HARDWARE) || (implType & MFX_IMPL_HARDWARE_ANY)) {
if (msdk_platform == MFX_HW_UNKNOWN) {
// if not on list of known MSDK platforms, prefer oneVPL
libs.emplace_back(ONEVPLHW);
libs.emplace_back(MFX_MODULES_DIR "/" ONEVPLHW);
}
// use MSDK (fallback if oneVPL is not installed)
libs.emplace_back(LIBMFXHW);
libs.emplace_back(MFX_MODULES_DIR "/" LIBMFXHW);
}
// add SW lib (oneVPL only)
if (implType == MFX_IMPL_AUTO || implType == MFX_IMPL_AUTO_ANY ||
(implType & MFX_IMPL_SOFTWARE)) {
libs.emplace_back(ONEVPLSW);
libs.emplace_back(MFX_MODULES_DIR "/" ONEVPLSW);
}
}
// fail if libs is empty (invalid Implementation)
mfx_res = MFX_ERR_UNSUPPORTED;
for (auto &lib : libs) {
std::shared_ptr<void> hdl = make_dlopen(lib.c_str(), RTLD_LOCAL | RTLD_NOW);
if (hdl) {
do {
/* Loading functions table */
bool wrong_version = false;
for (int i = 0; i < eFunctionsNum; ++i) {
assert(i == g_mfxFuncTable[i].id);
m_table[i] = dlsym(hdl.get(), g_mfxFuncTable[i].name);
if (!m_table[i] && ((g_mfxFuncTable[i].version <= par.Version))) {
wrong_version = true;
break;
}
}
// if version >= 2.0, load these functions as well
if (par.Version.Major >= 2) {
for (int i = 0; i < eFunctionsNum2; ++i) {
assert(i == g_mfxFuncTable2[i].id);
m_table2[i] = dlsym(hdl.get(), g_mfxFuncTable2[i].name);
if (!m_table2[i] && (g_mfxFuncTable2[i].version <= par.Version)) {
wrong_version = true;
break;
}
}
}
if (wrong_version) {
mfx_res = MFX_ERR_UNSUPPORTED;
break;
}
if (par.Version.Major >= 2) {
// for API >= 2.0 call MFXInitialize instead of MFXInitEx
mfx_res =
((decltype(MFXInitialize) *)m_table2[eMFXInitialize])(vplParam, &m_session);
}
else {
if (m_table[eMFXInitEx]) {
// initialize with MFXInitEx if present (API >= 1.14)
mfx_res = ((decltype(MFXInitEx) *)m_table[eMFXInitEx])(par, &m_session);
}
else {
// initialize with MFXInit for API < 1.14
mfx_res = ((decltype(MFXInit) *)m_table[eMFXInit])(par.Implementation,
&(par.Version),
&m_session);
}
}
if (MFX_ERR_NONE != mfx_res) {
break;
}
// Below we just get some data and double check that we got what we have expected
// to get. Some of these checks are done inside mediasdk init function
mfx_res =
((decltype(MFXQueryVersion) *)m_table[eMFXQueryVersion])(m_session, &m_version);
if (MFX_ERR_NONE != mfx_res) {
break;
}
if (m_version < par.Version) {
mfx_res = MFX_ERR_UNSUPPORTED;
break;
}
mfx_res = ((decltype(MFXQueryIMPL) *)m_table[eMFXQueryIMPL])(m_session,
&m_implementation);
if (MFX_ERR_NONE != mfx_res) {
mfx_res = MFX_ERR_UNSUPPORTED;
break;
}
} while (false);
if (MFX_ERR_NONE == mfx_res) {
m_dlh = std::move(hdl);
break;
}
else {
Close();
}
}
}
return mfx_res;
}
mfxStatus LoaderCtx::Close() {
auto proc = (decltype(MFXClose) *)m_table[eMFXClose];
mfxStatus mfx_res = (proc) ? (*proc)(m_session) : MFX_ERR_NONE;
m_implementation = {};
m_version = {};
m_session = nullptr;
std::fill(std::begin(m_table), std::end(m_table), nullptr);
return mfx_res;
}
} // namespace MFX
// internal function - load a specific DLL, return unsupported if it fails
// vplParam is required for API >= 2.0 (load via MFXInitialize)
mfxStatus MFXInitEx2(mfxVersion version,
mfxInitializationParam vplParam,
mfxIMPL hwImpl,
mfxSession *session,
mfxU16 *deviceID,
char *dllName) {
if (!session)
return MFX_ERR_NULL_PTR;
*deviceID = 0;
// fill minimal 1.x parameters for Init to choose correct initialization path
mfxInitParam par = {};
par.Version = version;
// select first adapter if not specified
// only relevant for MSDK-via-MFXLoad path
if (!hwImpl)
hwImpl = MFX_IMPL_HARDWARE;
switch (vplParam.AccelerationMode) {
case MFX_ACCEL_MODE_NA:
par.Implementation = MFX_IMPL_SOFTWARE;
break;
case MFX_ACCEL_MODE_VIA_D3D9:
par.Implementation = hwImpl | MFX_IMPL_VIA_D3D9;
break;
case MFX_ACCEL_MODE_VIA_D3D11:
par.Implementation = hwImpl | MFX_IMPL_VIA_D3D11;
break;
case MFX_ACCEL_MODE_VIA_VAAPI:
par.Implementation = hwImpl | MFX_IMPL_VIA_VAAPI;
break;
default:
par.Implementation = hwImpl;
break;
}
// also pass extBuf array (if any) to MFXInitEx for 1.x API
par.NumExtParam = vplParam.NumExtParam;
par.ExtParam = (vplParam.NumExtParam ? vplParam.ExtParam : nullptr);
try {
std::unique_ptr<MFX::LoaderCtx> loader;
loader.reset(new MFX::LoaderCtx{});
mfxStatus mfx_res = loader->Init(par, vplParam, deviceID, dllName);
if (MFX_ERR_NONE == mfx_res) {
*session = (mfxSession)loader.release();
}
else {
*session = nullptr;
}
return mfx_res;
}
catch (...) {
return MFX_ERR_MEMORY_ALLOC;
}
}
#ifdef __cplusplus
extern "C" {
#endif
mfxStatus MFXInit(mfxIMPL impl, mfxVersion *ver, mfxSession *session) {
mfxInitParam par{};
par.Implementation = impl;
if (ver) {
par.Version = *ver;
}
else {
par.Version = VERSION(MFX_VERSION_MAJOR, MFX_VERSION_MINOR);
}
return MFXInitEx(par, session);
}
mfxStatus MFXInitEx(mfxInitParam par, mfxSession *session) {
if (!session)
return MFX_ERR_NULL_PTR;
const mfxIMPL implMethod = par.Implementation & (MFX_IMPL_VIA_ANY - 1);
mfxInitializationParam vplParam = {};
if (implMethod == MFX_IMPL_SOFTWARE) {
vplParam.AccelerationMode = MFX_ACCEL_MODE_NA;
}
else {
vplParam.AccelerationMode = MFX_ACCEL_MODE_VIA_VAAPI;
}
try {
std::unique_ptr<MFX::LoaderCtx> loader;
loader.reset(new MFX::LoaderCtx{});
mfxStatus mfx_res = loader->Init(par, vplParam, nullptr, nullptr);
if (MFX_ERR_NONE == mfx_res) {
*session = (mfxSession)loader.release();
}
else {
*session = nullptr;
}
return mfx_res;
}
catch (...) {
return MFX_ERR_MEMORY_ALLOC;
}
}
mfxStatus MFXClose(mfxSession session) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
try {
std::unique_ptr<MFX::LoaderCtx> loader((MFX::LoaderCtx *)session);
mfxStatus mfx_res = loader->Close();
if (mfx_res == MFX_ERR_UNDEFINED_BEHAVIOR) {
// It is possible, that there is an active child session.
// Can't unload library in this case.
loader.release();
}
return mfx_res;
}
catch (...) {
return MFX_ERR_MEMORY_ALLOC;
}
}
// passthrough functions to implementation
mfxStatus MFXMemory_GetSurfaceForVPP(mfxSession session, mfxFrameSurface1 **surface) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc = (decltype(MFXMemory_GetSurfaceForVPP) *)loader->getFunction2(
MFX::eMFXMemory_GetSurfaceForVPP);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), surface);
}
mfxStatus MFXMemory_GetSurfaceForVPPOut(mfxSession session, mfxFrameSurface1 **surface) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc = (decltype(MFXMemory_GetSurfaceForVPPOut) *)loader->getFunction2(
MFX::eMFXMemory_GetSurfaceForVPPOut);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), surface);
}
mfxStatus MFXMemory_GetSurfaceForEncode(mfxSession session, mfxFrameSurface1 **surface) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc = (decltype(MFXMemory_GetSurfaceForEncode) *)loader->getFunction2(
MFX::eMFXMemory_GetSurfaceForEncode);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), surface);
}
mfxStatus MFXMemory_GetSurfaceForDecode(mfxSession session, mfxFrameSurface1 **surface) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc = (decltype(MFXMemory_GetSurfaceForDecode) *)loader->getFunction2(
MFX::eMFXMemory_GetSurfaceForDecode);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), surface);
}
mfxStatus MFXVideoDECODE_VPP_Init(mfxSession session,
mfxVideoParam *decode_par,
mfxVideoChannelParam **vpp_par_array,
mfxU32 num_vpp_par) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc =
(decltype(MFXVideoDECODE_VPP_Init) *)loader->getFunction2(MFX::eMFXVideoDECODE_VPP_Init);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), decode_par, vpp_par_array, num_vpp_par);
}
mfxStatus MFXVideoDECODE_VPP_DecodeFrameAsync(mfxSession session,
mfxBitstream *bs,
mfxU32 *skip_channels,
mfxU32 num_skip_channels,
mfxSurfaceArray **surf_array_out) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc = (decltype(MFXVideoDECODE_VPP_DecodeFrameAsync) *)loader->getFunction2(
MFX::eMFXVideoDECODE_VPP_DecodeFrameAsync);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), bs, skip_channels, num_skip_channels, surf_array_out);
}
mfxStatus MFXVideoDECODE_VPP_Reset(mfxSession session,
mfxVideoParam *decode_par,
mfxVideoChannelParam **vpp_par_array,
mfxU32 num_vpp_par) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc =
(decltype(MFXVideoDECODE_VPP_Reset) *)loader->getFunction2(MFX::eMFXVideoDECODE_VPP_Reset);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), decode_par, vpp_par_array, num_vpp_par);
}
mfxStatus MFXVideoDECODE_VPP_GetChannelParam(mfxSession session,
mfxVideoChannelParam *par,
mfxU32 channel_id) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc = (decltype(MFXVideoDECODE_VPP_GetChannelParam) *)loader->getFunction2(
MFX::eMFXVideoDECODE_VPP_GetChannelParam);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), par, channel_id);
}
mfxStatus MFXVideoDECODE_VPP_Close(mfxSession session) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc =
(decltype(MFXVideoDECODE_VPP_Close) *)loader->getFunction2(MFX::eMFXVideoDECODE_VPP_Close);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession());
}
mfxStatus MFXVideoVPP_ProcessFrameAsync(mfxSession session,
mfxFrameSurface1 *in,
mfxFrameSurface1 **out) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
auto proc = (decltype(MFXVideoVPP_ProcessFrameAsync) *)loader->getFunction2(
MFX::eMFXVideoVPP_ProcessFrameAsync);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), in, out);
}
mfxStatus MFXJoinSession(mfxSession session, mfxSession child_session) {
if (!session || !child_session) {
return MFX_ERR_INVALID_HANDLE;
}
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
MFX::LoaderCtx *child_loader = (MFX::LoaderCtx *)child_session;
if (loader->getVersion().Version != child_loader->getVersion().Version) {
return MFX_ERR_INVALID_HANDLE;
}
auto proc = (decltype(MFXJoinSession) *)loader->getFunction(MFX::eMFXJoinSession);
if (!proc) {
return MFX_ERR_INVALID_HANDLE;
}
return (*proc)(loader->getSession(), child_loader->getSession());
}
mfxStatus MFXCloneSession(mfxSession session, mfxSession *clone) {
if (!session)
return MFX_ERR_INVALID_HANDLE;
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session;
mfxVersion version = loader->getVersion();
// initialize the clone session
// currently supported for 1.x API only
// for 2.x runtimes, need to use RT implementation (passthrough)
if (version.Major == 1) {
mfxStatus mfx_res = MFXInit(loader->getImpl(), &version, clone);
if (MFX_ERR_NONE != mfx_res) {
return mfx_res;
}
// join the sessions
mfx_res = MFXJoinSession(session, *clone);
if (MFX_ERR_NONE != mfx_res) {
MFXClose(*clone);
*clone = nullptr;
return mfx_res;
}
}
else {
return MFX_ERR_UNSUPPORTED;
}
return MFX_ERR_NONE;
}
#undef FUNCTION
#define FUNCTION(return_value, func_name, formal_param_list, actual_param_list) \
return_value MFX_CDECL func_name formal_param_list { \
/* get the function's address and make a call */ \
if (!session) \
return MFX_ERR_INVALID_HANDLE; \
\
MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session; \
\
auto proc = (decltype(func_name) *)loader->getFunction(MFX::e##func_name); \
if (!proc) \
return MFX_ERR_INVALID_HANDLE; \
\
/* get the real session pointer */ \
session = loader->getSession(); \
/* pass down the call */ \
return (*proc)actual_param_list; \
}
#include "linux/mfxvideo_functions.h" // NOLINT(build/include)
#ifdef __cplusplus
}
#endif