Bug 1214967 - Create a list of GMPs/codecs that can be used for <video> decoding. r=jwwang
authorChris Pearce <cpearce@mozilla.com>
Thu, 29 Oct 2015 21:54:52 +1300
changeset 304378 81edda656162c13bcbb6ce06659ada57e039f0a3
parent 304377 3cfb964dd761aa12893df1fa8de743ceb81a82a4
child 304379 285de7bf33e3d8e116ce81039ff2de0ec1bfc38a
push id5513
push userraliiev@mozilla.com
push dateMon, 25 Jan 2016 13:55:34 +0000
treeherdermozilla-beta@5ee97dd05b5c [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjwwang
bugs1214967
milestone44.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 1214967 - Create a list of GMPs/codecs that can be used for <video> decoding. r=jwwang
dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.cpp
@@ -6,18 +6,22 @@
 
 #include "GMPDecoderModule.h"
 #include "GMPAudioDecoder.h"
 #include "GMPVideoDecoder.h"
 #include "MediaDataDecoderProxy.h"
 #include "mozIGeckoMediaPluginService.h"
 #include "nsServiceManagerUtils.h"
 #include "mozilla/Preferences.h"
+#include "mozilla/StaticMutex.h"
 #include "gmp-audio-decode.h"
 #include "gmp-video-decode.h"
+#ifdef XP_WIN
+#include "WMFDecoderModule.h"
+#endif
 
 namespace mozilla {
 
 GMPDecoderModule::GMPDecoderModule()
 {
 }
 
 GMPDecoderModule::~GMPDecoderModule()
@@ -84,23 +88,95 @@ GMPDecoderModule::DecoderNeedsConversion
   // GMPVideoCodecType::kGMPVideoCodecH264 specifies that encoded frames must be in AVCC format.
   if (aConfig.IsVideo()) {
     return kNeedAVCC;
   } else {
     return kNeedNone;
   }
 }
 
+static bool
+HasGMPFor(const nsACString& aAPI,
+          const nsACString& aCodec,
+          const nsACString& aGMP)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+#ifdef XP_WIN
+  // gmp-clearkey uses WMF for decoding, so if we're using clearkey we must
+  // verify that WMF works before continuing.
+  if (aGMP.EqualsLiteral("org.w3.clearkey")) {
+    RefPtr<WMFDecoderModule> pdm(new WMFDecoderModule());
+    if (aCodec.EqualsLiteral("aac") &&
+        !pdm->SupportsMimeType(NS_LITERAL_CSTRING("audio/mp4a-latm"))) {
+      return false;
+    }
+    if (aCodec.EqualsLiteral("h264") &&
+        !pdm->SupportsMimeType(NS_LITERAL_CSTRING("video/avc"))) {
+      return false;
+    }
+  }
+#endif
+  nsTArray<nsCString> tags;
+  tags.AppendElement(aCodec);
+  tags.AppendElement(aGMP);
+  nsCOMPtr<mozIGeckoMediaPluginService> mps =
+    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
+  if (NS_WARN_IF(!mps)) {
+    return false;
+  }
+  bool hasPlugin = false;
+  if (NS_FAILED(mps->HasPluginForAPI(aAPI, &tags, &hasPlugin))) {
+    return false;
+  }
+  return hasPlugin;
+}
+
+StaticMutex sGMPCodecsMutex;
+
+struct GMPCodecs {
+  const char* mKeySystem;
+  bool mHasAAC;
+  bool mHasH264;
+};
+
+static GMPCodecs sGMPCodecs[] = {
+  { "org.w3.clearkey", false, false },
+  { "com.adobe.primetime", false, false },
+};
+
+void
+GMPDecoderModule::UpdateUsableCodecs()
+{
+  MOZ_ASSERT(NS_IsMainThread());
+
+  StaticMutexAutoLock lock(sGMPCodecsMutex);
+  for (GMPCodecs& gmp : sGMPCodecs) {
+    gmp.mHasAAC = HasGMPFor(NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                            NS_LITERAL_CSTRING("aac"),
+                            nsDependentCString(gmp.mKeySystem));
+    gmp.mHasH264 = HasGMPFor(NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                             NS_LITERAL_CSTRING("h264"),
+                             nsDependentCString(gmp.mKeySystem));
+  }
+}
+
 static uint32_t sPreferredAacGmp = 0;
 static uint32_t sPreferredH264Gmp = 0;
 
 /* static */
 void
 GMPDecoderModule::Init()
 {
+  MOZ_ASSERT(NS_IsMainThread());
+
+  // GMPService::HasPluginForAPI is main thread only, so to implement
+  // SupportsMimeType() we build a table of the codecs which each whitelisted
+  // GMP has and update it when any GMPs are removed or added at runtime.
+  UpdateUsableCodecs();
+
   Preferences::AddUintVarCache(&sPreferredAacGmp,
                                "media.gmp.decoder.aac", 0);
   Preferences::AddUintVarCache(&sPreferredH264Gmp,
                                "media.gmp.decoder.h264", 0);
 }
 
 /* static */
 const Maybe<nsCString>
