Bug 1207019 - Remove WMF availability check in MediaKeySystemAccess requests. r=edwin,a=lizzard
authorChris Pearce <cpearce@mozilla.com>
Mon, 05 Oct 2015 15:03:48 +1300
changeset 296698 45f9754edccf
parent 296697 842a741af958
child 296699 d7438e734352
push id5296
push usercpearce@mozilla.com
push date2015-11-12 01:32 +0000
treeherdermozilla-beta@ba6b7797e925 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersedwin, lizzard
bugs1207019
milestone43.0
Bug 1207019 - Remove WMF availability check in MediaKeySystemAccess requests. r=edwin,a=lizzard
dom/media/MediaResource.cpp
dom/media/VideoUtils.cpp
dom/media/VideoUtils.h
dom/media/eme/MediaKeySystemAccess.cpp
dom/media/fmp4/MP4Decoder.cpp
dom/media/fmp4/MP4Decoder.h
--- a/dom/media/MediaResource.cpp
+++ b/dom/media/MediaResource.cpp
@@ -562,17 +562,17 @@ nsresult ChannelMediaResource::OpenChann
     nsresult rv = mChannel->SetNotificationCallbacks(mListener.get());
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = SetupChannelHeaders();
     NS_ENSURE_SUCCESS(rv, rv);
 
     rv = mChannel->AsyncOpen2(mListener);
     NS_ENSURE_SUCCESS(rv, rv);
-    
+
     // Tell the media element that we are fetching data from a channel.
     MediaDecoderOwner* owner = mDecoder->GetMediaOwner();
     NS_ENSURE_TRUE(owner, NS_ERROR_FAILURE);
     dom::HTMLMediaElement* element = owner->GetMediaElement();
     element->DownloadResumed(true);
   }
 
   return NS_OK;
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -3,29 +3,32 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "VideoUtils.h"
 
 #include "mozilla/Preferences.h"
 #include "mozilla/Base64.h"
 #include "mozilla/TaskQueue.h"
 #include "mozilla/Telemetry.h"
+#include "mozilla/Function.h"
 
 #include "MediaResource.h"
 #include "TimeUnits.h"
 #include "nsMathUtils.h"
 #include "nsSize.h"
 #include "VorbisUtils.h"
 #include "ImageContainer.h"
 #include "mozilla/SharedThreadPool.h"
 #include "nsIRandomGenerator.h"
 #include "nsIServiceManager.h"
 #include "nsServiceManagerUtils.h"
 #include "nsIConsoleService.h"
 #include "nsThreadUtils.h"
+#include "nsCharSeparatedTokenizer.h"
+#include "nsContentTypeParser.h"
 
 #include <stdint.h>
 
 namespace mozilla {
 
 using layers::PlanarYCbCrImage;
 
 static inline CheckedInt64 SaferMultDiv(int64_t aValue, uint32_t aMul, uint32_t aDiv) {
@@ -444,9 +447,83 @@ LogToBrowserConsole(const nsAString& aMs
   if (!console) {
     NS_WARNING("Failed to log message to console.");
     return;
   }
   nsAutoString msg(aMsg);
   console->LogStringMessage(msg.get());
 }
 
+bool
+ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs)
+{
+  aOutCodecs.Clear();
+  bool expectMoreTokens = false;
+  nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
+  while (tokenizer.hasMoreTokens()) {
+    const nsSubstring& token = tokenizer.nextToken();
+    expectMoreTokens = tokenizer.separatorAfterCurrentToken();
+    aOutCodecs.AppendElement(token);
+  }
+  if (expectMoreTokens) {
+    // Last codec name was empty
+    return false;
+  }
+  return true;
+}
+
+static bool
+CheckContentType(const nsAString& aContentType,
+                 mozilla::Function<bool(const nsAString&)> aSubtypeFilter,
+                 mozilla::Function<bool(const nsAString&)> aCodecFilter)
+{
+  nsContentTypeParser parser(aContentType);
+  nsAutoString mimeType;
+  nsresult rv = parser.GetType(mimeType);
+  if (NS_FAILED(rv) || !aSubtypeFilter(mimeType)) {
+    return false;
+  }
+
+  nsString codecsStr;
+  parser.GetParameter("codecs", codecsStr);
+  nsTArray<nsString> codecs;
+  if (!ParseCodecsString(codecsStr, codecs)) {
+    return false;
+  }
+  for (const nsString& codec : codecs) {
+    if (!aCodecFilter(codec)) {
+      return false;
+    }
+  }
+  return true;
+}
+
+bool
+IsH264ContentType(const nsAString& aContentType)
+{
+  return CheckContentType(aContentType,
+    [](const nsAString& type) {
+      return type.EqualsLiteral("video/mp4");
+    },
+    [](const nsAString& codec) {
+      int16_t profile = 0;
+      int16_t level = 0;
+      return ExtractH264CodecDetails(codec, profile, level);
+    }
+  );
+}
+
+bool
+IsAACContentType(const nsAString& aContentType)
+{
+  return CheckContentType(aContentType,
+    [](const nsAString& type) {
+      return type.EqualsLiteral("audio/mp4") ||
+             type.EqualsLiteral("audio/x-m4a");
+    },
+    [](const nsAString& codec) {
+      return codec.EqualsLiteral("mp4a.40.2") || // MPEG4 AAC-LC
+             codec.EqualsLiteral("mp4a.40.5") || // MPEG4 HE-AAC
+             codec.EqualsLiteral("mp4a.67");     // MPEG2 AAC-LC
+    });
+}
+
 } // end namespace mozilla
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -319,11 +319,20 @@ private:
 
   nsRefPtr<nsIRunnable> mTask;
   nsCOMPtr<nsITimer> mTimer;
 };
 
 void
 LogToBrowserConsole(const nsAString& aMsg);
 
