Bug 1240630: [ffvpx] P1. Add a FFVPX PDM. r=kentuckyfriedtakahe
☠☠ backed out by 42c80e510f03 ☠ ☠
authorJean-Yves Avenard <jyavenard@mozilla.com>
Tue, 19 Jan 2016 17:29:19 +1100
changeset 280762 07e06fefa4c75d825877d099203aef623cfa76e1
parent 280761 8e4206bb3073efb696bbb928c1f587cadc9404f5
child 280763 ea16467602a86ec88b67c0def839e12d39ed26ec
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
bugs1240630
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 1240630: [ffvpx] P1. Add a FFVPX PDM. r=kentuckyfriedtakahe This allows support for linking to two different version of libavcodec: our own and the system one if found. ffvpx symbols are loaded within the namespace mozilla::ffvpx.
dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
dom/media/platforms/ffmpeg/FFmpegLibs.h
dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.cpp
dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.h
dom/media/platforms/ffmpeg/ffvpx/moz.build
dom/media/platforms/moz.build
--- a/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegAudioDecoder.cpp
@@ -96,34 +96,34 @@ CopyAndPackAudio(AVFrame* aFrame, uint32
   return audio;
 }
 
 void
 FFmpegAudioDecoder<LIBAV_VER>::DecodePacket(MediaRawData* aSample)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   AVPacket packet;
-  av_init_packet(&packet);
+  AV_CALL(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 =
-      avcodec_decode_audio4(mCodecContext, mFrame, &decoded, &packet);
+      AV_CALL(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/FFmpegDataDecoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegDataDecoder.cpp
@@ -49,17 +49,17 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
   AVCodec* codec = FindAVCodec(mCodecID);
   if (!codec) {
     NS_WARNING("Couldn't find ffmpeg decoder");
     return NS_ERROR_FAILURE;
   }
 
   StaticMutexAutoLock mon(sMonitor);
 
-  if (!(mCodecContext = avcodec_alloc_context3(codec))) {
+  if (!(mCodecContext = AV_CALL(avcodec_alloc_context3(codec)))) {
     NS_WARNING("Couldn't init ffmpeg context");
     return NS_ERROR_FAILURE;
   }
 
   mCodecContext->opaque = this;
 
   InitCodecContext();
 
@@ -72,20 +72,20 @@ FFmpegDataDecoder<LIBAV_VER>::InitDecode
   } else {
     mCodecContext->extradata_size = 0;
   }
 
   if (codec->capabilities & CODEC_CAP_DR1) {
     mCodecContext->flags |= CODEC_FLAG_EMU_EDGE;
   }
 
-  if (avcodec_open2(mCodecContext, codec, nullptr) < 0) {
+  if (AV_CALL(avcodec_open2(mCodecContext, codec, nullptr)) < 0) {
     NS_WARNING("Couldn't initialise ffmpeg decoder");
-    avcodec_close(mCodecContext);
-    av_freep(&mCodecContext);
+    AV_CALL(avcodec_close(mCodecContext));
+    AV_CALL(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 +136,73 @@ FFmpegDataDecoder<LIBAV_VER>::Drain()
   return NS_OK;
 }
 
 void
 FFmpegDataDecoder<LIBAV_VER>::ProcessFlush()
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
   if (mCodecContext) {
-    avcodec_flush_buffers(mCodecContext);
+    AV_CALL(avcodec_flush_buffers(mCodecContext));
   }
   MonitorAutoLock mon(mMonitor);
   mIsFlushing = false;
   mon.NotifyAll();
 }
 
 void
 FFmpegDataDecoder<LIBAV_VER>::ProcessShutdown()
 {
   StaticMutexAutoLock mon(sMonitor);
 
   if (sFFmpegInitDone && mCodecContext) {
-    avcodec_close(mCodecContext);
-    av_freep(&mCodecContext);
+    AV_CALL(avcodec_close(mCodecContext));
+    AV_CALL(av_freep(&mCodecContext));
 #if LIBAVCODEC_VERSION_MAJOR >= 55
-    av_frame_free(&mFrame);
+    AV_CALL(av_frame_free(&mFrame));
 #elif LIBAVCODEC_VERSION_MAJOR == 54
-    avcodec_free_frame(&mFrame);
+    AV_CALL(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_frame_unref(mFrame);
+    AV_CALL(av_frame_unref(mFrame));
   } else {
-    mFrame = av_frame_alloc();
+    mFrame = AV_CALL(av_frame_alloc());
   }
 #elif LIBAVCODEC_VERSION_MAJOR == 54
   if (mFrame) {
-    avcodec_get_frame_defaults(mFrame);
+    AV_CALL(avcodec_get_frame_defaults(mFrame));
   } else {
-    mFrame = avcodec_alloc_frame();
+    mFrame = AV_CALL(avcodec_alloc_frame());
   }
 #else
   delete mFrame;
   mFrame = new AVFrame;
-  avcodec_get_frame_defaults(mFrame);
+  AV_CALL(avcodec_get_frame_defaults(mFrame));
 #endif
   return mFrame;
 }
 
 /* static */ AVCodec*
 FFmpegDataDecoder<LIBAV_VER>::FindAVCodec(AVCodecID aCodec)
 {
   StaticMutexAutoLock mon(sMonitor);
   if (!sFFmpegInitDone) {
-    avcodec_register_all();
+    AV_CALL(avcodec_register_all());
 #ifdef DEBUG
-    av_log_set_level(AV_LOG_DEBUG);
+    AV_CALL(av_log_set_level(AV_LOG_DEBUG));
 #endif
     sFFmpegInitDone = true;
   }
-  return avcodec_find_decoder(aCodec);
+  return AV_CALL(avcodec_find_decoder(aCodec));
 }
   
 } // namespace mozilla
--- a/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
+++ b/dom/media/platforms/ffmpeg/FFmpegDecoderModule.h
@@ -42,24 +42,32 @@ public:
     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);
     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 = FFmpegH264Decoder<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);
   }
 
