diff mbox series

[FFmpeg-devel,v6] avcodec/mfenc: Dynamically load MFPlat.DLL

Message ID 09de2d36-147f-a02d-987c-6fc34cdf110a@tytanium.xyz
State New
Headers show
Series [FFmpeg-devel,v6] avcodec/mfenc: Dynamically load MFPlat.DLL | expand

Checks

Context Check Description
yinshiyou/configure_loongarch64 warning Failed to apply patch
andriy/make_x86 success Make finished
andriy/make_fate_x86 success Make fate finished
andriy/make_armv7_RPi4 success Make finished
andriy/make_fate_armv7_RPi4 success Make fate finished

Commit Message

Trystan Mata May 25, 2022, 10:54 a.m. UTC
Changes since the v5:
   - Library handle and function pointer are back in MFContext.
     - MFTEnumEx has been move to it too.
   - dlopen and dlclose are preferred.

This will avoid multiple look up on one context. And each context will
have his own library handle.

Also, sorry for the e-mail earlier sent twice.

// Trystan

Comments

Martin Storsjö May 25, 2022, 8:51 p.m. UTC | #1
On Wed, 25 May 2022, Trystan Mata wrote:

> Changes since the v5:
>  - Library handle and function pointer are back in MFContext.
>    - MFTEnumEx has been move to it too.
>  - dlopen and dlclose are preferred.
>
> This will avoid multiple look up on one context. And each context will
> have his own library handle.

Thanks, this looks mostly good to me!

In testing of it, I noted that you still need to link against -lmfuuid 
when building with MSVC (but in mingw, those UUIDs are defined inline in 
headers I think).

Additionally, I realized I do prefer to keep using plain LoadLibraryA and 
FreeLibrary instead of using the wrapper, as this file is all quite 
Windows specific code; using the direct Windows APIs makes it clearer 
what's going on.

Finally, I removed the extra "" in the GetProcAddress call - I don't 
remember if you ever responded to why that was there.

With that, this builds fine with MSVC for desktop and for UWP, and with 
mingw for desktop.

As the patch seemed fine otherwise, I went ahead and pushed it. Thanks!

// Martin
Timo Rothenpieler May 25, 2022, 9:16 p.m. UTC | #2
On 25/05/2022 22:51, Martin Storsjö wrote:
> On Wed, 25 May 2022, Trystan Mata wrote:
> 
>> Changes since the v5:
>>  - Library handle and function pointer are back in MFContext.
>>    - MFTEnumEx has been move to it too.
>>  - dlopen and dlclose are preferred.
>>
>> This will avoid multiple look up on one context. And each context will
>> have his own library handle.
> 
> Thanks, this looks mostly good to me!
> 
> In testing of it, I noted that you still need to link against -lmfuuid 
> when building with MSVC (but in mingw, those UUIDs are defined inline in 
> headers I think).
> 
> Additionally, I realized I do prefer to keep using plain LoadLibraryA 
> and FreeLibrary instead of using the wrapper, as this file is all quite 
> Windows specific code; using the direct Windows APIs makes it clearer 
> what's going on.

There is quite a bit of work put into the wrapper to eliminate a bunch 
of common attack vectors with LoadLibrary and pitfalls with it on Windows.
So I'd strongly recommend to use it.
Martin Storsjö May 25, 2022, 9:34 p.m. UTC | #3
On Wed, 25 May 2022, Timo Rothenpieler wrote:

> On 25/05/2022 22:51, Martin Storsjö wrote:
>> On Wed, 25 May 2022, Trystan Mata wrote:
>> 
>>> Changes since the v5:
>>>  - Library handle and function pointer are back in MFContext.
>>>    - MFTEnumEx has been move to it too.
>>>  - dlopen and dlclose are preferred.
>>>
>>> This will avoid multiple look up on one context. And each context will
>>> have his own library handle.
>> 
>> Thanks, this looks mostly good to me!
>> 
>> In testing of it, I noted that you still need to link against -lmfuuid 
>> when building with MSVC (but in mingw, those UUIDs are defined inline in 
>> headers I think).
>> 
>> Additionally, I realized I do prefer to keep using plain LoadLibraryA 
>> and FreeLibrary instead of using the wrapper, as this file is all quite 
>> Windows specific code; using the direct Windows APIs makes it clearer 
>> what's going on.
>
> There is quite a bit of work put into the wrapper to eliminate a bunch 
> of common attack vectors with LoadLibrary and pitfalls with it on Windows.
> So I'd strongly recommend to use it.

