Bug 1240995: [ffmpeg] Refactor FFmpeg PDM. r=kentuckyfriedtakahe
☠☠ backed out by e98ddda28b11 ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Wed, 20 Jan 2016 20:22:43 +1100
changeset 280810 8b78eccf2c296d54da1d8ed02d1293a8ff6138e3
parent 280809 dc949ca1e1ecc7eb5324fc4d5e510ebbc9e2ee0b
child 280811 1079f6d91f65f24c401133e6c313c7f405749c82
push id29922
push usercbook@mozilla.com
push dateThu, 21 Jan 2016 10:51:00 +0000
treeherdermozilla-central@977d78a8dd78 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskentuckyfriedtakahe
bugs1240995
milestone46.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1240995: [ffmpeg] Refactor FFmpeg PDM. r=kentuckyfriedtakahe This greatly simplify how the external libavcodec and libavutil are linked.
dom/media/platforms/PDMFactory.cpp
dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
dom/media/platforms/ffmpeg/FFmpegFunctionList.h
dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
dom/media/platforms/ffmpeg/FFmpegLibs.h
dom/media/platforms/ffmpeg/FFmpegLog.h
dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.h
dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.cpp
dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.h
dom/media/platforms/ffmpeg/moz.build
dom/media/platforms/moz.build
layout/build/nsLayoutStatics.cpp
--- a/dom/media/platforms/PDMFactory.cpp
+++ b/dom/media/platforms/PDMFactory.cpp
@@ -53,17 +53,16 @@ bool PDMFactory::sGonkDecoderEnabled = f
 #endif
 #ifdef MOZ_WIDGET_ANDROID
 bool PDMFactory::sAndroidMCDecoderEnabled = false;
 bool PDMFactory::sAndroidMCDecoderPreferred = false;
 #endif
 bool PDMFactory::sGMPDecoderEnabled = false;
 #ifdef MOZ_FFVPX
 bool PDMFactory::sFFVPXDecoderEnabled = false;
-using namespace ffvpx;
 #endif
 #ifdef MOZ_FFMPEG
 bool PDMFactory::sFFmpegDecoderEnabled = false;
 #endif
 #ifdef XP_WIN
 bool PDMFactory::sWMFDecoderEnabled = false;
 #endif
 
@@ -119,20 +118,20 @@ PDMFactory::Init()
 
 #ifdef XP_WIN
   WMFDecoderModule::Init();
 #endif
 #ifdef MOZ_APPLEMEDIA
   AppleDecoderModule::Init();
 #endif
 #ifdef MOZ_FFVPX
-  FFVPXRuntimeLinker::Link();
+  FFVPXRuntimeLinker::Init();
 #endif
 #ifdef MOZ_FFMPEG
-  FFmpegRuntimeLinker::Link();
+  FFmpegRuntimeLinker::Init();
 #endif
   GMPDecoderModule::Init();
 }
 
 PDMFactory::PDMFactory()
 {
   CreatePDMs();
 }
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -1,29 +1,28 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/TaskQueue.h"
 