--- a/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegH264Decoder.cpp
@@ -146,17 +146,17 @@ FFmpegH264Decoder<LIBAV_VER>::InitCodecC
   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_parser_init(mCodecID);
+  mCodecParser = AV_CALL(av_parser_init(mCodecID));
   if (mCodecParser) {
     mCodecParser->flags |= PARSER_FLAG_COMPLETE_FRAMES;
   }
 }
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample)
 {
@@ -170,20 +170,20 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
 #if LIBAVCODEC_VERSION_MAJOR >= 55
       || mCodecID == AV_CODEC_ID_VP9
 #endif
       )) {
     bool gotFrame = false;
     while (inputSize) {
       uint8_t* data;
       int size;
-      int len = av_parser_parse2(mCodecParser, mCodecContext, &data, &size,
-                                 inputData, inputSize,
-                                 aSample->mTime, aSample->mTimecode,
-                                 aSample->mOffset);
+      int len = AV_CALL(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 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
 
 FFmpegH264Decoder<LIBAV_VER>::DecodeResult
 FFmpegH264Decoder<LIBAV_VER>::DoDecodeFrame(MediaRawData* aSample,
                                             uint8_t* aData, int aSize)
 {
   MOZ_ASSERT(mTaskQueue->IsCurrentThreadIn());
 
   AVPacket packet;
-  av_init_packet(&packet);
+  AV_CALL(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 @@ FFmpegH264Decoder<LIBAV_VER>::DoDecodeFr
     return DecodeResult::DECODE_ERROR;
   }
 
   // Required with old version of FFmpeg/LibAV
   mFrame->reordered_opaque = AV_NOPTS_VALUE;
 
   int decoded;
   int bytesConsumed =
-    avcodec_decode_video2(mCodecContext, mFrame, &decoded, &packet);
+    AV_CALL(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 @@ FFmpegH264Decoder<LIBAV_VER>::ProcessFlu
   mDurationMap.Clear();
   FFmpegDataDecoder::ProcessFlush();
 }
 
 FFmpegH264Decoder<LIBAV_VER>::~FFmpegH264Decoder()
 {
   MOZ_COUNT_DTOR(FFmpegH264Decoder);
   if (mCodecParser) {
-    av_parser_close(mCodecParser);
+    AV_CALL(av_parser_close(mCodecParser));
     mCodecParser = nullptr;
   }
 }
 
 AVCodecID
 FFmpegH264Decoder<LIBAV_VER>::GetCodecId(const nsACString& aMimeType)
 {
   if (aMimeType.EqualsLiteral("video/avc") || aMimeType.EqualsLiteral("video/mp4")) {
--- a/dom/media/platforms/ffmpeg/FFmpegLibs.h
+++ b/dom/media/platforms/ffmpeg/FFmpegLibs.h
@@ -26,19 +26,34 @@ extern "C" {
 #define AV_CODEC_ID_H264 CODEC_ID_H264
 #define AV_CODEC_ID_AAC CODEC_ID_AAC
 #define AV_CODEC_ID_MP3 CODEC_ID_MP3
 #define AV_CODEC_ID_VP8 CODEC_ID_VP8
 #define AV_CODEC_ID_NONE CODEC_ID_NONE
 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/FFmpegRuntimeLinker.cpp
+++ b/dom/media/platforms/ffmpeg/FFmpegRuntimeLinker.cpp
@@ -2,28 +2,19 @@
 /* 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 "mozilla/ArrayUtils.h"
 #include "FFmpegLog.h"
-#include "mozilla/Preferences.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
 {
 
 FFmpegRuntimeLinker::LinkStatus FFmpegRuntimeLinker::sLinkStatus =
   LinkStatus_INIT;
 
 template <int V> class FFmpegDecoderModule
 {
@@ -48,41 +39,43 @@ static const char* sLibs[] = {
   "libavcodec.so.53",
 #endif
 };
 
 PRLibrary* FFmpegRuntimeLinker::sLinkedLib = nullptr;
 PRLibrary* FFmpegRuntimeLinker::sLinkedUtilLib = nullptr;
 static unsigned (*avcodec_version)() = nullptr;
 
+#if !defined(XP_WIN)
 #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
+#endif
 
 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()
 {
   if (sLinkStatus) {
     return sLinkStatus == LinkStatus_SUCCEEDED;
   }
-
+#if !defined(XP_WIN)
   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)) {
@@ -95,73 +88,29 @@ FFmpegRuntimeLinker::Link()
   }
 
   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");
 
-#ifdef MOZ_FFVPX
-  // 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)&FFmpegRuntimeLinker::Link);
-  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;
-  }
-  PR_Free(path);
-
-  nsCOMPtr<nsIFile> rootDir;
-  if (NS_FAILED(xulFile->GetParent(getter_AddRefs(rootDir))) || !rootDir) {
-    return false;
-  }
-  nsAutoCString rootPath;
-  if (NS_FAILED(rootDir->GetNativePath(rootPath))) {
-    return false;
-  }
-
-  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);
-  PR_FreeLibraryName(libname);
-  libname = PR_GetLibraryName(rootPath.get(), "mozavcodec");
-  if (!libname) {
-    Unlink();
-    return false;
-  }
-  sLinkedLib = MozAVLink(libname);
-  PR_FreeLibraryName(libname);
-  if (sLinkedLib && sLinkedUtilLib) {
-    if (Bind("mozavcodec")) {
-      sLinkStatus = LinkStatus_SUCCEEDED;
-      return true;
-    }
-  }
+  Unlink();
 #endif
 
-  Unlink();
-
   sLinkStatus = LinkStatus_FAILED;
   return false;
 }
 
 /* static */ bool
 FFmpegRuntimeLinker::Bind(const char* aLibName)
 {
+#if defined(XP_WIN)
+  return false;
+#else
   avcodec_version = (decltype(avcodec_version))PR_FindSymbol(sLinkedLib,
                                                            "avcodec_version");
   uint32_t major, minor, micro;
   if (!GetVersion(major, minor, micro)) {
     return false;
   }
 
   int version;
@@ -198,41 +147,44 @@ FFmpegRuntimeLinker::Bind(const char* aL
       return false;                                                            \
     }                                                                          \
   } else {                                                                     \
     func = (decltype(func))nullptr;                                            \
   }
 #include "FFmpegFunctionList.h"
 #undef AV_FUNC
   return true;
+#endif
 }
 
 /* static */ already_AddRefed<PlatformDecoderModule>
 FFmpegRuntimeLinker::CreateDecoderModule()
 {
+#if defined(XP_WIN)
+  return nullptr;
+#else
   if (!Link()) {
     return nullptr;
   }
   uint32_t major, minor, micro;
   if (!GetVersion(major, minor, micro)) {
     return  nullptr;
   }
 
   RefPtr<PlatformDecoderModule> module;
   switch (major) {
-#ifndef XP_WIN
     case 53: module = FFmpegDecoderModule<53>::Create(); break;
     case 54: module = FFmpegDecoderModule<54>::Create(); break;
     case 55:
     case 56: module = FFmpegDecoderModule<55>::Create(); break;
-#endif
     case 57: module = FFmpegDecoderModule<57>::Create(); break;
     default: module = nullptr;
   }
   return module.forget();
+#endif
 }
 
 /* static */ void
 FFmpegRuntimeLinker::Unlink()
 {
   if (sLinkedUtilLib && sLinkedUtilLib != sLinkedLib) {
     PR_UnloadLibrary(sLinkedUtilLib);
   }
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.cpp
@@ -0,0 +1,162 @@
+/* -*- 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 "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();
+};
+
+namespace ffvpx
+{
+
+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()
+{
+  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);
+  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;
+  }
+  PR_Free(path);
+
+  nsCOMPtr<nsIFile> rootDir;
+  if (NS_FAILED(xulFile->GetParent(getter_AddRefs(rootDir))) || !rootDir) {
+    return false;
+  }
+  nsAutoCString rootPath;
+  if (NS_FAILED(rootDir->GetNativePath(rootPath))) {
+    return false;
+  }
+
+  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);
+  PR_FreeLibraryName(libname);
+  libname = PR_GetLibraryName(rootPath.get(), "mozavcodec");
+  if (libname) {
+    sLinkedLib = MozAVLink(libname);
+    PR_FreeLibraryName(libname);
+    if (sLinkedLib && sLinkedUtilLib) {
+      if (Bind("mozavcodec")) {
+        sLinkStatus = LinkStatus_SUCCEEDED;
+        return true;
+      }
+    }
+  }
+
+  Unlink();
+
+  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;
+}
+
+/* static */ already_AddRefed<PlatformDecoderModule>
+FFVPXRuntimeLinker::CreateDecoderModule()
+{
+  if (!Link()) {
+    return nullptr;
+  }
+  return FFmpegDecoderModule<FFVPX_VERSION>::Create();
+}
+
+/* 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
new file mode 100644
--- /dev/null
+++ b/dom/media/platforms/ffmpeg/ffvpx/FFVPXRuntimeLinker.h
@@ -0,0 +1,43 @@
+/* -*- 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/. */
+
+#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 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/ffvpx/moz.build
@@ -0,0 +1,44 @@
+# -*- 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/.
+
+LOCAL_INCLUDES += ['/xpcom/build']
+EXPORTS += [
+    'FFVPXRuntimeLinker.h',
+]
+
+UNIFIED_SOURCES += [
+    '../FFmpegDataDecoder.cpp',
+    '../FFmpegDecoderModule.cpp',
+    '../FFmpegH264Decoder.cpp',
+]
+SOURCES += [
+    'FFVPXRuntimeLinker.cpp',
+]
+LOCAL_INCLUDES += [
+    '..',
+    '../ffmpeg57/include',
+]
+
+if CONFIG['OS_ARCH'] == 'WINNT':
+    LOCAL_INCLUDES += [
+        '../ffmpeg57/include',
+    ]
+
+if CONFIG['GNU_CXX']:
+  CXXFLAGS += [ '-Wno-deprecated-declarations' ]
+if CONFIG['CLANG_CXX']:
+  CXXFLAGS += [
+    '-Wno-unknown-attributes',
+  ]
+if CONFIG['_MSC_VER']:
+  CXXFLAGS += [
+    '-wd4996', # deprecated declaration
+  ]
+
+DEFINES['FFVPX_VERSION'] = 46465650
+DEFINES['USING_MOZFFVPX'] = True
+
+FINAL_LIBRARY = 'xul'
--- a/dom/media/platforms/moz.build
+++ b/dom/media/platforms/moz.build
@@ -34,38 +34,34 @@ DIRS += [
 
 if CONFIG['MOZ_WMF']:
     DIRS += [ 'wmf' ];
 
 if CONFIG['MOZ_EME']:
     DIRS += ['agnostic/eme']
 
 if CONFIG['MOZ_FFMPEG']:
-    LOCAL_INCLUDES += ['/xpcom/build']
     EXPORTS += [
         'ffmpeg/FFmpegRuntimeLinker.h',
     ]
     UNIFIED_SOURCES += [
         'ffmpeg/FFmpegRuntimeLinker.cpp',
     ]
+    if CONFIG['MOZ_FFVPX']:
+        DIRS += [
+            'ffmpeg/ffvpx',
+        ]
     if CONFIG['OS_ARCH'] != 'WINNT':
         DIRS += [
             'ffmpeg/libav53',
             'ffmpeg/libav54',
             'ffmpeg/libav55',
-        ]
-    else:
-        LOCAL_INCLUDES += [
-            'ffmpeg/ffmpeg57/include',
+            'ffmpeg/ffmpeg57',
         ]
 
-    DIRS += [
-        'ffmpeg/ffmpeg57',
-    ]
-
 if CONFIG['MOZ_APPLEMEDIA']:
   EXPORTS += [
       'apple/AppleDecoderModule.h',
   ]
   UNIFIED_SOURCES += [
       'apple/AppleATDecoder.cpp',
       'apple/AppleCMLinker.cpp',
       'apple/AppleDecoderModule.cpp',