+bool
+ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs);
+
+bool
+IsH264ContentType(const nsAString& aContentType);
+
+bool
+IsAACContentType(const nsAString& aContentType);
+
 } // end namespace mozilla
 
 #endif
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -26,16 +26,18 @@
 #include "mozilla/Services.h"
 #include "nsIObserverService.h"
 #include "mozilla/EMEUtils.h"
 #include "GMPUtils.h"
 #include "nsAppDirectoryServiceDefs.h"
 #include "nsDirectoryServiceUtils.h"
 #include "nsDirectoryServiceDefs.h"
 #include "nsXULAppAPI.h"
+#include "gmp-audio-decode.h"
+#include "gmp-video-decode.h"
 
 #if defined(XP_WIN) || defined(XP_MACOSX)
 #define PRIMETIME_EME_SUPPORTED 1
 #endif
 
 namespace mozilla {
 namespace dom {
 
@@ -292,101 +294,140 @@ MediaKeySystemAccess::GetKeySystemStatus
     return EnsureMinCDMVersion(mps, aKeySystem, aMinCdmVersion, aOutMessage, aOutCdmVersion);
   }
 #endif
 
   return MediaKeySystemStatus::Cdm_not_supported;
 }
 
 static bool
-IsPlayableWithGMP(mozIGeckoMediaPluginService* aGMPS,
-                  const nsAString& aKeySystem,
-                  const nsAString& aContentType)
+GMPDecryptsAndDecodesAAC(mozIGeckoMediaPluginService* aGMPS,
+                         const nsAString& aKeySystem)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPS,
+                        NS_ConvertUTF16toUTF8(aKeySystem),
+                        NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  return HaveGMPFor(aGMPS,
+                    NS_ConvertUTF16toUTF8(aKeySystem),
+                    NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                    NS_LITERAL_CSTRING("aac"));
+}
+
+static bool
+GMPDecryptsAndDecodesH264(mozIGeckoMediaPluginService* aGMPS,
+                          const nsAString& aKeySystem)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPS,
+                        NS_ConvertUTF16toUTF8(aKeySystem),
+                        NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  return HaveGMPFor(aGMPS,
+                    NS_ConvertUTF16toUTF8(aKeySystem),
+                    NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                    NS_LITERAL_CSTRING("aac"));
+}
+
+// If this keysystem's CDM explicitly says it doesn't support decoding,
+// that means it's OK with passing the decrypted samples back to Gecko
+// for decoding. Note we special case Clearkey on Windows, where we need
+// to check for whether WMF is usable because the CDM uses that
+// to decode.
+static bool
+GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
+                               const nsAString& aKeySystem,
+                               const nsAString& aContentType)
 {
-#ifdef MOZ_FMP4
-  nsContentTypeParser parser(aContentType);
-  nsAutoString mimeType;
-  nsresult rv = parser.GetType(mimeType);
-  if (NS_FAILED(rv)) {
-    return false;
-  }
+  MOZ_ASSERT(HaveGMPFor(aGMPService,
+                        NS_ConvertUTF16toUTF8(aKeySystem),
+                        NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  MOZ_ASSERT(IsH264ContentType(aContentType));
+  return
+    (!HaveGMPFor(aGMPService,
+                 NS_ConvertUTF16toUTF8(aKeySystem),
+                 NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                 NS_LITERAL_CSTRING("h264"))
+#ifdef XP_WIN
+    // Clearkey on Windows XP can't decode, but advertises that it can
+    // in its GMP info file.
+    || (aKeySystem.EqualsLiteral("org.w3.clearkey") && !IsVistaOrLater())
+#endif
+    ) && MP4Decoder::CanHandleMediaType(aContentType);
+}
 
-  if (!mimeType.EqualsLiteral("audio/mp4") &&
-      !mimeType.EqualsLiteral("audio/x-m4a") &&
-      !mimeType.EqualsLiteral("video/mp4")) {
+static bool
+GMPDecryptsAndGeckoDecodesAAC(mozIGeckoMediaPluginService* aGMPService,
+                              const nsAString& aKeySystem,
+                              const nsAString& aContentType)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPService,
+                        NS_ConvertUTF16toUTF8(aKeySystem),
+                        NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  MOZ_ASSERT(IsAACContentType(aContentType));
+  return
+    (!HaveGMPFor(aGMPService,
+                 NS_ConvertUTF16toUTF8(aKeySystem),
+                 NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                 NS_LITERAL_CSTRING("aac"))
+#ifdef XP_WIN
+    // Clearkey on Windows XP can't decode, but advertises that it can
+    // in its GMP info file.
+    || (aKeySystem.EqualsLiteral("org.w3.clearkey") && !IsVistaOrLater())
+#endif
+    ) && MP4Decoder::CanHandleMediaType(aContentType);
+}
+
+static bool
+IsSupported(mozIGeckoMediaPluginService* aGMPService,
+            const nsAString& aKeySystem,
+            const MediaKeySystemOptions& aConfig)
+{
+  if (!aConfig.mInitDataType.EqualsLiteral("cenc")) {
     return false;
   }
-
-  nsAutoString codecs;
-  parser.GetParameter("codecs", codecs);
-
-  NS_ConvertUTF16toUTF8 mimeTypeUTF8(mimeType);
-  bool hasAAC = false;
-  bool hasH264 = false;
-  bool hasMP3 = false;
-  if (!MP4Decoder::CanHandleMediaType(mimeTypeUTF8,
-                                      codecs,
-                                      hasAAC,
-                                      hasH264,
-                                      hasMP3) ||
-      hasMP3) {
+  if (!aConfig.mAudioCapability.IsEmpty() ||
+      !aConfig.mVideoCapability.IsEmpty()) {
+    // Don't support any capabilities until we know we have a CDM with
+    // capabilities...
     return false;
   }
-  return (!hasAAC ||
-          !HaveGMPFor(aGMPS,
-                      NS_ConvertUTF16toUTF8(aKeySystem),
-                      NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                      NS_LITERAL_CSTRING("aac"))) &&
-         (!hasH264 ||
-          !HaveGMPFor(aGMPS,
-                     NS_ConvertUTF16toUTF8(aKeySystem),
-                     NS_LITERAL_CSTRING(GMP_API_DECRYPTOR),
-                     NS_LITERAL_CSTRING("h264")));
-#else
-  return false;
-#endif
+  if (!aConfig.mAudioType.IsEmpty() &&
+      (!IsAACContentType(aConfig.mAudioType) ||
+       (!GMPDecryptsAndDecodesAAC(aGMPService, aKeySystem) &&
+        !GMPDecryptsAndGeckoDecodesAAC(aGMPService, aKeySystem, aConfig.mAudioType)))) {
+    return false;
+  }
+  if (!aConfig.mVideoType.IsEmpty() &&
+      (!IsH264ContentType(aConfig.mVideoType) ||
+       (!GMPDecryptsAndDecodesH264(aGMPService, aKeySystem) &&
+        !GMPDecryptsAndGeckoDecodesH264(aGMPService, aKeySystem, aConfig.mVideoType)))) {
+    return false;
+  }
+  return true;
 }
 
 /* static */
 bool
 MediaKeySystemAccess::IsSupported(const nsAString& aKeySystem,
                                   const Sequence<MediaKeySystemOptions>& aOptions)
 {
   nsCOMPtr<mozIGeckoMediaPluginService> mps =
     do_GetService("@mozilla.org/gecko-media-plugin-service;1");
   if (NS_WARN_IF(!mps)) {
     return false;
   }
 
-  for (size_t i = 0; i < aOptions.Length(); i++) {
-    const MediaKeySystemOptions& options = aOptions[i];
-    if (!options.mInitDataType.EqualsLiteral("cenc")) {
-      continue;
-    }
-    if (!options.mAudioCapability.IsEmpty() ||
-        !options.mVideoCapability.IsEmpty()) {
-      // Don't support any capabilites until we know we have a CDM with
-      // capabilities...
-      continue;
+  if (!HaveGMPFor(mps,
+                  NS_ConvertUTF16toUTF8(aKeySystem),
+                  NS_LITERAL_CSTRING(GMP_API_DECRYPTOR))) {
+    return false;
+  }
+
+  for (const MediaKeySystemOptions& config : aOptions) {
+    if (mozilla::dom::IsSupported(mps, aKeySystem, config)) {
+      return true;
     }
-    if (!options.mAudioType.IsEmpty() &&
-        !IsPlayableWithGMP(mps, aKeySystem, options.mAudioType)) {
-      continue;
-    }
-    if (!options.mVideoType.IsEmpty() &&
-        !IsPlayableWithGMP(mps, aKeySystem, options.mVideoType)) {
-      continue;
-    }
-
-    // Our sandbox provides an origin specific unique identifier, and the
-    // ability to persist data. We don't yet have a way to turn those off
-    // and on for specific GMPs/CDMs, so we don't check the uniqueidentifier
-    // and stateful attributes here.
-
-    return true;
   }
   return false;
 }
 
 /* static */
 void
 MediaKeySystemAccess::NotifyObservers(nsIDOMWindow* aWindow,
                                       const nsAString& aKeySystem,
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -10,16 +10,17 @@
 #include "MP4Demuxer.h"
 #include "mozilla/Preferences.h"
 #include "nsCharSeparatedTokenizer.h"
 #ifdef MOZ_EME
 #include "mozilla/CDMProxy.h"
 #endif
 #include "mozilla/Logging.h"
 #include "nsMimeTypes.h"
+#include "nsContentTypeParser.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsIGfxInfo.h"
 #include "AndroidBridge.h"
 #endif
@@ -153,40 +154,55 @@ MP4Decoder::CanHandleMediaType(const nsA
 #endif
   if ((!aType.EqualsASCII("video/mp4") && !aType.EqualsASCII("video/x-m4v")) ||
       !MP4Decoder::CanCreateH264Decoder()) {
     return false;
   }
 
   // Verify that all the codecs specifed are ones that we expect that
   // we can play.
-  nsCharSeparatedTokenizer tokenizer(aCodecs, ',');
-  bool expectMoreTokens = false;
-  while (tokenizer.hasMoreTokens()) {
-    const nsSubstring& token = tokenizer.nextToken();
-    expectMoreTokens = tokenizer.separatorAfterCurrentToken();
-    if (IsSupportedAudioCodec(token,
+  nsTArray<nsString> codecs;
+  if (!ParseCodecsString(aCodecs, codecs)) {
+    return false;
+  }
+  for (const nsString& codec : codecs) {
+    if (IsSupportedAudioCodec(codec,
                               aOutContainsAAC,
                               aOutContainsMP3)) {
       continue;
     }
-    if (IsSupportedH264Codec(token)) {
+    if (IsSupportedH264Codec(codec)) {
       aOutContainsH264 = true;
       continue;
     }
-    return false;
-  }
-  if (expectMoreTokens) {
-    // Last codec name was empty
+    // Some unsupported codec.
     return false;
   }
 
   return true;
 }
 
+/* static */ bool
+MP4Decoder::CanHandleMediaType(const nsAString& aContentType)
+{
+  nsContentTypeParser parser(aContentType);
+  nsAutoString mimeType;
+  nsresult rv = parser.GetType(mimeType);
+  if (NS_FAILED(rv)) {
+    return false;
+  }
+  nsString codecs;
+  parser.GetParameter("codecs", codecs);
+
+  bool ignoreAAC, ignoreH264, ignoreMP3;
+  return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType),
+                            codecs,
+                            ignoreAAC, ignoreH264, ignoreMP3);
+}
+
 static bool
 IsFFmpegAvailable()
 {
 #ifndef MOZ_FFMPEG
   return false;
 #else
   if (!Preferences::GetBool("media.fragmented-mp4.ffmpeg.enabled", false)) {
     return  false;
--- a/dom/media/fmp4/MP4Decoder.h
+++ b/dom/media/fmp4/MP4Decoder.h
@@ -34,16 +34,18 @@ public:
   // with a comma-delimited list of codecs to check support for. Notes in
   // out params wether the codecs string contains AAC or H.264.
   static bool CanHandleMediaType(const nsACString& aMIMEType,
                                  const nsAString& aCodecs,
                                  bool& aOutContainsAAC,
                                  bool& aOutContainsH264,
                                  bool& aOutContainsMP3);
 
+  static bool CanHandleMediaType(const nsAString& aMIMEType);
+
   // Returns true if the MP4 backend is preffed on.
   static bool IsEnabled();
 
   static bool IsVideoAccelerated(layers::LayersBackend aBackend, nsACString& aReason);
   static bool CanCreateAACDecoder();
   static bool CanCreateH264Decoder();
 };