[3/3] Allow loading the OpenH264 library dynamically

Message ID 1468579396-35501-3-git-send-email-martin@martin.st
State New
Headers show

Commit Message

Martin Storsjö July 15, 2016, 10:43 a.m.
This simplifies use of the patent license, simplifying use with a
library that has been downloaded at runtime (making it possible
to actually load and run libavcodec before the corresponding
OpenH264 library exists).
---
This patch was sent for review earlier, and got some feedback, but it was
deferred back then due not knowing how much interest there would be in it
- see https://patches.libav.org/patch/56287/.

Now there seems to be interest
in it, see https://lists.libav.org/pipermail/libav-devel/2016-July/078139.html,
so I'm resending it, with the feedback from the previous round applied,
and updated on top of the latest master.
---
 configure                   |  8 ++++--
 libavcodec/Makefile         |  1 +
 libavcodec/libopenh264.c    |  4 +--
 libavcodec/libopenh264.h    |  5 +++-
 libavcodec/libopenh264dec.c | 61 ++++++++++++++++++++++++++++++++++++++++++---
 libavcodec/libopenh264enc.c | 49 +++++++++++++++++++++++++++++++++---
 6 files changed, 115 insertions(+), 13 deletions(-)

Comments

Diego Biurrun July 25, 2016, 11:24 a.m. | #1
On Fri, Jul 15, 2016 at 01:43:16PM +0300, Martin Storsjö wrote:
> This simplifies use of the patent license, simplifying use with a
> library that has been downloaded at runtime (making it possible
> to actually load and run libavcodec before the corresponding
> OpenH264 library exists).
> ---
> This patch was sent for review earlier, and got some feedback, but it was
> deferred back then due not knowing how much interest there would be in it
> - see https://patches.libav.org/patch/56287/.
> 
> Now there seems to be interest
> in it, see https://lists.libav.org/pipermail/libav-devel/2016-July/078139.html,
> so I'm resending it, with the feedback from the previous round applied,
> and updated on top of the latest master.
> ---
>  configure                   |  8 ++++--
>  libavcodec/Makefile         |  1 +
>  libavcodec/libopenh264.c    |  4 +--
>  libavcodec/libopenh264.h    |  5 +++-
>  libavcodec/libopenh264dec.c | 61 ++++++++++++++++++++++++++++++++++++++++++---
>  libavcodec/libopenh264enc.c | 49 +++++++++++++++++++++++++++++++++---
>  6 files changed, 115 insertions(+), 13 deletions(-)

I'm undecided about the general merits of this patch.

Some implementation remarks below...

> --- a/configure
> +++ b/configure
> @@ -4594,6 +4596,8 @@ enabled libopencore_amrnb && require libopencore_amrnb opencore-amrnb/interf_dec
>  enabled libopencv         && require_pkg_config opencv opencv/cv.h cvCreateImageHeader
>  enabled libopenh264       && require_pkg_config openh264 wels/codec_api.h WelsGetCodecVersion
> +enabled libopenh264_dyn   && { { check_header wels/codec_ver.h && enabled dyn_lib_open; } ||
> +                                 die "ERROR: OpenH264 1.3 header not found, or dlopen/LoadLibrary not found"; }

Not sure if mentioning the version number here is a good idea looking
forward.

> index 8eb7d36..f639ac5 100644
> --- a/libavcodec/Makefile
> +++ b/libavcodec/Makefile
> @@ -771,6 +771,7 @@ SKIPHEADERS                            += %_tablegen.h                  \
>  SKIPHEADERS-$(CONFIG_D3D11VA)          += d3d11va.h dxva2_internal.h
>  SKIPHEADERS-$(CONFIG_DXVA2)            += dxva2.h dxva2_internal.h
>  SKIPHEADERS-$(CONFIG_LIBSCHROEDINGER)  += libschroedinger.h
> +SKIPHEADERS-$(CONFIG_LIBOPENH264)      += libopenh264.h

This will also skip the header if LIBOPENH264_DYN is set, which I think
is not what you intend.

> --- a/libavcodec/libopenh264dec.c
> +++ b/libavcodec/libopenh264dec.c
> @@ -23,6 +23,9 @@
>  #include <wels/codec_ver.h>
>  
>  #include "libavutil/common.h"
> +#if CONFIG_LIBOPENH264_DYN
> +#include "libavutil/dyn_lib_open.h"
> +#endif