Oh, thanks for catching it and letting me know, and sorry for pushing the 
patch with those changes, without taking it via another round of 
discussions. I'll send another patch to fix that.

// Martin
Trystan Mata May 26, 2022, 6:34 a.m. UTC | #4
> In testing of it, I noted that you still need to link against -lmfuuid
> when building with MSVC (but in mingw, those UUIDs are defined inline in
> headers I think).

Ah, I just used used mingw for my testing, sorry.

> Additionally, I realized I do prefer to keep using plain LoadLibraryA
> and FreeLibrary instead of using the wrapper, as this file is all quite
> Windows specific code; using the direct Windows APIs makes it clearer
> what's going on.

I'm not used at all to the Windows APIs, 

> Finally, I removed the extra "" in the GetProcAddress call - I don't
> remember if you ever responded to why that was there.

I didn't see the "Why the extra "" here?", it was the first time that I
did a macro with conversion to text. I just thought that at least a ""
was required to text convert with #.

> As the patch seemed fine otherwise, I went ahead and pushed it. Thanks!

Thank for your reviews !

// Trystan
diff mbox series

Patch

From 1f6a42a758e9bd7fca49f22ce96c39b59b059dc2 Mon Sep 17 00:00:00 2001
From: Trystan Mata <trystan.mata@tytanium.xyz>
Date: Wed, 25 May 2022 10:58:08 +0200
Subject: [PATCH] avcodec/mfenc: Dynamically load MediaFoundation library

Allows non-UWP builds of FFmpeg with MediaFoundation to work on
N editions of Windows which are without MediaFoundation by default.

On UWP target, FFmpeg is link directly against MediaFoundation since
LoadLibrary is not available.

This commit adresses https://trac.ffmpeg.org/ticket/9788
---
 configure             |  5 ++-
 libavcodec/mf_utils.c | 59 +++++++-------------------
 libavcodec/mf_utils.h | 35 ++++++++++------
 libavcodec/mfenc.c    | 96 +++++++++++++++++++++++++++++++++++++++----
 4 files changed, 128 insertions(+), 67 deletions(-)

diff --git a/configure b/configure
index f115b21064..432a0d163d 100755
--- a/configure
+++ b/configure
@@ -3130,7 +3130,6 @@  wmv3_vdpau_hwaccel_select="vc1_vdpau_hwaccel"
 
 # hardware-accelerated codecs
 mediafoundation_deps="mftransform_h MFCreateAlignedMemoryBuffer"
-mediafoundation_extralibs="-lmfplat -lmfuuid -lole32 -lstrmiids"
 omx_deps="libdl pthreads"
 omx_rpi_select="omx"
 qsv_deps="libmfx"
@@ -6876,6 +6875,10 @@  test_cpp <<EOF && enable uwp && d3d11va_extralibs="-ldxgi -ld3d11"
 #endif
 EOF
 
+# mediafoundation requires linking directly to mfplat and mfuuid if building
+# for uwp target
+enabled uwp && mediafoundation_extralibs="-lmfplat -lmfuuid -lole32 -lstrmiids" || mediafoundation_extralibs="-lole32 -lstrmiids"
+
 enabled libdrm &&
     check_pkg_config libdrm_getfb2 libdrm "xf86drmMode.h" drmModeGetFB2
 
