/*############################################################################ # Copyright (C) Intel Corporation # # SPDX-License-Identifier: MIT ############################################################################*/ #include #include #include #include #include #include #include #include #include #include #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, bool bCloneSession = false); 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; } inline void *getHandle() const { return m_dlh.get(); } inline const char *getLibPath() const { return m_libToLoad.c_str(); } // special operations to set session pointer and version from MFXCloneSession() inline void setSession(const mfxSession session) { m_session = session; } inline void setVersion(const mfxVersion version) { m_version = version; } private: std::shared_ptr m_dlh; mfxVersion m_version{}; mfxIMPL m_implementation{}; mfxSession m_session = nullptr; void *m_table[eFunctionsNum]{}; void *m_table2[eFunctionsNum2]{}; std::string m_libToLoad; }; std::shared_ptr make_dlopen(const char *filename, int flags) { return std::shared_ptr(dlopen(filename, flags), [](void *handle) { if (handle) dlclose(handle); }); } mfxStatus LoaderCtx::Init(mfxInitParam &par, mfxInitializationParam &vplParam, mfxU16 *pDeviceID, char *dllName, bool bCloneSession) { mfxStatus mfx_res = MFX_ERR_NONE; std::vector libs; std::vector 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 // this may also be used later by MFXCloneSession() m_libToLoad = dllName; libs.emplace_back(m_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 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 (bCloneSession == true) { // success - exit loop since caller will create session with MFXCloneSession() mfx_res = MFX_ERR_NONE; 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); #ifdef ONEVPL_EXPERIMENTAL // if GPUCopy is enabled via MFXSetConfigProperty(DeviceCopy), set corresponding // flag in mfxInitParam for legacy RTs par.GPUCopy = vplParam.DeviceCopy; #endif try { std::unique_ptr 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 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 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()); } static mfxStatus AllocateCloneLoader(MFX::LoaderCtx *parentLoader, MFX::LoaderCtx **cloneLoader) { // initialization param structs are not used when bCloneSession == true mfxInitParam par = {}; mfxInitializationParam vplParam = {}; mfxU16 deviceID = 0; // initialization extBufs are not saved at this level // the RT should save these when the parent session is created and may use // them when creating the cloned session par.NumExtParam = 0; try { std::unique_ptr cl; cl.reset(new MFX::LoaderCtx{}); mfxStatus mfx_res = cl->Init(par, vplParam, &deviceID, (char *)parentLoader->getLibPath(), true); if (MFX_ERR_NONE == mfx_res) { *cloneLoader = cl.release(); } else { *cloneLoader = nullptr; } return mfx_res; } catch (...) { return MFX_ERR_MEMORY_ALLOC; } } mfxStatus MFXCloneSession(mfxSession session, mfxSession *clone) { if (!session || !clone) return MFX_ERR_INVALID_HANDLE; MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session; mfxVersion version = loader->getVersion(); *clone = nullptr; // initialize the clone session // for runtimes with 1.x API, call MFXInit followed by MFXJoinSession // for runtimes with 2.x API, use RT implementation of MFXCloneSession (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 if (version.Major == 2) { MFX::LoaderCtx *loader = (MFX::LoaderCtx *)session; // MFXCloneSession not included in function pointer search during init // for bwd-compat, check for it here and fail gracefully if missing void *libHandle = loader->getHandle(); auto proc = (decltype(MFXCloneSession) *)(dlsym(libHandle, "MFXCloneSession")); if (!proc) return MFX_ERR_UNSUPPORTED; // allocate new dispatcher-level session object and copy // state from parent session (function pointer tables, impl type, etc.) MFX::LoaderCtx *cloneLoader; mfxStatus mfx_res = AllocateCloneLoader(loader, &cloneLoader); if (mfx_res != MFX_ERR_NONE) return mfx_res; // call RT implementation of MFXCloneSession mfxSession cloneRT; mfx_res = (*proc)(loader->getSession(), &cloneRT); if (mfx_res != MFX_ERR_NONE || cloneRT == NULL) { // RT call failed, delete cloned loader (no valid session created) delete cloneLoader; return MFX_ERR_UNSUPPORTED; } cloneLoader->setSession(cloneRT); // get version of cloned session mfxVersion cloneVersion = {}; mfx_res = MFXQueryVersion((mfxSession)cloneLoader, &cloneVersion); cloneLoader->setVersion(cloneVersion); if (mfx_res != MFX_ERR_NONE) { MFXClose((mfxSession)cloneLoader); return mfx_res; } *clone = (mfxSession)cloneLoader; } 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