@@ -110,56 +186,50 @@ GMPDecoderModule::PreferredGMP(const nsA
   if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
     switch (sPreferredAacGmp) {
       case 1: rv.emplace(NS_LITERAL_CSTRING("org.w3.clearkey")); break;
       case 2: rv.emplace(NS_LITERAL_CSTRING("com.adobe.primetime")); break;
       default: break;
     }
   }
 
-  if (aMimeType.EqualsLiteral("video/avc")) {
+  if (aMimeType.EqualsLiteral("video/avc") ||
+      aMimeType.EqualsLiteral("video/mp4")) {
     switch (sPreferredH264Gmp) {
       case 1: rv.emplace(NS_LITERAL_CSTRING("org.w3.clearkey")); break;
       case 2: rv.emplace(NS_LITERAL_CSTRING("com.adobe.primetime")); break;
       default: break;
     }
   }
 
   return rv;
 }
 
+/* static */
 bool
 GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType,
                                    const Maybe<nsCString>& aGMP)
 {
-  nsTArray<nsCString> tags;
-  nsCString api;
-  if (aMimeType.EqualsLiteral("audio/mp4a-latm")) {
-    tags.AppendElement(NS_LITERAL_CSTRING("aac"));
-    api = NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER);
-  } else if (aMimeType.EqualsLiteral("video/avc") ||
-             aMimeType.EqualsLiteral("video/mp4")) {
-    tags.AppendElement(NS_LITERAL_CSTRING("h264"));
-    api = NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER);
-  } else {
-    return false;
+  const bool isAAC = aMimeType.EqualsLiteral("audio/mp4a-latm");
+  const bool isH264 = aMimeType.EqualsLiteral("video/avc") ||
+                      aMimeType.EqualsLiteral("video/mp4");
+
+  StaticMutexAutoLock lock(sGMPCodecsMutex);
+  for (GMPCodecs& gmp : sGMPCodecs) {
+    if (isAAC && gmp.mHasAAC &&
+        (aGMP.isNothing() || aGMP.value().EqualsASCII(gmp.mKeySystem))) {
+      return true;
+    }
+    if (isH264 && gmp.mHasH264 &&
+        (aGMP.isNothing() || aGMP.value().EqualsASCII(gmp.mKeySystem))) {
+      return true;
+    }
   }
-  if (aGMP.isSome()) {
-    tags.AppendElement(aGMP.value());
-  }
-  nsCOMPtr<mozIGeckoMediaPluginService> mps =
-    do_GetService("@mozilla.org/gecko-media-plugin-service;1");
-  if (NS_WARN_IF(!mps)) {
-    return false;
-  }
-  bool hasPlugin = false;
-  if (NS_FAILED(mps->HasPluginForAPI(api, &tags, &hasPlugin))) {
-    return false;
-  }
-  return hasPlugin;
+
+  return false;
 }
 
 bool
 GMPDecoderModule::SupportsMimeType(const nsACString& aMimeType)
 {
   return SupportsMimeType(aMimeType, PreferredGMP(aMimeType));
 }
 
--- a/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
+++ b/dom/media/platforms/agnostic/gmp/GMPDecoderModule.h
@@ -33,20 +33,23 @@ public:
                      MediaDataDecoderCallback* aCallback) override;
 
   ConversionRequired
   DecoderNeedsConversion(const TrackInfo& aConfig) const override;
 
   bool
   SupportsMimeType(const nsACString& aMimeType) override;
 
+  // Main thread only.
   static void Init();
 
   static const Maybe<nsCString> PreferredGMP(const nsACString& aMimeType);
 
   static bool SupportsMimeType(const nsACString& aMimeType,
                                const Maybe<nsCString>& aGMP);
 
+  // Main thread only.
+  static void UpdateUsableCodecs();
 };
 
 } // namespace mozilla
 
 #endif // GMPDecoderModule_h_