diff --git a/libavcodec/mf_utils.c b/libavcodec/mf_utils.c
index eeabd0ce0b..48e3a63efc 100644
--- a/libavcodec/mf_utils.c
+++ b/libavcodec/mf_utils.c
@@ -47,39 +47,6 @@  HRESULT ff_MFSetAttributeSize(IMFAttributes *pattr, REFGUID guid,
 #define ff_MFSetAttributeRatio ff_MFSetAttributeSize
 #define ff_MFGetAttributeRatio ff_MFGetAttributeSize
 
-// MFTEnumEx was missing from mingw-w64's mfplat import library until
-// mingw-w64 v6.0.0, thus wrap it and load it using GetProcAddress.
-// It's also missing in Windows Vista's mfplat.dll.
-HRESULT ff_MFTEnumEx(GUID guidCategory, UINT32 Flags,
-                     const MFT_REGISTER_TYPE_INFO *pInputType,
-                     const MFT_REGISTER_TYPE_INFO *pOutputType,
-                     IMFActivate ***pppMFTActivate, UINT32 *pnumMFTActivate)
-{
-    HRESULT (WINAPI *MFTEnumEx_ptr)(GUID guidCategory, UINT32 Flags,
-                                    const MFT_REGISTER_TYPE_INFO *pInputType,
-                                    const MFT_REGISTER_TYPE_INFO *pOutputType,
-                                    IMFActivate ***pppMFTActivate,
-                                    UINT32 *pnumMFTActivate) = NULL;
-#if !HAVE_UWP
-    HANDLE lib = GetModuleHandleW(L"mfplat.dll");
-    if (lib)
-        MFTEnumEx_ptr = (void *)GetProcAddress(lib, "MFTEnumEx");
-#else
-    // In UWP (which lacks GetModuleHandle), just link directly against
-    // the functions - this requires building with new/complete enough
-    // import libraries.
-    MFTEnumEx_ptr = MFTEnumEx;
-#endif
-    if (!MFTEnumEx_ptr)
-        return E_FAIL;
-    return MFTEnumEx_ptr(guidCategory,
-                         Flags,
-                         pInputType,
-                         pOutputType,
-                         pppMFTActivate,
-                         pnumMFTActivate);
-}
-
 char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr)
 {
 #define HR(x) case x: return (char *) # x;
@@ -106,19 +73,20 @@  char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr)
 // If fill_data!=NULL, initialize the buffer and set the length. (This is a
 // subtle but important difference: some decoders want CurrentLength==0 on
 // provided output buffers.)
-IMFSample *ff_create_memory_sample(void *fill_data, size_t size, size_t align)
+IMFSample *ff_create_memory_sample(MFFunctions *f,void *fill_data, size_t size,
+                                   size_t align)
 {
     HRESULT hr;
     IMFSample *sample;
     IMFMediaBuffer *buffer;
 
-    hr = MFCreateSample(&sample);
+    hr = f->MFCreateSample(&sample);
     if (FAILED(hr))
         return NULL;
 
     align = FFMAX(align, 16); // 16 is "recommended", even if not required
 
-    hr = MFCreateAlignedMemoryBuffer(size, align - 1, &buffer);
+    hr = f->MFCreateAlignedMemoryBuffer(size, align - 1, &buffer);
     if (FAILED(hr))
         return NULL;
 
@@ -548,7 +516,7 @@  const CLSID *ff_codec_to_mf_subtype(enum AVCodecID codec)
     }
 }
 
-static int init_com_mf(void *log)
+static int init_com_mf(void *log, MFFunctions *f)
 {
     HRESULT hr;
 
@@ -561,7 +529,7 @@  static int init_com_mf(void *log)
         return AVERROR(ENOSYS);
     }
 
-    hr = MFStartup(MF_VERSION, MFSTARTUP_FULL);
+    hr = f->MFStartup(MF_VERSION, MFSTARTUP_FULL);
     if (FAILED(hr)) {
         av_log(log, AV_LOG_ERROR, "could not initialize MediaFoundation\n");
         CoUninitialize();
@@ -571,15 +539,16 @@  static int init_com_mf(void *log)
     return 0;
 }
 
