Bug 1211339 Part 2 - Make MP4Decoder::CanHandleMediaType() check all codecs are supported by a PDM before reporting support. r=jya
☠☠ backed out by e3f18ef6b5ce ☠ ☠
authorChris Pearce <cpearce@mozilla.com>
Mon, 05 Oct 2015 22:39:10 +1300
changeset 299431 91bff7bc67d79185b294f0d075de04b6e3ca0f37
parent 299430 d34f694b13cfabe11d9561a2b328133c74a78f57
child 299432 adf5d43959d886622ba431e6b8825f0bad909357
push idunknown
push userunknown
push dateunknown
reviewersjya
bugs1211339
milestone44.0a1
Bug 1211339 Part 2 - Make MP4Decoder::CanHandleMediaType() check all codecs are supported by a PDM before reporting support. r=jya
dom/media/DecoderTraits.cpp
dom/media/VideoUtils.cpp
dom/media/VideoUtils.h
dom/media/fmp4/MP4Decoder.cpp
dom/media/fmp4/MP4Decoder.h
--- a/dom/media/DecoderTraits.cpp
+++ b/dom/media/DecoderTraits.cpp
@@ -342,19 +342,17 @@ IsDirectShowSupportedType(const nsACStri
 }
 #endif
 
 #ifdef MOZ_FMP4
 static bool
 IsMP4SupportedType(const nsACString& aType,
                    const nsAString& aCodecs = EmptyString())
 {
-  // MP4Decoder/Reader is currently used for MSE and mp4 files local playback.
-  bool haveAAC, haveMP3, haveH264;
-  return MP4Decoder::CanHandleMediaType(aType, aCodecs, haveAAC, haveH264, haveMP3);
+  return MP4Decoder::CanHandleMediaType(aType, aCodecs);
 }
 #endif
 
 /* static */ bool
 DecoderTraits::IsMP4Type(const nsACString& aType)
 {
 #ifdef MOZ_FMP4
   return IsMP4SupportedType(aType);
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -465,16 +465,25 @@ LogToBrowserConsole(const nsAString& aMs
     NS_WARNING("Failed to log message to console.");
     return;
   }
   nsAutoString msg(aMsg);
   console->LogStringMessage(msg.get());
 }
 
 bool
+IsAACCodecString(const nsAString& aCodec)
+{
+  return
+    aCodec.EqualsLiteral("mp4a.40.2") || // MPEG4 AAC-LC
+    aCodec.EqualsLiteral("mp4a.40.5") || // MPEG4 HE-AAC
+    aCodec.EqualsLiteral("mp4a.67"); // MPEG2 AAC-LC}
+}
+
+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();
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -333,11 +333,14 @@ bool
 ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs);
 
 bool
 IsH264ContentType(const nsAString& aContentType);
 
 bool
 IsAACContentType(const nsAString& aContentType);
 
+bool
+IsAACCodecString(const nsAString& aCodec);
+
 } // end namespace mozilla
 
 #endif
--- a/dom/media/fmp4/MP4Decoder.cpp
+++ b/dom/media/fmp4/MP4Decoder.cpp
@@ -11,16 +11,17 @@
 #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"
+#include "VideoUtils.h"
 
 #ifdef XP_WIN
 #include "mozilla/WindowsVersion.h"
 #endif
 #ifdef MOZ_WIDGET_ANDROID
 #include "nsIGfxInfo.h"
 #include "AndroidBridge.h"
 #endif
@@ -48,40 +49,17 @@ MP4Decoder::MP4Decoder()
 MediaDecoderStateMachine* MP4Decoder::CreateStateMachine()
 {
   MediaDecoderReader* reader = new MediaFormatReader(this, new MP4Demuxer(GetResource()));
 
   return new MediaDecoderStateMachine(this, reader);
 }
 
 static bool