-#include "FFmpegRuntimeLinker.h"
 #include "FFmpegAudioDecoder.h"
 #include "TimeUnits.h"
 
 #define MAX_CHANNELS 16
 
 namespace mozilla
 {
 
-FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(
+FFmpegAudioDecoder<LIBAV_VER>::FFmpegAudioDecoder(FFmpegLibWrapper* aLib,
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const AudioInfo& aConfig)
-  : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
+  : FFmpegDataDecoder(aLib, aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
 {
   MOZ_COUNT_CTOR(FFmpegAudioDecoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
   mExtraData->AppendElements(*aConfig.mCodecSpecificConfig);
 }
 
 RefPtr<MediaDataDecoder::InitPromise>
@@ -39,21 +38,19 @@ void
 FFmpegAudioDecoder<LIBAV_VER>::InitCodecContext()
 {
   MOZ_ASSERT(mCodecContext);
   // We do not want to set this value to 0 as FFmpeg by default will
   // use the number of cores, which with our mozlibavutil get_cpu_count
   // isn't implemented.
   mCodecContext->thread_count = 1;
   // FFmpeg takes this as a suggestion for what format to use for audio samples.
-  uint32_t major, minor, micro;
-  FFmpegRuntimeLinker::GetVersion(major, minor, micro);
   // LibAV 0.8 produces rubbish float interleaved samples, request 16 bits audio.
   mCodecContext->request_sample_fmt =
-    (major == 53) ? AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
+    (mLib->mVersion == 53) ? AV_SAMPLE_FMT_S16 : AV_SAMPLE_FMT_FLT;
 }
 
 static UniquePtr<AudioDataValue[]>
 CopyAndPackAudio(AVFrame* aFrame, uint32_t aNumChannels, uint32_t aNumAFrames)
 {
   MOZ_ASSERT(aNumChannels <= MAX_CHANNELS);
 
   auto audio = MakeUnique<AudioDataValue[]>(aNumChannels * aNumAFrames);
@@ -96,34 +93,34 @@ CopyAndPackAudio(AVFrame* aFrame, uint32
   return audio;
 }
 
 void
 FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   AVPacket packet;
-  AV_CALL(av_init_packet(&packet));
+  mLib->av_init_packet(&packet);
 
   packet.data = const_cast<uint8_t*>(aSample->Data());
   packet.size = aSample->Size();
 
   if (!PrepareFrame()) {
     NS_WARNING("FFmpeg audio decoder failed to allocate frame.");
     mCallback->Error();
     return;
   }
 
   int64_t samplePosition = aSample->mOffset;
   media::TimeUnit pts = media::TimeUnit::FromMicroseconds(aSample->mTime);
 
   while (packet.size > 0) {
     int decoded;
     int bytesConsumed =
-      AV_CALL(avcodec_decode_audio4(mCodecContext, mFrame, &decoded, &packet));
+      mLib->avcodec_decode_audio4(mCodecContext, mFrame, &decoded, &packet);
 
     if (bytesConsumed < 0) {
       NS_WARNING("FFmpeg audio decoder error.");
       mCallback->Error();
       return;
     }
 
     if (decoded) {
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.h
@@ -2,30 +2,31 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegAACDecoder_h__
 #define __FFmpegAACDecoder_h__
 
+#include "FFmpegLibWrapper.h"
 #include "FFmpegDataDecoder.h"
 
 namespace mozilla
 {
 
 template <int V> class FFmpegAudioDecoder
 {
 };
 
 template <>
 class FFmpegAudioDecoder<LIBAV_VER> : public FFmpegDataDecoder<LIBAV_VER>
 {
 public:
-  FFmpegAudioDecoder(FlushableTaskQueue* aTaskQueue,
+  FFmpegAudioDecoder(FFmpegLibWrapper* aLib, FlushableTaskQueue* aTaskQueue,
                      MediaDataDecoderCallback* aCallback,
                      const AudioInfo& aConfig);
   virtual ~FFmpegAudioDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
   void InitCodecContext() override;
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
@@ -13,53 +13,55 @@
 
 #include "FFmpegLog.h"
 #include "FFmpegDataDecoder.h"
 #include "prsystem.h"
 
 namespace mozilla
 {
 
-bool FFmpegDataDecoder<LIBAV_VER>::sFFmpegInitDone = false;
 StaticMutex FFmpegDataDecoder<LIBAV_VER>::sMonitor;
 
-FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
-                                                MediaDataDecoderCallback* aCallback,
-                                                AVCodecID aCodecID)
-  : mTaskQueue(aTaskQueue)
+  FFmpegDataDecoder<LIBAV_VER>::FFmpegDataDecoder(FFmpegLibWrapper* aLib,
+                                                  FlushableTaskQueue* aTaskQueue,
+                                                  MediaDataDecoderCallback* aCallback,
+                                                  AVCodecID aCodecID)
+  : mLib(aLib)
+  , mTaskQueue(aTaskQueue)
   , mCallback(aCallback)
   , mCodecContext(nullptr)
   , mFrame(NULL)
   , mExtraData(nullptr)
   , mCodecID(aCodecID)
   , mMonitor("FFMpegaDataDecoder")
   , mIsFlushing(false)
 {
+  MOZ_ASSERT(aLib);
   MOZ_COUNT_CTOR(FFmpegDataDecoder);
 }
 
 FFmpegDataDecoder<LIBAV_VER>::~FFmpegDataDecoder()
 {
   MOZ_COUNT_DTOR(FFmpegDataDecoder);
 }
 
 nsresult
 FFmpegDataDecoder<LIBAV_VER>::InitDecoder()
 {
   FFMPEG_LOG("Initialising FFmpeg decoder.");
 
-  AVCodec* codec = FindAVCodec(mCodecID);
+  AVCodec* codec = FindAVCodec(mLib, mCodecID);
   if (!codec) {
     NS_WARNING("Couldn't find ffmpeg decoder");
     return NS_ERROR_FAILURE;
   }
 
   StaticMutexAutoLock mon(sMonitor);
 
-  if (!(mCodecContext = AV_CALL(avcodec_alloc_context3(codec)))) {
+  if (!(mCodecContext = mLib->avcodec_alloc_context3(codec))) {
     NS_WARNING("Couldn't init ffmpeg context");
     return NS_ERROR_FAILURE;
   }
 
   mCodecContext->opaque = this;
 
   InitCodecContext();
 
@@ -72,20 +74,20 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
   } else {
     mCodecContext->extradata_size = 0;
   }
 
   if (codec->capabilities & CODEC_CAP_DR1) {
     mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
   }
 
-  if (AV_CALL(avcodec_open2(mCodecContext, codec, nullptr)) < 0) {
+  if (mLib->avcodec_open2(mCodecContext, codec, nullptr) < 0) {
     NS_WARNING("Couldn't initialise ffmpeg decoder");
-    AV_CALL(avcodec_close(mCodecContext));
-    AV_CALL(av_freep(&mCodecContext));
+    mLib->avcodec_close(mCodecContext);
+    mLib->av_freep(&mCodecContext);
     return NS_ERROR_FAILURE;
   }
 
   if (mCodecContext->codec_type == AVMEDIA_TYPE_AUDIO &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLT &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_FLTP &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16 &&
       mCodecContext->sample_fmt != AV_SAMPLE_FMT_S16P) {
@@ -136,73 +138,66 @@ FFmpegDataDecoder<LIBAV_VER>::Drain()
   return NS_OK;
 }
 
 void
 FFmpegDataDecoder<LIBAV_VER>::ProcessFlush()
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   if (mCodecContext) {
-    AV_CALL(avcodec_flush_buffers(mCodecContext));
+    mLib->avcodec_flush_buffers(mCodecContext);
   }
   MonitorAutoLock mon(mMonitor);
   mIsFlushing = false;
   mon.NotifyAll();
 }
 
 void
 FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown()
 {
   StaticMutexAutoLock mon(sMonitor);
 
-  if (sFFmpegInitDone && mCodecContext) {
-    AV_CALL(avcodec_close(mCodecContext));
-    AV_CALL(av_freep(&mCodecContext));
+  if (mCodecContext) {
+    mLib->avcodec_close(mCodecContext);
+    mLib->av_freep(&mCodecContext);
 #if LIBAVCODEC_VERSION_MAJOR >= 55
-    AV_CALL(av_frame_free(&mFrame));
+    mLib->av_frame_free(&mFrame);
 #elif LIBAVCODEC_VERSION_MAJOR == 54
-    AV_CALL(avcodec_free_frame(&mFrame));
+    mLib->avcodec_free_frame(&mFrame);
 #else
     delete mFrame;
     mFrame = nullptr;
 #endif
   }
 }
 
 AVFrame*
 FFmpegDataDecoder<LIBAV_VER>::PrepareFrame()
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 #if LIBAVCODEC_VERSION_MAJOR >= 55
   if (mFrame) {
-    AV_CALL(av_frame_unref(mFrame));
+    mLib->av_frame_unref(mFrame);
   } else {
-    mFrame = AV_CALL(av_frame_alloc());
+    mFrame = mLib->av_frame_alloc();
   }
 #elif LIBAVCODEC_VERSION_MAJOR == 54
   if (mFrame) {
-    AV_CALL(avcodec_get_frame_defaults(mFrame));
+    mLib->avcodec_get_frame_defaults(mFrame);
   } else {
-    mFrame = AV_CALL(avcodec_alloc_frame());
+    mFrame = mLib->avcodec_alloc_frame();
   }
 #else
   delete mFrame;
   mFrame = new AVFrame;
-  AV_CALL(avcodec_get_frame_defaults(mFrame));
+  mLib->avcodec_get_frame_defaults(mFrame);
 #endif
   return mFrame;
 }
 
 /* static */ AVCodec*
-FFmpegDataDecoder<LIBAV_VER>::FindAVCodec(AVCodecID aCodec)
+FFmpegDataDecoder<LIBAV_VER>::FindAVCodec(FFmpegLibWrapper* aLib,
+                                          AVCodecID aCodec)
 {
-  StaticMutexAutoLock mon(sMonitor);
-  if (!sFFmpegInitDone) {
-    AV_CALL(avcodec_register_all());
-#ifdef DEBUG
-    AV_CALL(av_log_set_level(AV_LOG_DEBUG));
-#endif
-    sFFmpegInitDone = true;
-  }
-  return AV_CALL(avcodec_find_decoder(aCodec));
+  return aLib->avcodec_find_decoder(aCodec);
 }
-  
+
 } // namespace mozilla
--- a/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.h
@@ -3,56 +3,57 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegDataDecoder_h__
 #define __FFmpegDataDecoder_h__
 
 #include "PlatformDecoderModule.h"
+#include "FFmpegLibWrapper.h"
+#include "mozilla/StaticMutex.h"
 #include "FFmpegLibs.h"
-#include "FFmpegRuntimeLinker.h"
-#include "mozilla/StaticMutex.h"
 
 namespace mozilla
 {
 
 template <int V>
 class FFmpegDataDecoder : public MediaDataDecoder
 {
 };
 
 template <>
 class FFmpegDataDecoder<LIBAV_VER> : public MediaDataDecoder
 {
 public:
-  FFmpegDataDecoder(FlushableTaskQueue* aTaskQueue,
+  FFmpegDataDecoder(FFmpegLibWrapper* aLib, FlushableTaskQueue* aTaskQueue,
                     MediaDataDecoderCallback* aCallback,
                     AVCodecID aCodecID);
   virtual ~FFmpegDataDecoder();
 
   static bool Link();
 
   RefPtr<InitPromise> Init() override = 0;
   nsresult Input(MediaRawData* aSample) override = 0;
   nsresult Flush() override;
   nsresult Drain() override;
   nsresult Shutdown() override;
 
-  static AVCodec* FindAVCodec(AVCodecID aCodec);
+  static AVCodec* FindAVCodec(FFmpegLibWrapper* aLib, AVCodecID aCodec);
 
 protected:
   // Flush and Drain operation, always run
   virtual void ProcessFlush();
   virtual void ProcessDrain() = 0;
   virtual void ProcessShutdown();
   virtual void InitCodecContext() {}
   AVFrame*        PrepareFrame();
   nsresult        InitDecoder();
 
+  FFmpegLibWrapper* mLib;
   RefPtr<FlushableTaskQueue> mTaskQueue;
   MediaDataDecoderCallback* mCallback;
 
   AVCodecContext* mCodecContext;
   AVFrame*        mFrame;
   RefPtr<MediaByteBuffer> mExtraData;
   AVCodecID mCodecID;
 
@@ -60,15 +61,14 @@ protected:
   // Protects mReorderQueue.
   Monitor mMonitor;
   // Set on reader/decode thread calling Flush() to indicate that output is
   // not required and so input samples on mTaskQueue need not be processed.
   // Cleared on mTaskQueue in ProcessDrain().
   Atomic<bool> mIsFlushing;
 
 private:
-  static bool sFFmpegInitDone;
   static StaticMutex sMonitor;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDataDecoder_h__
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
@@ -3,88 +3,91 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegDecoderModule_h__
 #define __FFmpegDecoderModule_h__
 
 #include "PlatformDecoderModule.h"
+#include "FFmpegLibWrapper.h"
 #include "FFmpegAudioDecoder.h"
 #include "FFmpegVideoDecoder.h"
 
 namespace mozilla
 {
 
 template <int V>
 class FFmpegDecoderModule : public PlatformDecoderModule
 {
 public:
   static already_AddRefed<PlatformDecoderModule>
-  Create()
+  Create(FFmpegLibWrapper* aLib)
   {
-    RefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule();
+    RefPtr<PlatformDecoderModule> pdm = new FFmpegDecoderModule(aLib);
 
     return pdm.forget();
   }
 
-  FFmpegDecoderModule() {}
+  explicit FFmpegDecoderModule(FFmpegLibWrapper* aLib) : mLib(aLib) {}
   virtual ~FFmpegDecoderModule() {}
 
   already_AddRefed<MediaDataDecoder>
   CreateVideoDecoder(const VideoInfo& aConfig,
                      layers::LayersBackend aLayersBackend,
                      layers::ImageContainer* aImageContainer,
                      FlushableTaskQueue* aVideoTaskQueue,
                      MediaDataDecoderCallback* aCallback) override
   {
     RefPtr<MediaDataDecoder> decoder =
-      new FFmpegVideoDecoder<V>(aVideoTaskQueue, aCallback, aConfig,
+      new FFmpegVideoDecoder<V>(mLib, aVideoTaskQueue, aCallback, aConfig,
                                 aImageContainer);
     return decoder.forget();
   }
 
   already_AddRefed<MediaDataDecoder>
   CreateAudioDecoder(const AudioInfo& aConfig,
                      FlushableTaskQueue* aAudioTaskQueue,
                      MediaDataDecoderCallback* aCallback) override
   {
 #ifdef USING_MOZFFVPX
     return nullptr;
 #else
     RefPtr<MediaDataDecoder> decoder =
-      new FFmpegAudioDecoder<V>(aAudioTaskQueue, aCallback, aConfig);
+      new FFmpegAudioDecoder<V>(mLib, aAudioTaskQueue, aCallback, aConfig);
     return decoder.forget();
 #endif
   }
 
   bool SupportsMimeType(const nsACString& aMimeType) const override
   {
 #ifdef USING_MOZFFVPX
     AVCodecID audioCodec = AV_CODEC_ID_NONE;
 #else
     AVCodecID audioCodec = FFmpegAudioDecoder<V>::GetCodecId(aMimeType);
 #endif
     AVCodecID videoCodec = FFmpegVideoDecoder<V>::GetCodecId(aMimeType);
     if (audioCodec == AV_CODEC_ID_NONE && videoCodec == AV_CODEC_ID_NONE) {
       return false;
     }
     AVCodecID codec = audioCodec != AV_CODEC_ID_NONE ? audioCodec : videoCodec;
-    return !!FFmpegDataDecoder<V>::FindAVCodec(codec);
+    return !!FFmpegDataDecoder<V>::FindAVCodec(mLib, codec);
   }
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override
   {
     if (aConfig.IsVideo() &&
         (aConfig.mMimeType.EqualsLiteral("video/avc") ||
          aConfig.mMimeType.EqualsLiteral("video/mp4"))) {
       return PlatformDecoderModule::kNeedAVCC;
     } else {
       return kNeedNone;
     }
   }
 
+private:
+  FFmpegLibWrapper* mLib;
 };
 
 } // namespace mozilla
 
 #endif // __FFmpegDecoderModule_h__
deleted file mode 100644
--- a/dom/media/platforms/ffmpeg/FFmpegFunctionList.h
+++ /dev/null
@@ -1,39 +0,0 @@
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-/* libavcodec */
-AV_FUNC(avcodec_alloc_context3, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(avcodec_close, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(avcodec_decode_audio4, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(avcodec_decode_video2, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(avcodec_find_decoder, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(avcodec_flush_buffers, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(avcodec_open2, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(avcodec_register_all, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(av_init_packet, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(av_parser_init, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(av_parser_close, AV_FUNC_AVCODEC_ALL)
-AV_FUNC(av_parser_parse2, AV_FUNC_AVCODEC_ALL)
-#if LIBAVCODEC_VERSION_MAJOR <= 54 || defined(LIBAVCODEC_ALLVERSION)
-AV_FUNC(avcodec_get_frame_defaults, (AV_FUNC_53 | AV_FUNC_54))
-#endif
-
-/* libavutil */
-AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL)
-AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL)
-AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL)
-
-#if defined(LIBAVCODEC_VERSION_MAJOR) || defined(LIBAVCODEC_ALLVERSION)
-#if LIBAVCODEC_VERSION_MAJOR == 54 || defined(LIBAVCODEC_ALLVERSION)
-/* libavutil v54 only */
-AV_FUNC(avcodec_alloc_frame, AV_FUNC_AVUTIL_54)
-AV_FUNC(avcodec_free_frame, AV_FUNC_AVUTIL_54)
-#endif
-#if LIBAVCODEC_VERSION_MAJOR >= 55 || defined(LIBAVCODEC_ALLVERSION)
-/* libavutil v55 and later only */
-AV_FUNC(av_frame_alloc, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
-AV_FUNC(av_frame_free, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
-AV_FUNC(av_frame_unref, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
-#endif
-#endif
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.cpp
@@ -0,0 +1,142 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "FFmpegLibWrapper.h"
+#include "FFmpegLog.h"
+#include "mozilla/PodOperations.h"
+#include "mozilla/Types.h"
+#include "prlink.h"
+
+#define AV_LOG_DEBUG    48
+
+namespace mozilla
+{
+
+FFmpegLibWrapper::FFmpegLibWrapper()
+{
+  PodZero(this);
+}
+
+FFmpegLibWrapper::~FFmpegLibWrapper()
+{
+  Unlink();
+}
+
+bool
+FFmpegLibWrapper::Link()
+{
+  if (!mAVCodecLib || !mAVUtilLib) {
+    Unlink();
+    return false;
+  }
+
+  avcodec_version =
+    (decltype(avcodec_version))PR_FindSymbol(mAVCodecLib, "avcodec_version");
+  if (!avcodec_version) {
+    Unlink();
+    return false;
+  }
+  uint32_t version = avcodec_version();
+  mVersion = (version >> 16) & 0xff;
+  uint32_t micro = version & 0xff;
+  if (mVersion == 57 && micro != 100) {
+    // a micro version of 100 indicates that it's FFmpeg (as opposed to LibAV).
+    // Due to current AVCodecContext binary incompatibility we can only
+    // support FFmpeg 57 at this stage.
+    Unlink();
+    return false;
+  }
+
+  enum {
+    AV_FUNC_AVUTIL_MASK = 1 << 8,
+    AV_FUNC_53 = 1 << 0,
+    AV_FUNC_54 = 1 << 1,
+    AV_FUNC_55 = 1 << 2,
+    AV_FUNC_56 = 1 << 3,
+    AV_FUNC_57 = 1 << 4,
+    AV_FUNC_AVUTIL_53 = AV_FUNC_53 | AV_FUNC_AVUTIL_MASK,
+    AV_FUNC_AVUTIL_54 = AV_FUNC_54 | AV_FUNC_AVUTIL_MASK,
+    AV_FUNC_AVUTIL_55 = AV_FUNC_55 | AV_FUNC_AVUTIL_MASK,
+    AV_FUNC_AVUTIL_56 = AV_FUNC_56 | AV_FUNC_AVUTIL_MASK,
+    AV_FUNC_AVUTIL_57 = AV_FUNC_57 | AV_FUNC_AVUTIL_MASK,
+    AV_FUNC_AVCODEC_ALL = AV_FUNC_53 | AV_FUNC_54 | AV_FUNC_55 | AV_FUNC_56 | AV_FUNC_57,
+    AV_FUNC_AVUTIL_ALL = AV_FUNC_AVCODEC_ALL | AV_FUNC_AVUTIL_MASK
+  };
+
+  switch (mVersion) {
+    case 53:
+      version = AV_FUNC_53;
+      break;
+    case 54:
+      version = AV_FUNC_54;
+      break;
+    case 55:
+      version = AV_FUNC_55;
+      break;
+    case 56:
+      version = AV_FUNC_56;
+      break;
+    case 57:
+      version = AV_FUNC_57;
+      break;
+    default:
+      FFMPEG_LOG("Unknown avcodec version");
+      Unlink();
+      return false;
+  }
+
+#define AV_FUNC(func, ver)                                                     \
+  if ((ver) & version) {                                                      \
+    if (!(func = (decltype(func))PR_FindSymbol(((ver) & AV_FUNC_AVUTIL_MASK) ? mAVUtilLib : mAVCodecLib, #func))) { \
+      FFMPEG_LOG("Couldn't load function " # func);                            \
+      Unlink();                                                                \
+      return false;                                                            \
+    }                                                                          \
+  } else {                                                                     \
+    func = (decltype(func))nullptr;                                            \
+  }
+  AV_FUNC(avcodec_alloc_context3, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_close, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_decode_audio4, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_decode_video2, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_find_decoder, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_flush_buffers, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_open2, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_register_all, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(av_init_packet, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(av_parser_init, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(av_parser_close, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(av_parser_parse2, AV_FUNC_AVCODEC_ALL)
+  AV_FUNC(avcodec_get_frame_defaults, (AV_FUNC_53 | AV_FUNC_54))
+  AV_FUNC(av_log_set_level, AV_FUNC_AVUTIL_ALL)
+  AV_FUNC(av_malloc, AV_FUNC_AVUTIL_ALL)
+  AV_FUNC(av_freep, AV_FUNC_AVUTIL_ALL)
+  AV_FUNC(avcodec_alloc_frame, AV_FUNC_AVUTIL_54)
+  AV_FUNC(avcodec_free_frame, AV_FUNC_AVUTIL_54)
+  AV_FUNC(av_frame_alloc, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
+  AV_FUNC(av_frame_free, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
+  AV_FUNC(av_frame_unref, (AV_FUNC_AVUTIL_55 | AV_FUNC_AVUTIL_56 | AV_FUNC_AVUTIL_57))
+#undef AV_FUNC
+
+  avcodec_register_all();
+#ifdef DEBUG
+  av_log_set_level(AV_LOG_DEBUG);
+#endif
+
+  return true;
+}
+
+void
+FFmpegLibWrapper::Unlink()
+{
+  if (mAVUtilLib && mAVUtilLib != mAVCodecLib) {
+    PR_UnloadLibrary(mAVUtilLib);
+  }
+  if (mAVCodecLib) {
+    PR_UnloadLibrary(mAVCodecLib);
+  }
+  PodZero(this);
+}
+
+} // namespace mozilla
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/ffmpeg/FFmpegLibWrapper.h
@@ -0,0 +1,78 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef __FFmpegLibWrapper_h__
+#define __FFmpegLibWrapper_h__
+
+#include "mozilla/Types.h"
+
+struct AVCodec;
+struct AVCodecContext;
+struct AVFrame;
+struct AVPacket;
+struct AVDictionary;
+struct AVCodecParserContext;
+struct PRLibrary;
+
+namespace mozilla
+{
+
+struct FFmpegLibWrapper
+{
+  FFmpegLibWrapper();
+  ~FFmpegLibWrapper();
+
+  // Attempt to resolve all symbols. Return true of successful.
+  // Upon failure, the entire object will be reset and any attached libraries
+  // will be unlinked.
+  bool Link();
+
+  // Reset the wrapper and unlink all attached libraries.
+  void Unlink();
+
+  // indicate the version of libavcodec linked to.
+  // 0 indicates that the function wasn't initialized with Link().
+  int mVersion;
+
+  // libavcodec
+  unsigned (*avcodec_version)();
+  AVCodecContext* (*avcodec_alloc_context3)(const AVCodec* codec);
+  int (*avcodec_close)(AVCodecContext* avctx);
+  int (*avcodec_decode_audio4)(AVCodecContext* avctx, AVFrame* frame, int* got_frame_ptr, const AVPacket* avpkt);
+  int (*avcodec_decode_video2)(AVCodecContext* avctx, AVFrame* picture, int* got_picture_ptr, const AVPacket* avpkt);
+  AVCodec* (*avcodec_find_decoder)(int id);
+  void (*avcodec_flush_buffers)(AVCodecContext *avctx);
+  int (*avcodec_open2)(AVCodecContext *avctx, const AVCodec* codec, AVDictionary** options);
+  void (*avcodec_register_all)();
+  void (*av_init_packet)(AVPacket* pkt);
+  AVCodecParserContext* (*av_parser_init)(int codec_id);
+  void (*av_parser_close)(AVCodecParserContext* s);
+  int (*av_parser_parse2)(AVCodecParserContext* s, AVCodecContext* avctx, uint8_t** poutbuf, int* poutbuf_size, const uint8_t* buf, int buf_size, int64_t pts, int64_t dts, int64_t pos);
+
+  // only used in libavcodec <= 54
+  void (*avcodec_get_frame_defaults)(AVFrame* pic);
+
+  // libavutil
+  void (*av_log_set_level)(int level);
+  void*	(*av_malloc)(size_t size);
+  void (*av_freep)(void *ptr);
+
+  // libavutil v54 only
+  AVFrame* (*avcodec_alloc_frame)();
+  void (*avcodec_free_frame)(AVFrame** frame);
+
+  // libavutil v55 and later only
+  AVFrame* (*av_frame_alloc)();
+  void (*av_frame_free)(AVFrame** frame);
+  void (*av_frame_unref)(AVFrame* frame);
+
+  PRLibrary* mAVCodecLib;
+  PRLibrary* mAVUtilLib;
+
+private:
+};
+
+} // namespace mozilla
+
+#endif // FFmpegLibWrapper
\ No newline at end of file
--- a/dom/media/platforms/ffmpeg/FFmpegLibs.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLibs.h
@@ -2,18 +2,16 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegLibs_h__
 #define __FFmpegLibs_h__
 
-#include "mozilla/Types.h"
-
 extern "C" {
 #ifdef __GNUC__
 #pragma GCC visibility push(default)
 #endif
 #include "libavcodec/avcodec.h"
 #include "libavutil/avutil.h"
 #include "libavutil/mem.h"
 #ifdef __GNUC__
@@ -32,28 +30,9 @@ typedef CodecID AVCodecID;
 #endif
 
 #ifdef FFVPX_VERSION
 enum { LIBAV_VER = FFVPX_VERSION };
 #else
 enum { LIBAV_VER = LIBAVCODEC_VERSION_MAJOR };
 #endif
 
-namespace mozilla {
-
-#ifdef USING_MOZFFVPX
-namespace ffvpx {
-#endif
-
-#define AV_FUNC(func, ver) extern decltype(func)* func;
-#include "FFmpegFunctionList.h"
-#undef AV_FUNC
-
-#ifdef USING_MOZFFVPX
-} // namespace ffvpx
-#define AV_CALL(func) mozilla::ffvpx::func
-#else
-#define AV_CALL(func) mozilla::func
-#endif
-
-}
-
 #endif // __FFmpegLibs_h__
--- a/dom/media/platforms/ffmpeg/FFmpegLog.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLog.h
@@ -2,12 +2,14 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegLog_h__
 #define __FFmpegLog_h__
 
+#include "mozilla/Logging.h"
+
 extern mozilla::LogModule* GetPDMLog();
 #define FFMPEG_LOG(...) MOZ_LOG(GetPDMLog(), mozilla::LogLevel::Debug, (__VA_ARGS__))
 
 #endif // __FFmpegLog_h__
--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
@@ -1,32 +1,34 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FFmpegRuntimeLinker.h"
+#include "FFmpegLibWrapper.h"
 #include "mozilla/ArrayUtils.h"
 #include "FFmpegLog.h"
-#include "mozilla/Types.h"
 #include "prlink.h"
 
 namespace mozilla
 {
 
 FFmpegRuntimeLinker::LinkStatus FFmpegRuntimeLinker::sLinkStatus =
   LinkStatus_INIT;
 
 template <int V> class FFmpegDecoderModule
 {
 public:
-  static already_AddRefed<PlatformDecoderModule> Create();
+  static already_AddRefed<PlatformDecoderModule> Create(FFmpegLibWrapper*);
 };
 
+static FFmpegLibWrapper sLibAV;
+
 static const char* sLibs[] = {
 #if defined(XP_DARWIN)
   "libavcodec.57.dylib",
   "libavcodec.56.dylib",
   "libavcodec.55.dylib",
   "libavcodec.54.dylib",
   "libavcodec.53.dylib",
 #else
@@ -35,168 +37,60 @@ static const char* sLibs[] = {
   "libavcodec.so.57",
   "libavcodec.so.56",
   "libavcodec.so.55",
   "libavcodec.so.54",
   "libavcodec.so.53",
 #endif
 };
 
-PRLibrary* FFmpegRuntimeLinker::sLinkedLib = nullptr;
-PRLibrary* FFmpegRuntimeLinker::sLinkedUtilLib = nullptr;
-static unsigned (*avcodec_version)() = nullptr;
-
-#ifdef __GNUC__
-#define AV_FUNC(func, ver) void (*func)();
-#define LIBAVCODEC_ALLVERSION
-#else
-#define AV_FUNC(func, ver) decltype(func)* func;
-#endif
-#include "FFmpegFunctionList.h"
-#undef AV_FUNC
-
-static PRLibrary*
-MozAVLink(const char* aName)
-{
-  PRLibSpec lspec;
-  lspec.type = PR_LibSpec_Pathname;
-  lspec.value.pathname = aName;
-  return PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
-}
-
 /* static */ bool
-FFmpegRuntimeLinker::Link()
+FFmpegRuntimeLinker::Init()
 {
   if (sLinkStatus) {
     return sLinkStatus == LinkStatus_SUCCEEDED;
   }
   MOZ_ASSERT(NS_IsMainThread());
 
   for (size_t i = 0; i < ArrayLength(sLibs); i++) {
     const char* lib = sLibs[i];
-    sLinkedLib = MozAVLink(lib);
-    if (sLinkedLib) {
-      sLinkedUtilLib = sLinkedLib;
-      if (Bind(lib)) {
+    PRLibSpec lspec;
+    lspec.type = PR_LibSpec_Pathname;
+    lspec.value.pathname = lib;
+    sLibAV.mAVCodecLib = PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
+    if (sLibAV.mAVCodecLib) {
+      sLibAV.mAVUtilLib = sLibAV.mAVCodecLib;
+      if (sLibAV.Link()) {
         sLinkStatus = LinkStatus_SUCCEEDED;
         return true;
       }
-      // Shouldn't happen but if it does then we try the next lib..
-      Unlink();
     }
   }
 
   FFMPEG_LOG("H264/AAC codecs unsupported without [");
   for (size_t i = 0; i < ArrayLength(sLibs); i++) {
     FFMPEG_LOG("%s %s", i ? "," : "", sLibs[i]);
   }
   FFMPEG_LOG(" ]\n");
 
-  Unlink();
-
   sLinkStatus = LinkStatus_FAILED;
   return false;
 }
 
-/* static */ bool
-FFmpegRuntimeLinker::Bind(const char* aLibName)
-{
-  avcodec_version = (decltype(avcodec_version))PR_FindSymbol(sLinkedLib,
-                                                           "avcodec_version");
-  uint32_t major, minor, micro;
-  if (!GetVersion(major, minor, micro)) {
-    return false;
-  }
-
-  int version;
-  switch (major) {
-    case 53:
-      version = AV_FUNC_53;
-      break;
-    case 54:
-      version = AV_FUNC_54;
-      break;
-    case 56:
-      // We use libavcodec 55 code instead. Fallback
-    case 55:
-      version = AV_FUNC_55;
-      break;
-    case 57:
-      if (micro != 100) {
-        // a micro version of 100 indicates that it's FFmpeg (as opposed to LibAV.
-        // Due to current AVCodecContext binary incompatibility we can only
-        // support FFmpeg at this stage.
-        return false;
-      }
-      version = AV_FUNC_57;
-      break;
-    default:
-      // Not supported at this stage.
-      return false;
-  }
-
-#define AV_FUNC(func, ver)                                                     \
-  if ((ver) & version) {                                                       \
-    if (!(func = (decltype(func))PR_FindSymbol(((ver) & AV_FUNC_AVUTIL_MASK) ? sLinkedUtilLib : sLinkedLib, #func))) { \
-      FFMPEG_LOG("Couldn't load function " #func " from %s.", aLibName);       \
-      return false;                                                            \
-    }                                                                          \
-  } else {                                                                     \
-    func = (decltype(func))nullptr;                                            \
-  }
-#include "FFmpegFunctionList.h"
-#undef AV_FUNC
-  return true;
-}
-
 /* static */ already_AddRefed<PlatformDecoderModule>
 FFmpegRuntimeLinker::CreateDecoderModule()
 {
-  if (!Link()) {
+  if (!Init()) {
     return nullptr;
   }
-  uint32_t major, minor, micro;
-  if (!GetVersion(major, minor, micro)) {
-    return  nullptr;
-  }
-
   RefPtr<PlatformDecoderModule> module;
-  switch (major) {
-    case 53: module = FFmpegDecoderModule<53>::Create(); break;
-    case 54: module = FFmpegDecoderModule<54>::Create(); break;
+  switch (sLibAV.mVersion) {
+    case 53: module = FFmpegDecoderModule<53>::Create(&sLibAV); break;
+    case 54: module = FFmpegDecoderModule<54>::Create(&sLibAV); break;
     case 55:
-    case 56: module = FFmpegDecoderModule<55>::Create(); break;
-    case 57: module = FFmpegDecoderModule<57>::Create(); break;
+    case 56: module = FFmpegDecoderModule<55>::Create(&sLibAV); break;
+    case 57: module = FFmpegDecoderModule<57>::Create(&sLibAV); break;
     default: module = nullptr;
   }
   return module.forget();
 }
 
-/* static */ void
-FFmpegRuntimeLinker::Unlink()
-{
-  if (sLinkedUtilLib && sLinkedUtilLib != sLinkedLib) {
-    PR_UnloadLibrary(sLinkedUtilLib);
-  }
-  if (sLinkedLib) {
-    PR_UnloadLibrary(sLinkedLib);
-    sLinkedLib = nullptr;
-    sLinkStatus = LinkStatus_INIT;
-    avcodec_version = nullptr;
-  }
-  sLinkedUtilLib = nullptr;
-}
-
-/* static */ bool
-FFmpegRuntimeLinker::GetVersion(uint32_t& aMajor, uint32_t& aMinor, uint32_t& aMicro)
-{
-  if (!avcodec_version) {
-    return false;
-  }
-  uint32_t version = avcodec_version();
-  aMajor = (version >> 16) & 0xff;
-  aMinor = (version >> 8) & 0xff;
-  aMicro = version & 0xff;
-  return true;
-}
-
-#undef LIBAVCODEC_ALLVERSION
 } // namespace mozilla
--- a/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.h
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.h
@@ -3,53 +3,27 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegRuntimeLinker_h__
 #define __FFmpegRuntimeLinker_h__
 
 #include "PlatformDecoderModule.h"
-#include <stdint.h>
-
-struct PRLibrary;
 
 namespace mozilla
 {
 
-enum {
-  AV_FUNC_AVUTIL_MASK = 1 << 8,
-  AV_FUNC_53 = 1 << 0,
-  AV_FUNC_54 = 1 << 1,
-  AV_FUNC_55 = 1 << 2,
-  AV_FUNC_56 = 1 << 3,
-  AV_FUNC_57 = 1 << 4,
-  AV_FUNC_AVUTIL_53 = AV_FUNC_53 | AV_FUNC_AVUTIL_MASK,
-  AV_FUNC_AVUTIL_54 = AV_FUNC_54 | AV_FUNC_AVUTIL_MASK,
-  AV_FUNC_AVUTIL_55 = AV_FUNC_55 | AV_FUNC_AVUTIL_MASK,
-  AV_FUNC_AVUTIL_56 = AV_FUNC_56 | AV_FUNC_AVUTIL_MASK,
-  AV_FUNC_AVUTIL_57 = AV_FUNC_57 | AV_FUNC_AVUTIL_MASK,
-  AV_FUNC_AVCODEC_ALL = AV_FUNC_53 | AV_FUNC_54 | AV_FUNC_55 | AV_FUNC_56 | AV_FUNC_57,
-  AV_FUNC_AVUTIL_ALL = AV_FUNC_AVCODEC_ALL | AV_FUNC_AVUTIL_MASK
-};
-
 class FFmpegRuntimeLinker
 {
 public:
-  static bool Link();
-  static void Unlink();
+  static bool Init();
   static already_AddRefed<PlatformDecoderModule> CreateDecoderModule();
-  static bool GetVersion(uint32_t& aMajor, uint32_t& aMinor, uint32_t& aMicro);
 
 private:
-  static PRLibrary* sLinkedLib;
-  static PRLibrary* sLinkedUtilLib;
-
-  static bool Bind(const char* aLibName);
-
   static enum LinkStatus {
     LinkStatus_INIT = 0,
     LinkStatus_FAILED,
     LinkStatus_SUCCEEDED
   } sLinkStatus;
 };
 
 }
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.cpp
@@ -93,21 +93,21 @@ void
 FFmpegVideoDecoder<LIBAV_VER>::PtsCorrectionContext::Reset()
 {
   mNumFaultyPts = 0;
   mNumFaultyDts = 0;
   mLastPts = INT64_MIN;
   mLastDts = INT64_MIN;
 }
 
-FFmpegVideoDecoder<LIBAV_VER>::FFmpegVideoDecoder(
+FFmpegVideoDecoder<LIBAV_VER>::FFmpegVideoDecoder(FFmpegLibWrapper* aLib,
   FlushableTaskQueue* aTaskQueue, MediaDataDecoderCallback* aCallback,
   const VideoInfo& aConfig,
   ImageContainer* aImageContainer)
-  : FFmpegDataDecoder(aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
+  : FFmpegDataDecoder(aLib, aTaskQueue, aCallback, GetCodecId(aConfig.mMimeType))
   , mImageContainer(aImageContainer)
   , mDisplay(aConfig.mDisplay)
   , mImage(aConfig.mImage)
   , mCodecParser(nullptr)
 {
   MOZ_COUNT_CTOR(FFmpegVideoDecoder);
   // Use a new MediaByteBuffer as the object will be modified during initialization.
   mExtraData = new MediaByteBuffer;
@@ -146,17 +146,17 @@ FFmpegVideoDecoder<LIBAV_VER>::InitCodec
   mCodecContext->thread_count = decode_threads;
   if (decode_threads > 1) {
     mCodecContext->thread_type = FF_THREAD_SLICE | FF_THREAD_FRAME;
   }
 
   // FFmpeg will call back to this to negotiate a video pixel format.
   mCodecContext->get_format = ChoosePixelFormat;
 
-  mCodecParser = AV_CALL(av_parser_init(mCodecID));
+  mCodecParser = mLib->av_parser_init(mCodecID);
   if (mCodecParser) {
     mCodecParser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
   }
 }
 
 FFmpegVideoDecoder<LIBAV_VER>::DecodeResult
 FFmpegVideoDecoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
@@ -170,20 +170,20 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecodeF
 #if LIBAVCODEC_VERSION_MAJOR >= 55
       || mCodecID == AV_CODEC_ID_VP9
 #endif
       )) {
     bool gotFrame = false;
     while (inputSize) {
       uint8_t* data;
       int size;
-      int len = AV_CALL(av_parser_parse2(mCodecParser, mCodecContext, &data, &size,
-                                         inputData, inputSize,
-                                         aSample->mTime, aSample->mTimecode,
-                                         aSample->mOffset));
+      int len = mLib->av_parser_parse2(mCodecParser, mCodecContext, &data, &size,
+                                       inputData, inputSize,
+                                       aSample->mTime, aSample->mTimecode,
+                                       aSample->mOffset);
       if (size_t(len) > inputSize) {
         mCallback->Error();
         return DecodeResult::DECODE_ERROR;
       }
       inputData += len;
       inputSize -= len;
       if (size) {
         switch (DoDecodeFrame(aSample, data, size)) {
@@ -205,17 +205,17 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecodeF
 
 FFmpegVideoDecoder<LIBAV_VER>::DecodeResult
 FFmpegVideoDecoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
                                             uint8_t* aData, int aSize)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 
   AVPacket packet;
-  AV_CALL(av_init_packet(&packet));
+  mLib->av_init_packet(&packet);
 
   packet.data = aData;
   packet.size = aSize;
   packet.dts = aSample->mTimecode;
   packet.pts = aSample->mTime;
   packet.flags = aSample->mKeyframe ? AV_PKT_FLAG_KEY : 0;
   packet.pos = aSample->mOffset;
 
@@ -232,17 +232,17 @@ FFmpegVideoDecoder<LIBAV_VER>::DoDecodeF
     return DecodeResult::DECODE_ERROR;
   }
 
   // Required with old version of FFmpeg/LibAV
   mFrame->reordered_opaque = AV_NOPTS_VALUE;
 
   int decoded;
   int bytesConsumed =
-    AV_CALL(avcodec_decode_video2(mCodecContext, mFrame, &decoded, &packet));
+    mLib->avcodec_decode_video2(mCodecContext, mFrame, &decoded, &packet);
 
   FFMPEG_LOG("DoDecodeFrame:decode_video: rv=%d decoded=%d "
              "(Input: pts(%lld) dts(%lld) Output: pts(%lld) "
              "opaque(%lld) pkt_pts(%lld) pkt_dts(%lld))",
              bytesConsumed, decoded, packet.pts, packet.dts, mFrame->pts,
              mFrame->reordered_opaque, mFrame->pkt_pts, mFrame->pkt_dts);
 
   if (bytesConsumed < 0) {
@@ -356,17 +356,17 @@ FFmpegVideoDecoder<LIBAV_VER>::ProcessFl
   mDurationMap.Clear();
   FFmpegDataDecoder::ProcessFlush();
 }
 
 FFmpegVideoDecoder<LIBAV_VER>::~FFmpegVideoDecoder()
 {
   MOZ_COUNT_DTOR(FFmpegVideoDecoder);
   if (mCodecParser) {
-    AV_CALL(av_parser_close(mCodecParser));
+    mLib->av_parser_close(mCodecParser);
     mCodecParser = nullptr;
   }
 }
 
 AVCodecID
 FFmpegVideoDecoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
   if (aMimeType.EqualsLiteral("video/avc") || aMimeType.EqualsLiteral("video/mp4")) {
--- a/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
+++ b/dom/media/platforms/ffmpeg/FFmpegVideoDecoder.h
@@ -2,16 +2,17 @@
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFmpegVideoDecoder_h__
 #define __FFmpegVideoDecoder_h__
 
+#include "FFmpegLibWrapper.h"
 #include "FFmpegDataDecoder.h"
 #include "mozilla/Pair.h"
 #include "nsTArray.h"
 
 namespace mozilla
 {
 
 template <int V>
@@ -27,20 +28,20 @@ class FFmpegVideoDecoder<LIBAV_VER> : pu
 
   enum DecodeResult {
     DECODE_FRAME,
     DECODE_NO_FRAME,
     DECODE_ERROR
   };
 
 public:
-  FFmpegVideoDecoder(FlushableTaskQueue* aTaskQueue,
-                    MediaDataDecoderCallback* aCallback,
-                    const VideoInfo& aConfig,
-                    ImageContainer* aImageContainer);
+  FFmpegVideoDecoder(FFmpegLibWrapper* aLib, FlushableTaskQueue* aTaskQueue,
+                     MediaDataDecoderCallback* aCallback,
+                     const VideoInfo& aConfig,
+                     ImageContainer* aImageContainer);
   virtual ~FFmpegVideoDecoder();
 
   RefPtr<InitPromise> Init() override;
   nsresult Input(MediaRawData* aSample) override;
   void ProcessDrain() override;
   void ProcessFlush() override;
   void InitCodecContext() override;
   static AVCodecID GetCodecId(const nsACString& aMimeType);
--- a/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.cpp
@@ -1,78 +1,58 @@
 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim:set ts=2 sw=2 sts=2 et cindent: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "FFVPXRuntimeLinker.h"
-#include "FFmpegRuntimeLinker.h"
+#include "FFmpegLibWrapper.h"
 #include "FFmpegLog.h"
-#include "mozilla/Types.h"
 #include "nsIFile.h"
 #include "nsXPCOMPrivate.h" // for XUL_DLL
 #include "prmem.h"
 #include "prlink.h"
 
-#if defined(XP_WIN)
-#include "libavcodec/avcodec.h"
-#include "libavutil/avutil.h"
-#endif
-
 namespace mozilla
 {
 
 template <int V> class FFmpegDecoderModule
 {
 public:
-  static already_AddRefed<PlatformDecoderModule> Create();
+  static already_AddRefed<PlatformDecoderModule> Create(FFmpegLibWrapper*);
 };
 
-namespace ffvpx
-{
+static FFmpegLibWrapper sFFVPXLib;
 
 FFVPXRuntimeLinker::LinkStatus FFVPXRuntimeLinker::sLinkStatus =
   LinkStatus_INIT;
 
-PRLibrary* FFVPXRuntimeLinker::sLinkedLib = nullptr;
-PRLibrary* FFVPXRuntimeLinker::sLinkedUtilLib = nullptr;
-static unsigned (*avcodec_version)() = nullptr;
-
-#ifdef __GNUC__
-#define AV_FUNC(func, ver) void (*func)();
-#define LIBAVCODEC_ALLVERSION
-#else
-#define AV_FUNC(func, ver) decltype(func)* func;
-#endif
-#include "FFmpegFunctionList.h"
-#undef AV_FUNC
-
 static PRLibrary*
 MozAVLink(const char* aName)
 {
   PRLibSpec lspec;
   lspec.type = PR_LibSpec_Pathname;
   lspec.value.pathname = aName;
   return PR_LoadLibraryWithFlags(lspec, PR_LD_NOW | PR_LD_LOCAL);
 }
 
 /* static */ bool
-FFVPXRuntimeLinker::Link()
+FFVPXRuntimeLinker::Init()
 {
   if (sLinkStatus) {
     return sLinkStatus == LinkStatus_SUCCEEDED;
   }
 
   MOZ_ASSERT(NS_IsMainThread());
 
   // We retrieve the path of the XUL library as this is where mozavcodec and
   // mozavutil libs are located.
   char* path =
-    PR_GetLibraryFilePathname(XUL_DLL, (PRFuncPtr)&FFVPXRuntimeLinker::Link);
+    PR_GetLibraryFilePathname(XUL_DLL, (PRFuncPtr)&FFVPXRuntimeLinker::Init);
   if (!path) {
     return false;
   }
   nsCOMPtr<nsIFile> xulFile = do_CreateInstance(NS_LOCAL_FILE_CONTRACTID);
   if (!xulFile ||
       NS_FAILED(xulFile->InitWithNativePath(nsDependentCString(path)))) {
     PR_Free(path);
     return false;
@@ -89,74 +69,34 @@ FFVPXRuntimeLinker::Link()
   }
 
   char* libname = NULL;
   /* Get the platform-dependent library name of the module */
   libname = PR_GetLibraryName(rootPath.get(), "mozavutil");
   if (!libname) {
     return false;
   }
-  sLinkedUtilLib = MozAVLink(libname);
+  sFFVPXLib.mAVUtilLib = MozAVLink(libname);
   PR_FreeLibraryName(libname);
   libname = PR_GetLibraryName(rootPath.get(), "mozavcodec");
   if (libname) {
-    sLinkedLib = MozAVLink(libname);
+    sFFVPXLib.mAVCodecLib = MozAVLink(libname);
     PR_FreeLibraryName(libname);
-    if (sLinkedLib && sLinkedUtilLib) {
-      if (Bind("mozavcodec")) {
-        sLinkStatus = LinkStatus_SUCCEEDED;
-        return true;
-      }
-    }
   }
-
-  Unlink();
-
+  if (sFFVPXLib.Link()) {
+    sLinkStatus = LinkStatus_SUCCEEDED;
+    return true;
+  }
   sLinkStatus = LinkStatus_FAILED;
-  return false;
-}
 
-/* static */ bool
-FFVPXRuntimeLinker::Bind(const char* aLibName)
-{
-  int version = AV_FUNC_57;
-
-#define AV_FUNC(func, ver)                                                     \
-  if ((ver) & version) {                                                       \
-    if (!(func = (decltype(func))PR_FindSymbol(((ver) & AV_FUNC_AVUTIL_MASK) ? sLinkedUtilLib : sLinkedLib, #func))) { \
-      FFMPEG_LOG("Couldn't load function " #func " from %s.", aLibName);       \
-      return false;                                                            \
-    }                                                                          \
-  } else {                                                                     \
-    func = (decltype(func))nullptr;                                            \
-  }
-#include "FFmpegFunctionList.h"
-#undef AV_FUNC
-  return true;
+  return false;
 }
 
 /* static */ already_AddRefed<PlatformDecoderModule>
 FFVPXRuntimeLinker::CreateDecoderModule()
 {
-  if (!Link()) {
+  if (!Init()) {
     return nullptr;
   }
-  return FFmpegDecoderModule<FFVPX_VERSION>::Create();
+  return FFmpegDecoderModule<FFVPX_VERSION>::Create(&sFFVPXLib);
 }
 
-/* static */ void
-FFVPXRuntimeLinker::Unlink()
-{
-  if (sLinkedUtilLib && sLinkedUtilLib != sLinkedLib) {
-    PR_UnloadLibrary(sLinkedUtilLib);
-  }
-  if (sLinkedLib) {
-    PR_UnloadLibrary(sLinkedLib);
-    sLinkedLib = nullptr;
-    sLinkStatus = LinkStatus_INIT;
-    avcodec_version = nullptr;
-  }
-  sLinkedUtilLib = nullptr;
-}
-
-#undef LIBAVCODEC_ALLVERSION
-} // namespace ffvpx
 } // namespace mozilla
--- a/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.h
+++ b/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.h
@@ -4,40 +4,28 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef __FFVPXRuntimeLinker_h__
 #define __FFVPXRuntimeLinker_h__
 
 #include "PlatformDecoderModule.h"
 
-struct PRLibrary;
-
 namespace mozilla
 {
-namespace ffvpx
-{
 
 class FFVPXRuntimeLinker
 {
 public:
-  static bool Link();
-  static void Unlink();
+  static bool Init();
   static already_AddRefed<PlatformDecoderModule> CreateDecoderModule();
 
 private:
-  static PRLibrary* sLinkedLib;
-  static PRLibrary* sLinkedUtilLib;
   static enum LinkStatus {
     LinkStatus_INIT = 0,
     LinkStatus_FAILED,
     LinkStatus_SUCCEEDED
   } sLinkStatus;
-
-  static bool Bind(const char* aLibName);
-
 };
 
 }
-}
-
 
 #endif /* __FFVPXRuntimeLinker_h__ */
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/ffmpeg/moz.build
@@ -0,0 +1,22 @@
+# -*- Mode: python; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 40 -*-
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+EXPORTS += [
+    'FFmpegRuntimeLinker.h',
+]
+
+DIRS += [
+    'libav53',
+    'libav54',
+    'libav55',
+    'ffmpeg57',
+]
+
+UNIFIED_SOURCES += [
+    'FFmpegRuntimeLinker.cpp',
+]
+
+FINAL_LIBRARY = 'xul'
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -33,33 +33,30 @@ DIRS += [
 ]
 
 if CONFIG['MOZ_WMF']:
     DIRS += [ 'wmf' ];
 
 if CONFIG['MOZ_EME']:
     DIRS += ['agnostic/eme']
 
+if CONFIG['MOZ_FFVPX'] or CONFIG['MOZ_FFMPEG']:
+    # common code to either FFmpeg or FFVPX
+    UNIFIED_SOURCES += [
+        'ffmpeg/FFmpegLibWrapper.cpp',
+    ]
+
 if CONFIG['MOZ_FFVPX']:
     DIRS += [
         'ffmpeg/ffvpx',
     ]
 
 if CONFIG['MOZ_FFMPEG']:
-    EXPORTS += [
-        'ffmpeg/FFmpegRuntimeLinker.h',
-    ]
-    UNIFIED_SOURCES += [
-        'ffmpeg/FFmpegRuntimeLinker.cpp',
-    ]
     DIRS += [
-        'ffmpeg/libav53',
-        'ffmpeg/libav54',
-        'ffmpeg/libav55',
-        'ffmpeg/ffmpeg57',
+        'ffmpeg',
     ]
 
 if CONFIG['MOZ_APPLEMEDIA']:
   EXPORTS += [
       'apple/AppleDecoderModule.h',
   ]
   UNIFIED_SOURCES += [
       'apple/AppleATDecoder.cpp',
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -92,20 +92,16 @@
 #ifdef MOZ_WEBSPEECH
 #include "nsSynthVoiceRegistry.h"
 #endif
 
 #ifdef MOZ_ANDROID_OMX
 #include "AndroidMediaPluginHost.h"
 #endif
 
-#ifdef MOZ_FFMPEG
-#include "FFmpegRuntimeLinker.h"
-#endif
-
 #include "CubebUtils.h"
 #include "Latency.h"
 #include "WebAudioUtils.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "nsVolumeService.h"
 using namespace mozilla::system;
 #endif
@@ -389,20 +385,16 @@ nsLayoutStatics::Shutdown()
   nsXBLService::Shutdown();
   nsAutoCopyListener::Shutdown();
   FrameLayerBuilder::Shutdown();
 
 #ifdef MOZ_ANDROID_OMX
   AndroidMediaPluginHost::Shutdown();
 #endif
 
-#ifdef MOZ_FFMPEG
-  FFmpegRuntimeLinker::Unlink();
-#endif
-
   CubebUtils::ShutdownLibrary();
   AsyncLatencyLogger::ShutdownLogger();
   WebAudioUtils::Shutdown();
 
 #ifdef MOZ_WIDGET_GONK
   nsVolumeService::Shutdown();
 #endif