-static void uninit_com_mf(void)
+static void uninit_com_mf(MFFunctions *f)
 {
-    MFShutdown();
+    f->MFShutdown();
     CoUninitialize();
 }
 
 // Find and create a IMFTransform with the given input/output types. When done,
 // you should use ff_free_mf() to destroy it, which will also uninit COM.
 int ff_instantiate_mf(void *log,
+                      MFFunctions *f,
                       GUID category,
                       MFT_REGISTER_TYPE_INFO *in_type,
                       MFT_REGISTER_TYPE_INFO *out_type,
@@ -594,7 +563,7 @@  int ff_instantiate_mf(void *log,
     IMFActivate *winner = 0;
     UINT32 flags;
 
-    ret = init_com_mf(log);
+    ret = init_com_mf(log, f);
     if (ret < 0)
         return ret;
 
@@ -606,7 +575,7 @@  int ff_instantiate_mf(void *log,
         flags |= MFT_ENUM_FLAG_SYNCMFT;
     }
 
-    hr = ff_MFTEnumEx(category, flags, in_type, out_type, &activate,
+    hr = f->MFTEnumEx(category, flags, in_type, out_type, &activate,
                       &num_activate);
     if (FAILED(hr))
         goto error_uninit_mf;
@@ -667,14 +636,14 @@  int ff_instantiate_mf(void *log,
     return 0;
 
 error_uninit_mf:
-    uninit_com_mf();
+    uninit_com_mf(f);
     return AVERROR(ENOSYS);
 }
 
-void ff_free_mf(IMFTransform **mft)
+void ff_free_mf(MFFunctions *f, IMFTransform **mft)
 {
     if (*mft)
         IMFTransform_Release(*mft);
     *mft = NULL;
-    uninit_com_mf();
+    uninit_com_mf(f);
 }
diff --git a/libavcodec/mf_utils.h b/libavcodec/mf_utils.h
index d514723c3b..3b12344f3e 100644
--- a/libavcodec/mf_utils.h
+++ b/libavcodec/mf_utils.h
@@ -41,6 +41,25 @@ 
 
 #include "avcodec.h"
 
+// Windows N editions does not provide MediaFoundation by default.
+// So to avoid DLL loading error, MediaFoundation will be dynamically loaded
+// except on UWP build since LoadLibrary is not available on it.
+typedef struct MFFunctions {
+    HRESULT (WINAPI *MFStartup) (ULONG Version, DWORD dwFlags);
+    HRESULT (WINAPI *MFShutdown) (void);
+    HRESULT (WINAPI *MFCreateAlignedMemoryBuffer) (DWORD cbMaxLength,
+                                                   DWORD cbAligment,
+                                                   IMFMediaBuffer **ppBuffer);
+    HRESULT (WINAPI *MFCreateSample) (IMFSample **ppIMFSample);
+    HRESULT (WINAPI *MFCreateMediaType) (IMFMediaType **ppMFType);
+    // MFTEnumEx is missing in Windows Vista's mfplat.dll.
+    HRESULT (WINAPI *MFTEnumEx)(GUID guidCategory, UINT32 Flags,
+                                const MFT_REGISTER_TYPE_INFO *pInputType,
+                                const MFT_REGISTER_TYPE_INFO *pOutputType,
+                                IMFActivate ***pppMFTActivate,
+                                UINT32 *pnumMFTActivate);
+} MFFunctions;
+
 // These functions do exist in mfapi.h, but are only available within
 // __cplusplus ifdefs.
 HRESULT ff_MFGetAttributeSize(IMFAttributes *pattr, REFGUID guid,
@@ -50,15 +69,6 @@  HRESULT ff_MFSetAttributeSize(IMFAttributes *pattr, REFGUID guid,
 #define ff_MFSetAttributeRatio ff_MFSetAttributeSize
 #define ff_MFGetAttributeRatio ff_MFGetAttributeSize
 
-// MFTEnumEx was missing from mingw-w64's mfplat import library until
-// mingw-w64 v6.0.0, thus wrap it and load it using GetProcAddress.
-// It's also missing in Windows Vista's mfplat.dll.
-HRESULT ff_MFTEnumEx(GUID guidCategory, UINT32 Flags,
-                     const MFT_REGISTER_TYPE_INFO *pInputType,
-                     const MFT_REGISTER_TYPE_INFO *pOutputType,
-                     IMFActivate ***pppMFTActivate, UINT32 *pnumMFTActivate);
-
-
 // These do exist in mingw-w64's codecapi.h, but they aren't properly defined
 // by the header until after mingw-w64 v7.0.0.
 DEFINE_GUID(ff_CODECAPI_AVDecVideoThumbnailGenerationMode, 0x2efd8eee,0x1150,0x4328,0x9c,0xf5,0x66,0xdc,0xe9,0x33,0xfc,0xf4);
@@ -150,7 +160,8 @@  char *ff_hr_str_buf(char *buf, size_t size, HRESULT hr);
 #define FF_VAL_VT_UI4(v) FF_VARIANT_VALUE(VT_UI4, .ulVal = (v))
 #define FF_VAL_VT_BOOL(v) FF_VARIANT_VALUE(VT_BOOL, .boolVal = (v))
 
-IMFSample *ff_create_memory_sample(void *fill_data, size_t size, size_t align);
+IMFSample *ff_create_memory_sample(MFFunctions *f, void *fill_data,
+                                   size_t size, size_t align);
 enum AVSampleFormat ff_media_type_to_sample_fmt(IMFAttributes *type);
 enum AVPixelFormat ff_media_type_to_pix_fmt(IMFAttributes *type);
 const GUID *ff_pix_fmt_to_guid(enum AVPixelFormat pix_fmt);
@@ -160,10 +171,10 @@  char *ff_guid_str_buf(char *buf, size_t buf_size, const GUID *guid);
 void ff_attributes_dump(void *log, IMFAttributes *attrs);
 void ff_media_type_dump(void *log, IMFMediaType *type);
 const CLSID *ff_codec_to_mf_subtype(enum AVCodecID codec);
-int ff_instantiate_mf(void *log, GUID category,
+int ff_instantiate_mf(void *log, MFFunctions *f, GUID category,
                       MFT_REGISTER_TYPE_INFO *in_type,
                       MFT_REGISTER_TYPE_INFO *out_type,
                       int use_hw, IMFTransform **res);
-void ff_free_mf(IMFTransform **mft);
+void ff_free_mf(MFFunctions *f, IMFTransform **mft);
 
 #endif
diff --git a/libavcodec/mfenc.c b/libavcodec/mfenc.c
index 280941cf2e..6eaea429da 100644
--- a/libavcodec/mfenc.c
+++ b/libavcodec/mfenc.c
@@ -29,9 +29,12 @@ 
 #include "libavutil/time.h"
 #include "codec_internal.h"
 #include "internal.h"
+#include "compat/w32dlfcn.h"
 
 typedef struct MFContext {
     AVClass *av_class;
+    HMODULE library;
+    MFFunctions functions;
     AVFrame *frame;
     int is_video, is_audio;
     GUID main_subtype;
@@ -292,7 +295,8 @@  static IMFSample *mf_a_avframe_to_sample(AVCodecContext *avctx, const AVFrame *f
     bps = av_get_bytes_per_sample(avctx->sample_fmt) * avctx->ch_layout.nb_channels;
     len = frame->nb_samples * bps;
 
-    sample = ff_create_memory_sample(frame->data[0], len, c->in_info.cbAlignment);
+    sample = ff_create_memory_sample(&c->functions, frame->data[0], len,
+                                     c->in_info.cbAlignment);
     if (sample)
         IMFSample_SetSampleDuration(sample, mf_to_mf_time(avctx, frame->nb_samples));
     return sample;
@@ -312,7 +316,8 @@  static IMFSample *mf_v_avframe_to_sample(AVCodecContext *avctx, const AVFrame *f
     if (size < 0)
         return NULL;
 
-    sample = ff_create_memory_sample(NULL, size, c->in_info.cbAlignment);
+    sample = ff_create_memory_sample(&c->functions, NULL, size,
+                                     c->in_info.cbAlignment);
     if (!sample)
         return NULL;
 
@@ -422,7 +427,9 @@  static int mf_receive_sample(AVCodecContext *avctx, IMFSample **out_sample)
         }
 
         if (!c->out_stream_provides_samples) {
-            sample = ff_create_memory_sample(NULL, c->out_info.cbSize, c->out_info.cbAlignment);
+            sample = ff_create_memory_sample(&c->functions, NULL,
+                                             c->out_info.cbSize,
+                                             c->out_info.cbAlignment);
             if (!sample)
                 return AVERROR(ENOMEM);
         }
@@ -777,7 +784,7 @@  static int mf_choose_output_type(AVCodecContext *avctx)
     if (out_type) {
         av_log(avctx, AV_LOG_VERBOSE, "picking output type %d.\n", out_type_index);
     } else {
-        hr = MFCreateMediaType(&out_type);
+        hr = c->functions.MFCreateMediaType(&out_type);
         if (FAILED(hr)) {
             ret = AVERROR(ENOMEM);
             goto done;
@@ -1005,7 +1012,8 @@  err:
     return res;
 }
 
-static int mf_create(void *log, IMFTransform **mft, const AVCodec *codec, int use_hw)
+static int mf_create(void *log, MFFunctions *f, IMFTransform **mft,
+                     const AVCodec *codec, int use_hw)
 {
     int is_audio = codec->type == AVMEDIA_TYPE_AUDIO;
     const CLSID *subtype = ff_codec_to_mf_subtype(codec->id);
@@ -1028,13 +1036,13 @@  static int mf_create(void *log, IMFTransform **mft, const AVCodec *codec, int us
         category = MFT_CATEGORY_VIDEO_ENCODER;
     }
 
-    if ((ret = ff_instantiate_mf(log, category, NULL, &reg, use_hw, mft)) < 0)
+    if ((ret = ff_instantiate_mf(log, f, category, NULL, &reg, use_hw, mft)) < 0)
         return ret;
 
     return 0;
 }
 
-static int mf_init(AVCodecContext *avctx)
+static int mf_init_encoder(AVCodecContext *avctx)
 {
     MFContext *c = avctx->priv_data;
     HRESULT hr;
@@ -1058,7 +1066,7 @@  static int mf_init(AVCodecContext *avctx)
 
     c->main_subtype = *subtype;
 
-    if ((ret = mf_create(avctx, &c->mft, avctx->codec, use_hw)) < 0)
+    if ((ret = mf_create(avctx, &c->functions, &c->mft, avctx->codec, use_hw)) < 0)
         return ret;
 
     if ((ret = mf_unlock_async(avctx)) < 0)
@@ -1122,6 +1130,56 @@  static int mf_init(AVCodecContext *avctx)
     return 0;
 }
 
+#if !HAVE_UWP
+#define LOAD_MF_FUNCTION(context, func_name) \
+    context->functions.func_name = (void *)GetProcAddress(context->library, #func_name ""); \
+    if (!context->functions.func_name) \
+    { \
+        av_log(context, AV_LOG_ERROR, "DLL mfplat.dll failed to find function "\
+           #func_name "\n"); \
+        return AVERROR_UNKNOWN; \
+    }
+#else
+// In UWP (which lacks LoadLibrary), just link directly against
+// the functions - this requires building with new/complete enough
+// import libraries.
+#define LOAD_MF_FUNCTION(context, func_name) \
+    context->functions.func_name = func_name; \
+    if (!context->functions.func_name) \
+    { \
+        av_log(context, AV_LOG_ERROR, "Failed to find function " #func_name \
+               "\n"); \
+        return AVERROR_UNKNOWN; \
+    }
+#endif
+
+// Windows N editions does not provide MediaFoundation by default.
+// So to avoid DLL loading error, MediaFoundation is dynamically loaded except
+// on UWP build since LoadLibrary is not available on it.
+static int mf_load_library(AVCodecContext *avctx)
+{
+    MFContext *c = avctx->priv_data;
+
+#if !HAVE_UWP
+    c->library = dlopen("mfplat.dll", 0);
+
+    if (!c->library) {
+        av_log(c, AV_LOG_ERROR, "DLL mfplat.dll failed to open\n");
+        return AVERROR_UNKNOWN;
+    }
+#endif
+
+    LOAD_MF_FUNCTION(c, MFStartup);
+    LOAD_MF_FUNCTION(c, MFShutdown);
+    LOAD_MF_FUNCTION(c, MFCreateAlignedMemoryBuffer);
+    LOAD_MF_FUNCTION(c, MFCreateSample);
+    LOAD_MF_FUNCTION(c, MFCreateMediaType);
+    // MFTEnumEx is missing in Windows Vista's mfplat.dll.
+    LOAD_MF_FUNCTION(c, MFTEnumEx);
+
+    return 0;
+}
+
 static int mf_close(AVCodecContext *avctx)
 {
     MFContext *c = avctx->priv_data;
@@ -1132,7 +1190,15 @@  static int mf_close(AVCodecContext *avctx)
     if (c->async_events)
         IMFMediaEventGenerator_Release(c->async_events);
 
-    ff_free_mf(&c->mft);
+#if !HAVE_UWP
+    if (c->library)
+        ff_free_mf(&c->functions, &c->mft);
+
+    dlclose(c->library);
+    c->library = NULL;
+#else
+    ff_free_mf(&c->functions, &c->mft);
+#endif
 
     av_frame_free(&c->frame);
 
@@ -1142,6 +1208,18 @@  static int mf_close(AVCodecContext *avctx)
     return 0;
 }
 
+static int mf_init(AVCodecContext *avctx)
+{
+    int ret;
+    if ((ret = mf_load_library(avctx)) == 0) {
+           if ((ret = mf_init_encoder(avctx)) == 0) {
+                return 0;
+        }
+    }
+    mf_close(avctx);
+    return ret;
+}
+
 #define OFFSET(x) offsetof(MFContext, x)
 
 #define MF_ENCODER(MEDIATYPE, NAME, ID, OPTS, EXTRA) \
-- 
2.36.1