You are missing a config.h #include.

> @@ -239,4 +291,5 @@ AVCodec ff_libopenh264_decoder = {
>      .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1,
>      .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_THREADSAFE |
>                        FF_CODEC_CAP_INIT_CLEANUP,
> +    .priv_class     = &class,

unrelated?

> --- a/libavcodec/libopenh264enc.c
> +++ b/libavcodec/libopenh264enc.c
> @@ -24,6 +24,9 @@
>  
>  #include "libavutil/attributes.h"
>  #include "libavutil/common.h"
> +#if CONFIG_LIBOPENH264_DYN
> +#include "libavutil/dyn_lib_open.h"
> +#endif
>  #include "libavutil/opt.h"
>  #include "libavutil/internal.h"
>  #include "libavutil/intreadwrite.h"

You're missing a config.h #include.

> --- a/libavcodec/libopenh264dec.c
> +++ b/libavcodec/libopenh264dec.c
> @@ -67,15 +93,41 @@ static av_cold int svc_decode_init(AVCodecContext *avctx)
>      WelsTraceCallback callback_function;
> +    int (*create_func)(ISVCDecoder **ppDecoder);
> +    OpenH264Version (*get_version_func)(void);
> +
> +#if CONFIG_LIBOPENH264_DYN
> +    if (!s->libname) {
> +        av_log(avctx, AV_LOG_ERROR, "No library name provided\n");
> +        return AVERROR(EINVAL);
> +    }
> +    s->lib = load_library(s->libname);
> +    if (!s->lib) {
> +        av_log(avctx, AV_LOG_ERROR, "Unable to load %s\n", s->libname);
> +        return AVERROR(EINVAL);
> +    }
> +#endif
>  
> -    if ((err = ff_libopenh264_check_version(avctx)) < 0)
> +    if ((err = ff_libopenh264_check_version(avctx, get_version_func)) < 0)
>          return err;
>  
> --- a/libavcodec/libopenh264enc.c
> +++ b/libavcodec/libopenh264enc.c
> @@ -97,11 +112,37 @@ static av_cold int svc_encode_init(AVCodecContext *avctx)
>      WelsTraceCallback callback_function;
>      AVCPBProperties *props;
> +    int (*create_func)(ISVCEncoder **ppEncoder);
> +    OpenH264Version (*get_version_func)(void);
> +
> +#if CONFIG_LIBOPENH264_DYN
> +    if (!s->libname) {
> +        av_log(avctx, AV_LOG_ERROR, "No library name provided\n");
> +        return AVERROR(EINVAL);
> +    }
> +    s->lib = load_library(s->libname);
> +    if (!s->lib) {
> +        av_log(avctx, AV_LOG_ERROR, "Unable to load %s\n", s->libname);
> +        return AVERROR(EINVAL);
> +    }
> +#endif
>  
> -    if ((err = ff_libopenh264_check_version(avctx)) < 0)
> +    if ((err = ff_libopenh264_check_version(avctx, get_version_func)) < 0)
>          return err;

This looks similar enough that it could be shared between decoder and
encoder.

Diego
Martin Storsjö July 25, 2016, 11:35 a.m. | #2
On Mon, 25 Jul 2016, Diego Biurrun wrote:

> On Fri, Jul 15, 2016 at 01:43:16PM +0300, Martin Storsjö wrote:
>> This simplifies use of the patent license, simplifying use with a
>> library that has been downloaded at runtime (making it possible
>> to actually load and run libavcodec before the corresponding
>> OpenH264 library exists).
>> ---
>> This patch was sent for review earlier, and got some feedback, but it was
>> deferred back then due not knowing how much interest there would be in it
>> - see https://patches.libav.org/patch/56287/.
>> 
>> Now there seems to be interest
>> in it, see https://lists.libav.org/pipermail/libav-devel/2016-July/078139.html,
>> so I'm resending it, with the feedback from the previous round applied,
>> and updated on top of the latest master.
>> ---
>>  configure                   |  8 ++++--
>>  libavcodec/Makefile         |  1 +
>>  libavcodec/libopenh264.c    |  4 +--
>>  libavcodec/libopenh264.h    |  5 +++-
>>  libavcodec/libopenh264dec.c | 61 ++++++++++++++++++++++++++++++++++++++++++---
>>  libavcodec/libopenh264enc.c | 49 +++++++++++++++++++++++++++++++++---
>>  6 files changed, 115 insertions(+), 13 deletions(-)
>
> I'm undecided about the general merits of this patch.
>
> Some implementation remarks below...
>
>> --- a/configure
>> +++ b/configure
>> @@ -4594,6 +4596,8 @@ enabled libopencore_amrnb && require libopencore_amrnb opencore-amrnb/interf_dec
>>  enabled libopencv         && require_pkg_config opencv opencv/cv.h cvCreateImageHeader
>>  enabled libopenh264       && require_pkg_config openh264 wels/codec_api.h WelsGetCodecVersion
>> +enabled libopenh264_dyn   && { { check_header wels/codec_ver.h && enabled dyn_lib_open; } ||
>> +                                 die "ERROR: OpenH264 1.3 header not found, or dlopen/LoadLibrary not found"; }
>
> Not sure if mentioning the version number here is a good idea looking
> forward.

Right, removed.

>> index 8eb7d36..f639ac5 100644
>> --- a/libavcodec/Makefile
>> +++ b/libavcodec/Makefile
>> @@ -771,6 +771,7 @@ SKIPHEADERS                            += %_tablegen.h                  \
>>  SKIPHEADERS-$(CONFIG_D3D11VA)          += d3d11va.h dxva2_internal.h
>>  SKIPHEADERS-$(CONFIG_DXVA2)            += dxva2.h dxva2_internal.h
>>  SKIPHEADERS-$(CONFIG_LIBSCHROEDINGER)  += libschroedinger.h
>> +SKIPHEADERS-$(CONFIG_LIBOPENH264)      += libopenh264.h
>
> This will also skip the header if LIBOPENH264_DYN is set, which I think
> is not what you intend.

Hmm, indeed. Fixing that properly is a bit harder...

>> --- a/libavcodec/libopenh264dec.c
>> +++ b/libavcodec/libopenh264dec.c
>> @@ -23,6 +23,9 @@
>>  #include <wels/codec_ver.h>
>>
>>  #include "libavutil/common.h"
>> +#if CONFIG_LIBOPENH264_DYN
>> +#include "libavutil/dyn_lib_open.h"
>> +#endif
>
> You are missing a config.h #include.

Sure

>> @@ -239,4 +291,5 @@ AVCodec ff_libopenh264_decoder = {
>>      .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1,
>>      .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_THREADSAFE |
>>                        FF_CODEC_CAP_INIT_CLEANUP,
>> +    .priv_class     = &class,
>
> unrelated?

No, it's for hooking up the options (for setting the library name to load; 
previously the decoder wrapper didn't have any options).

>> --- a/libavcodec/libopenh264enc.c
>> +++ b/libavcodec/libopenh264enc.c
>> @@ -24,6 +24,9 @@
>>
>>  #include "libavutil/attributes.h"
>>  #include "libavutil/common.h"
>> +#if CONFIG_LIBOPENH264_DYN
>> +#include "libavutil/dyn_lib_open.h"
>> +#endif
>>  #include "libavutil/opt.h"
>>  #include "libavutil/internal.h"
>>  #include "libavutil/intreadwrite.h"
>
> You're missing a config.h #include.
>
>> --- a/libavcodec/libopenh264dec.c
>> +++ b/libavcodec/libopenh264dec.c
>> @@ -67,15 +93,41 @@ static av_cold int svc_decode_init(AVCodecContext *avctx)
>>      WelsTraceCallback callback_function;
>> +    int (*create_func)(ISVCDecoder **ppDecoder);
>> +    OpenH264Version (*get_version_func)(void);
>> +
>> +#if CONFIG_LIBOPENH264_DYN
>> +    if (!s->libname) {
>> +        av_log(avctx, AV_LOG_ERROR, "No library name provided\n");
>> +        return AVERROR(EINVAL);
>> +    }
>> +    s->lib = load_library(s->libname);
>> +    if (!s->lib) {
>> +        av_log(avctx, AV_LOG_ERROR, "Unable to load %s\n", s->libname);
>> +        return AVERROR(EINVAL);
>> +    }
>> +#endif
>> 
>> -    if ((err = ff_libopenh264_check_version(avctx)) < 0)
>> +    if ((err = ff_libopenh264_check_version(avctx, get_version_func)) < 0)
>>          return err;
>> 
>> --- a/libavcodec/libopenh264enc.c
>> +++ b/libavcodec/libopenh264enc.c
>> @@ -97,11 +112,37 @@ static av_cold int svc_encode_init(AVCodecContext *avctx)
>>      WelsTraceCallback callback_function;
>>      AVCPBProperties *props;
>> +    int (*create_func)(ISVCEncoder **ppEncoder);
>> +    OpenH264Version (*get_version_func)(void);
>> +
>> +#if CONFIG_LIBOPENH264_DYN
>> +    if (!s->libname) {
>> +        av_log(avctx, AV_LOG_ERROR, "No library name provided\n");
>> +        return AVERROR(EINVAL);
>> +    }
>> +    s->lib = load_library(s->libname);
>> +    if (!s->lib) {
>> +        av_log(avctx, AV_LOG_ERROR, "Unable to load %s\n", s->libname);
>> +        return AVERROR(EINVAL);
>> +    }
>> +#endif
>> 
>> -    if ((err = ff_libopenh264_check_version(avctx)) < 0)
>> +    if ((err = ff_libopenh264_check_version(avctx, get_version_func)) < 0)
>>          return err;
>
> This looks similar enough that it could be shared between decoder and
> encoder.

No, I don't think it's really worth it, since it'd still end up something 
like this:

#if CONFIG_LIBOPENH264_DYN
if (load_library_ifdef_dyn_and_warn_if_not(s->libname))
     return AVERROR(EINVAL);
create_func = get_function()
#endif


You save a reused load_library call and error logging, nothing more, both 
which are trivial enough.

// Martin

Patch

diff --git a/configure b/configure
index ed32341..8dbc73b 100755
--- a/configure
+++ b/configure
@@ -204,6 +204,7 @@  External library support:
   --enable-libopencore-amrwb AMR-WB audio decoding
   --enable-libopencv         computer vision
   --enable-libopenh264       H.264 video encoding/decoding
+  --enable-libopenh264-dyn   H.264 video encoding/decoding via a dynamically loaded OpenH264
   --enable-libopenjpeg       JPEG 2000 image encoding/decoding
   --enable-libopus           Opus audio encoding/decoding
   --enable-libpulse          Pulseaudio sound server
@@ -1254,6 +1255,7 @@  EXTERNAL_LIBRARY_LIST="
     libopencore_amrwb
     libopencv
     libopenh264
+    libopenh264_dyn
     libopenjpeg
     libopus
     libpulse
@@ -2237,9 +2239,9 @@  libopencore_amrnb_decoder_deps="libopencore_amrnb"
 libopencore_amrnb_encoder_deps="libopencore_amrnb"
 libopencore_amrnb_encoder_select="audio_frame_queue"
 libopencore_amrwb_decoder_deps="libopencore_amrwb"
-libopenh264_decoder_deps="libopenh264"
+libopenh264_decoder_deps_any="libopenh264 libopenh264_dyn"
 libopenh264_decoder_select="h264_mp4toannexb_bsf"
-libopenh264_encoder_deps="libopenh264"
+libopenh264_encoder_deps_any="libopenh264 libopenh264_dyn"
 libopenjpeg_decoder_deps="libopenjpeg"
 libopenjpeg_encoder_deps="libopenjpeg"
 libopus_decoder_deps="libopus"
@@ -4594,6 +4596,8 @@  enabled libopencore_amrnb && require libopencore_amrnb opencore-amrnb/interf_dec
 enabled libopencore_amrwb && require libopencore_amrwb opencore-amrwb/dec_if.h D_IF_init -lopencore-amrwb
 enabled libopencv         && require_pkg_config opencv opencv/cv.h cvCreateImageHeader
 enabled libopenh264       && require_pkg_config openh264 wels/codec_api.h WelsGetCodecVersion
+enabled libopenh264_dyn   && { { check_header wels/codec_ver.h && enabled dyn_lib_open; } ||
+                                 die "ERROR: OpenH264 1.3 header not found, or dlopen/LoadLibrary not found"; }
 enabled libopenjpeg       && { { check_header openjpeg.h && check_lib2 openjpeg.h opj_version -lopenjpeg -DOPJ_STATIC; } ||
                                { require_pkg_config libopenjpeg1 openjpeg.h opj_version -DOPJ_STATIC; } }
 enabled libopus           && require_pkg_config opus opus_multistream.h opus_multistream_decoder_create
diff --git a/libavcodec/Makefile b/libavcodec/Makefile
index 8eb7d36..f639ac5 100644
--- a/libavcodec/Makefile
+++ b/libavcodec/Makefile
@@ -771,6 +771,7 @@  SKIPHEADERS                            += %_tablegen.h                  \
 SKIPHEADERS-$(CONFIG_D3D11VA)          += d3d11va.h dxva2_internal.h
 SKIPHEADERS-$(CONFIG_DXVA2)            += dxva2.h dxva2_internal.h
 SKIPHEADERS-$(CONFIG_LIBSCHROEDINGER)  += libschroedinger.h
+SKIPHEADERS-$(CONFIG_LIBOPENH264)      += libopenh264.h
 SKIPHEADERS-$(CONFIG_LIBVPX)           += libvpx.h
 SKIPHEADERS-$(CONFIG_MPEG_XVMC_DECODER) += xvmc.h
 SKIPHEADERS-$(CONFIG_NVENC)            += nvenc.h
diff --git a/libavcodec/libopenh264.c b/libavcodec/libopenh264.c
index 6252cfd..77bac8e 100644
--- a/libavcodec/libopenh264.c
+++ b/libavcodec/libopenh264.c
@@ -46,13 +46,13 @@  void ff_libopenh264_trace_callback(void *ctx, int level, const char *msg)
     av_log(ctx, equiv_libav_log_level, "%s\n", msg);
 }
 
-int ff_libopenh264_check_version(void *logctx)
+int ff_libopenh264_check_version(void *logctx, OpenH264Version (*get_version_func)(void))
 {
     // Mingw GCC < 4.7 on x86_32 uses an incorrect/buggy ABI for the WelsGetCodecVersion
     // function (for functions returning larger structs), thus skip the check in those
     // configurations.
 #if !defined(_WIN32) || !defined(__GNUC__) || !ARCH_X86_32 || AV_GCC_VERSION_AT_LEAST(4, 7)
-    OpenH264Version libver = WelsGetCodecVersion();
+    OpenH264Version libver = get_version_func();
     if (memcmp(&libver, &g_stCodecVersion, sizeof(libver))) {
         av_log(logctx, AV_LOG_ERROR, "Incorrect library version loaded\n");
         return AVERROR(EINVAL);
diff --git a/libavcodec/libopenh264.h b/libavcodec/libopenh264.h
index 7c69481..865cb00 100644
--- a/libavcodec/libopenh264.h
+++ b/libavcodec/libopenh264.h
@@ -22,6 +22,9 @@ 
 #ifndef AVCODEC_LIBOPENH264_H
 #define AVCODEC_LIBOPENH264_H
 
+#include <wels/codec_api.h>
+#include <wels/codec_ver.h>
+
 #define OPENH264_VER_AT_LEAST(maj, min) \
     ((OPENH264_MAJOR  > (maj)) || \
      (OPENH264_MAJOR == (maj) && OPENH264_MINOR >= (min)))
@@ -34,6 +37,6 @@ 
 
 void ff_libopenh264_trace_callback(void *ctx, int level, const char *msg);
 
-int ff_libopenh264_check_version(void *logctx);
+int ff_libopenh264_check_version(void *logctx, OpenH264Version (*get_version_func)(void));
 
 #endif /* AVCODEC_LIBOPENH264_H */
diff --git a/libavcodec/libopenh264dec.c b/libavcodec/libopenh264dec.c
index cc18f24..6f45782 100644
--- a/libavcodec/libopenh264dec.c
+++ b/libavcodec/libopenh264dec.c
@@ -23,6 +23,9 @@ 
 #include <wels/codec_ver.h>
 
 #include "libavutil/common.h"
+#if CONFIG_LIBOPENH264_DYN
+#include "libavutil/dyn_lib_open.h"
+#endif
 #include "libavutil/fifo.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/intreadwrite.h"
@@ -34,19 +37,38 @@ 
 #include "libopenh264.h"
 
 typedef struct SVCContext {
+    const AVClass *av_class;
+#if CONFIG_LIBOPENH264_DYN
+    const char *libname;
+    void *lib;
+#endif
+    void (*destroy_func)(ISVCDecoder *ppDecoder);
     ISVCDecoder *decoder;
     AVBSFContext *bsf;
     AVFifoBuffer *packet_fifo;
     AVPacket pkt_filtered;
 } SVCContext;
 
+#define OFFSET(x) offsetof(SVCContext, x)
+#define VD AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_DECODING_PARAM
+static const AVOption options[] = {
+#if CONFIG_LIBOPENH264_DYN
+    { "openh264lib", "OpenH264 library name", OFFSET(libname), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VD },
+#endif
+    { NULL }
+};
+
+static const AVClass class = {
+    "libopenh264dec", av_default_item_name, options, LIBAVUTIL_VERSION_INT
+};
+
 static av_cold int svc_decode_close(AVCodecContext *avctx)
 {
     SVCContext *s = avctx->priv_data;
     AVPacket pkt;
 
-    if (s->decoder)
-        WelsDestroyDecoder(s->decoder);
+    if (s->decoder && s->destroy_func)
+        s->destroy_func(s->decoder);
 
     while (s->packet_fifo && av_fifo_size(s->packet_fifo) >= sizeof(pkt)) {
         av_fifo_generic_read(s->packet_fifo, &pkt, sizeof(pkt), NULL);
@@ -56,6 +78,10 @@  static av_cold int svc_decode_close(AVCodecContext *avctx)
     av_bsf_free(&s->bsf);
     av_packet_unref(&s->pkt_filtered);
     av_fifo_free(s->packet_fifo);
+#if CONFIG_LIBOPENH264_DYN
+    if (s->lib)
+        free_library(s->lib);
+#endif
 
     return 0;
 }
@@ -67,15 +93,41 @@  static av_cold int svc_decode_init(AVCodecContext *avctx)
     int err;
     int log_level;
     WelsTraceCallback callback_function;
+    int (*create_func)(ISVCDecoder **ppDecoder);
+    OpenH264Version (*get_version_func)(void);
+
+#if CONFIG_LIBOPENH264_DYN
+    if (!s->libname) {
+        av_log(avctx, AV_LOG_ERROR, "No library name provided\n");
+        return AVERROR(EINVAL);
+    }
+    s->lib = load_library(s->libname);
+    if (!s->lib) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to load %s\n", s->libname);
+        return AVERROR(EINVAL);
+    }
+    create_func      = (void*) get_function(s->lib, "WelsCreateDecoder");
+    s->destroy_func  = (void*) get_function(s->lib, "WelsDestroyDecoder");
+    get_version_func = (void*) get_function(s->lib, "WelsGetCodecVersion");
+
+    if (!create_func || !s->destroy_func || !get_version_func) {
+        av_log(avctx, AV_LOG_ERROR, "%s doesn't contain the necessary functions\n", s->libname);
+        return AVERROR(EINVAL);
+    }
+#else
+    create_func = WelsCreateDecoder;
+    get_version_func = WelsGetCodecVersion;
+    s->destroy_func = WelsDestroyDecoder;
+#endif
 
-    if ((err = ff_libopenh264_check_version(avctx)) < 0)
+    if ((err = ff_libopenh264_check_version(avctx, get_version_func)) < 0)
         return err;
 
     s->packet_fifo = av_fifo_alloc(sizeof(AVPacket));
     if (!s->packet_fifo)
         return AVERROR(ENOMEM);
 
-    if (WelsCreateDecoder(&s->decoder)) {
+    if (create_func(&s->decoder)) {
         av_log(avctx, AV_LOG_ERROR, "Unable to create decoder\n");
         return AVERROR_UNKNOWN;
     }
@@ -239,4 +291,5 @@  AVCodec ff_libopenh264_decoder = {
     .capabilities   = AV_CODEC_CAP_DELAY | AV_CODEC_CAP_DR1,
     .caps_internal  = FF_CODEC_CAP_SETS_PKT_DTS | FF_CODEC_CAP_INIT_THREADSAFE |
                       FF_CODEC_CAP_INIT_CLEANUP,
+    .priv_class     = &class,
 };
diff --git a/libavcodec/libopenh264enc.c b/libavcodec/libopenh264enc.c
index a09b7cf..1d67fdb 100644
--- a/libavcodec/libopenh264enc.c
+++ b/libavcodec/libopenh264enc.c
@@ -24,6 +24,9 @@ 
 
 #include "libavutil/attributes.h"
 #include "libavutil/common.h"
+#if CONFIG_LIBOPENH264_DYN
+#include "libavutil/dyn_lib_open.h"
+#endif
 #include "libavutil/opt.h"
 #include "libavutil/internal.h"
 #include "libavutil/intreadwrite.h"
@@ -39,6 +42,11 @@ 
 
 typedef struct SVCContext {
     const AVClass *av_class;
+#if CONFIG_LIBOPENH264_DYN
+    const char *libname;
+    void *lib;
+#endif
+    void (*destroy_func)(ISVCEncoder *ppEncoder);
     ISVCEncoder *encoder;
     int slice_mode;
     int loopfilter;
@@ -52,6 +60,9 @@  typedef struct SVCContext {
 #define OFFSET(x) offsetof(SVCContext, x)
 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 static const AVOption options[] = {
+#if CONFIG_LIBOPENH264_DYN
+    { "openh264lib", "OpenH264 library name", OFFSET(libname), AV_OPT_TYPE_STRING, { 0 }, 0, 0, VE },
+#endif
 #if OPENH264_VER_AT_LEAST(1, 6)
     { "slice_mode", "Slice mode", OFFSET(slice_mode), AV_OPT_TYPE_INT, { .i64 = SM_FIXEDSLCNUM_SLICE }, SM_SINGLE_SLICE, SM_RESERVED, VE, "slice_mode" },
 #else
@@ -82,10 +93,14 @@  static av_cold int svc_encode_close(AVCodecContext *avctx)
 {
     SVCContext *s = avctx->priv_data;
 
-    if (s->encoder)
-        WelsDestroySVCEncoder(s->encoder);
+    if (s->encoder && s->destroy_func)
+        s->destroy_func(s->encoder);
     if (s->skipped > 0)
         av_log(avctx, AV_LOG_WARNING, "%d frames skipped\n", s->skipped);
+#if CONFIG_LIBOPENH264_DYN
+    if (s->lib)
+        free_library(s->lib);
+#endif
     return 0;
 }
 
@@ -97,11 +112,37 @@  static av_cold int svc_encode_init(AVCodecContext *avctx)
     int log_level;
     WelsTraceCallback callback_function;
     AVCPBProperties *props;
+    int (*create_func)(ISVCEncoder **ppEncoder);
+    OpenH264Version (*get_version_func)(void);
+
+#if CONFIG_LIBOPENH264_DYN
+    if (!s->libname) {
+        av_log(avctx, AV_LOG_ERROR, "No library name provided\n");
+        return AVERROR(EINVAL);
+    }
+    s->lib = load_library(s->libname);
+    if (!s->lib) {
+        av_log(avctx, AV_LOG_ERROR, "Unable to load %s\n", s->libname);
+        return AVERROR(EINVAL);
+    }
+    create_func      = (void*) get_function(s->lib, "WelsCreateSVCEncoder");
+    s->destroy_func  = (void*) get_function(s->lib, "WelsDestroySVCEncoder");
+    get_version_func = (void*) get_function(s->lib, "WelsGetCodecVersion");
+
+    if (!create_func || !s->destroy_func || !get_version_func) {
+        av_log(avctx, AV_LOG_ERROR, "%s doesn't contain the necessary functions\n", s->libname);
+        return AVERROR(EINVAL);
+    }
+#else
+    create_func = WelsCreateSVCEncoder;
+    get_version_func = WelsGetCodecVersion;
+    s->destroy_func = WelsDestroySVCEncoder;
+#endif
 
-    if ((err = ff_libopenh264_check_version(avctx)) < 0)
+    if ((err = ff_libopenh264_check_version(avctx, get_version_func)) < 0)
         return err;
 
-    if (WelsCreateSVCEncoder(&s->encoder)) {
+    if (create_func(&s->encoder)) {
         av_log(avctx, AV_LOG_ERROR, "Unable to create encoder\n");
         return AVERROR_UNKNOWN;
     }