-IsSupportedAudioCodec(const nsAString& aCodec,
-                      bool& aOutContainsAAC,
-                      bool& aOutContainsMP3)
-{
-  // AAC-LC or HE-AAC in M4A.
-  aOutContainsAAC = aCodec.EqualsASCII("mp4a.40.2")     // MPEG4 AAC-LC
-                    || aCodec.EqualsASCII("mp4a.40.5")  // MPEG4 HE-AAC
-                    || aCodec.EqualsASCII("mp4a.67");   // MPEG2 AAC-LC
-  if (aOutContainsAAC) {
-    return true;
-  }
-#ifndef MOZ_GONK_MEDIACODEC // B2G doesn't support MP3 in MP4 yet.
-  aOutContainsMP3 = aCodec.EqualsASCII("mp3");
-  if (aOutContainsMP3) {
-    return true;
-  }
-#else
-  aOutContainsMP3 = false;
-#endif
-  return false;
-}
-
-static bool
-IsSupportedH264Codec(const nsAString& aCodec)
+IsWhitelistedH264Codec(const nsAString& aCodec)
 {
   int16_t profile = 0, level = 0;
 
   if (!ExtractH264CodecDetails(aCodec, profile, level)) {
     return false;
   }
 
 #ifdef XP_WIN
@@ -105,62 +83,84 @@ IsSupportedH264Codec(const nsAString& aC
          (profile == H264_PROFILE_BASE ||
           profile == H264_PROFILE_MAIN ||
           profile == H264_PROFILE_EXTENDED ||
           profile == H264_PROFILE_HIGH);
 }
 
 /* static */
 bool
-MP4Decoder::CanHandleMediaType(const nsACString& aType,
-                               const nsAString& aCodecs,
-                               bool& aOutContainsAAC,
-                               bool& aOutContainsH264,
-                               bool& aOutContainsMP3)
+MP4Decoder::CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
+                               const nsAString& aCodecs)
 {
   if (!IsEnabled()) {
     return false;
   }
 
-  if (aType.EqualsASCII("audio/mp4") || aType.EqualsASCII("audio/x-m4a")) {
-    return MP4Decoder::CanCreateAACDecoder() &&
-           (aCodecs.IsEmpty() ||
-            IsSupportedAudioCodec(aCodecs,
-                                  aOutContainsAAC,
-                                  aOutContainsMP3));
-  }
-
-#ifdef MOZ_GONK_MEDIACODEC
-  if (aType.EqualsASCII(VIDEO_3GPP)) {
-    return Preferences::GetBool("media.fragmented-mp4.gonk.enabled", false);
-  }
-#endif
-  if ((!aType.EqualsASCII("video/mp4") && !aType.EqualsASCII("video/x-m4v")) ||
-      !MP4Decoder::CanCreateH264Decoder()) {
+  // Whitelist MP4 types, so they explicitly match what we encounter on
+  // the web, as opposed to what we use internally (i.e. what our demuxers
+  // etc output).
+  if (!aMIMETypeExcludingCodecs.EqualsASCII("audio/mp4") &&
+      !aMIMETypeExcludingCodecs.EqualsASCII("audio/x-m4a") &&
+      !aMIMETypeExcludingCodecs.EqualsASCII("video/mp4") &&
+      !aMIMETypeExcludingCodecs.EqualsASCII("video/x-m4v")) {
     return false;
   }
 
-  // Verify that all the codecs specifed are ones that we expect that
-  // we can play.
-  nsTArray<nsString> codecs;
-  if (!ParseCodecsString(aCodecs, codecs)) {
+#ifdef MOZ_GONK_MEDIACODEC
+  if (aMIMETypeExcludingCodecs.EqualsASCII(VIDEO_3GPP)) {
+    return Preferences::GetBool("media.fragmented-mp4.gonk.enabled", false);
+  }
+#endif
+
+  nsTArray<nsCString> codecMimes;
+  if (aCodecs.IsEmpty()) {
+    // No codecs specified. Assume AAC/H.264
+    if (aMIMETypeExcludingCodecs.EqualsLiteral("audio/mp4") ||
+        aMIMETypeExcludingCodecs.EqualsLiteral("audio/x-m4a")) {
+      codecMimes.AppendElement(NS_LITERAL_CSTRING("audio/mp4a-latm"));
+    } else if (aMIMETypeExcludingCodecs.EqualsLiteral("video/mp4") ||
+               aMIMETypeExcludingCodecs.EqualsLiteral("video/x-m4v")) {
+      codecMimes.AppendElement(NS_LITERAL_CSTRING("video/avc"));
+    }
+  } else {
+    // Verify that all the codecs specified are ones that we expect that
+    // we can play.
+    nsTArray<nsString> codecs;
+    if (!ParseCodecsString(aCodecs, codecs)) {
+      return false;
+    }
+    for (const nsString& codec : codecs) {
+      if (IsAACCodecString(codec)) {
+        codecMimes.AppendElement(NS_LITERAL_CSTRING("audio/mp4a-latm"));
+        continue;
+      }
+      if (codec.EqualsLiteral("mp3")) {
+        codecMimes.AppendElement(NS_LITERAL_CSTRING("audio/mpeg"));
+        continue;
+      }
+      if (IsWhitelistedH264Codec(codec)) {
+        codecMimes.AppendElement(NS_LITERAL_CSTRING("video/avc"));
+        continue;
+      }
+      // Some unsupported codec.
+      return false;
+    }
+  }
+
+  // Verify that we have a PDM that supports the whitelisted types.
+  PlatformDecoderModule::Init();
+  nsRefPtr<PlatformDecoderModule> platform = PlatformDecoderModule::Create();
+  if (!platform) {
     return false;
   }
-  for (const nsString& codec : codecs) {
-    if (IsSupportedAudioCodec(codec,
-                              aOutContainsAAC,
-                              aOutContainsMP3)) {
-      continue;
+  for (const nsCString& codecMime : codecMimes) {
+    if (!platform->SupportsMimeType(codecMime)) {
+      return false;
     }
-    if (IsSupportedH264Codec(codec)) {
-      aOutContainsH264 = true;
-      continue;
-    }
-    // Some unsupported codec.
-    return false;
   }
 
   return true;
 }
 
 /* static */ bool
 MP4Decoder::CanHandleMediaType(const nsAString& aContentType)
 {
@@ -168,20 +168,17 @@ MP4Decoder::CanHandleMediaType(const nsA
   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);
+  return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType), codecs);
 }
 
 static bool
 IsFFmpegAvailable()
 {
 #ifndef MOZ_FFMPEG
   return false;
 #else
--- a/dom/media/fmp4/MP4Decoder.h
+++ b/dom/media/fmp4/MP4Decoder.h
@@ -24,21 +24,18 @@ public:
   }
 
   virtual MediaDecoderStateMachine* CreateStateMachine() override;
 
   // Returns true if aMIMEType is a type that we think we can render with the
   // a MP4 platform decoder backend. If aCodecs is non emtpy, it is filled
   // 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 nsACString& aMIMETypeExcludingCodecs,
+                                 const nsAString& aCodecs);
 
   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();