Bug 1257716 - Handle clearkey encrypted WebMs. r=cpearce draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Wed, 08 Jun 2016 14:07:09 +1200
changeset 381841 2d4768697510f96bd744fd352e1e2fd4d9124485
parent 381837 d87b76177b2f5cf0839d73becebb614ab8a9ef7f
child 381842 7786ad8f0baae4efad84fc8b4358cf8b00df9e35
push id21571
push userbvandyk@mozilla.com
push dateTue, 28 Jun 2016 12:38:38 +0000
reviewerscpearce
bugs1257716
milestone50.0a1
Bug 1257716 - Handle clearkey encrypted WebMs. r=cpearce Handle encrypted WebM streams for the clearkey case. Add checking for the widevine case, though these should currently fail, as not all of the plumping is in place for widevine. MozReview-Commit-ID: 5d9fvc5IkZF
dom/media/MediaFormatReader.cpp
dom/media/VideoUtils.cpp
dom/media/VideoUtils.h
dom/media/eme/MediaKeySystemAccess.cpp
dom/media/mediasource/TrackBuffersManager.cpp
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -335,17 +335,17 @@ MediaFormatReader::OnDemuxerInitDone(nsr
 
   mIsEncrypted = crypto && crypto->IsEncrypted();
 
   if (mDecoder && crypto && crypto->IsEncrypted()) {
 #ifdef MOZ_EME
     // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
     for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) {
       NS_DispatchToMainThread(
-        new DispatchKeyNeededEvent(mDecoder, crypto->mInitDatas[i].mInitData, NS_LITERAL_STRING("cenc")));
+        new DispatchKeyNeededEvent(mDecoder, crypto->mInitDatas[i].mInitData, crypto->mInitDatas[i].mType));
     }
 #endif // MOZ_EME
     mInfo.mCrypto = *crypto;
   }
 
   int64_t videoDuration = HasVideo() ? mInfo.mVideo.mDuration : 0;
   int64_t audioDuration = HasAudio() ? mInfo.mAudio.mDuration : 0;
 
--- a/dom/media/VideoUtils.cpp
+++ b/dom/media/VideoUtils.cpp
@@ -485,9 +485,46 @@ IsAACContentType(const nsAString& aConte
     },
     [](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
     });
 }
 
+bool
+IsVorbisContentType(const nsAString& aContentType)
+{
+  return CheckContentType(aContentType,
+    [](const nsAString& type) {
+      return type.EqualsLiteral("audio/webm") ||
+             type.EqualsLiteral("audio/ogg");
+    },
+    [](const nsAString& codec) {
+      return codec.EqualsLiteral("vorbis");
+    });
+}
+
+bool
+IsVP8ContentType(const nsAString& aContentType)
+{
+  return CheckContentType(aContentType,
+    [](const nsAString& type) {
+      return type.EqualsLiteral("video/webm");
+    },
+    [](const nsAString& codec) {
+      return codec.EqualsLiteral("vp8");
+    });
+}
+
+bool
+IsVP9ContentType(const nsAString& aContentType)
+{
+  return CheckContentType(aContentType,
+    [](const nsAString& type) {
+      return type.EqualsLiteral("video/webm");
+    },
+    [](const nsAString& codec) {
+      return codec.EqualsLiteral("vp9");
+    });
+}
+
 } // end namespace mozilla
--- a/dom/media/VideoUtils.h
+++ b/dom/media/VideoUtils.h
@@ -442,11 +442,20 @@ StringListContains(const ListString& aLi
   for (const auto& listItem : MakeStringListRange(aList)) {
     if (listItem.Equals(aItem)) {
       return true;
     }
   }
   return false;
 }
 
+bool
+IsVorbisContentType(const nsAString& aContentType);
+
+bool
+IsVP8ContentType(const nsAString& aContentType);
+
+bool
+IsVP9ContentType(const nsAString& aContentType);
+
 } // end namespace mozilla
 
 #endif
--- a/dom/media/eme/MediaKeySystemAccess.cpp
+++ b/dom/media/eme/MediaKeySystemAccess.cpp
@@ -30,16 +30,17 @@
 #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"
 #include "DecoderDoctorDiagnostics.h"
+#include "WebMDecoder.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(MediaKeySystemAccess,
                                       mParent)
 NS_IMPL_CYCLE_COLLECTING_ADDREF(MediaKeySystemAccess)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(MediaKeySystemAccess)
@@ -407,35 +408,100 @@ GMPDecryptsAndGeckoDecodesAAC(mozIGeckoM
     }
     return false;
   }
 #endif
   return MP4Decoder::CanHandleMediaType(aContentType, aDiagnostics);
 }
 
 static bool
+GMPDecryptsAndGeckoDecodesVorbis(mozIGeckoMediaPluginService* aGMPService,
+                                const nsAString& aKeySystem,
+                                const nsAString& aContentType,
+                                DecoderDoctorDiagnostics* aDiagnostics)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPService,
+             NS_ConvertUTF16toUTF8(aKeySystem),
+             NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  MOZ_ASSERT(IsVorbisContentType(aContentType));
+  return !HaveGMPFor(aGMPService,
+                     NS_ConvertUTF16toUTF8(aKeySystem),
+                     NS_LITERAL_CSTRING(GMP_API_AUDIO_DECODER),
+                     NS_LITERAL_CSTRING("vorbis")) &&
+         WebMDecoder::CanHandleMediaType(aContentType);
+}
+
+static bool
+GMPDecryptsAndGeckoDecodesVP8(mozIGeckoMediaPluginService* aGMPService,
+                             const nsAString& aKeySystem,
+                             const nsAString& aContentType,
+                             DecoderDoctorDiagnostics* aDiagnostics)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPService,
+             NS_ConvertUTF16toUTF8(aKeySystem),
+             NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  MOZ_ASSERT(IsVP8ContentType(aContentType));
+  return !HaveGMPFor(aGMPService,
+                     NS_ConvertUTF16toUTF8(aKeySystem),
+                     NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                     NS_LITERAL_CSTRING("vp8")) &&
+         WebMDecoder::CanHandleMediaType(aContentType);
+}
+
+static bool
+GMPDecryptsAndGeckoDecodesVP9(mozIGeckoMediaPluginService* aGMPService,
+                             const nsAString& aKeySystem,
+                             const nsAString& aContentType,
+                             DecoderDoctorDiagnostics* aDiagnostics)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPService,
+             NS_ConvertUTF16toUTF8(aKeySystem),
+             NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  MOZ_ASSERT(IsVP9ContentType(aContentType));
+  return !HaveGMPFor(aGMPService,
+                     NS_ConvertUTF16toUTF8(aKeySystem),
+                     NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                     NS_LITERAL_CSTRING("vp9")) &&
+         WebMDecoder::CanHandleMediaType(aContentType);
+}
+
+static bool
 IsSupportedAudio(mozIGeckoMediaPluginService* aGMPService,
                  const nsAString& aKeySystem,
                  const nsAString& aAudioType,
                  DecoderDoctorDiagnostics* aDiagnostics)
 {
-  return IsAACContentType(aAudioType) &&
-         (GMPDecryptsAndDecodesAAC(aGMPService, aKeySystem, aDiagnostics) ||
-          GMPDecryptsAndGeckoDecodesAAC(aGMPService, aKeySystem, aAudioType, aDiagnostics));
+  if (IsAACContentType(aAudioType)) {
+    return GMPDecryptsAndDecodesAAC(aGMPService, aKeySystem, aDiagnostics) ||
+           GMPDecryptsAndGeckoDecodesAAC(aGMPService, aKeySystem, aAudioType, aDiagnostics);
+  }
+  if (IsVorbisContentType(aAudioType) && aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+    // GMP does not decode Vorbis, so don't bother checking
+    return GMPDecryptsAndGeckoDecodesVorbis(aGMPService, aKeySystem, aAudioType, aDiagnostics);
+  }
+  return false;
 }
 
 static bool
 IsSupportedVideo(mozIGeckoMediaPluginService* aGMPService,
                  const nsAString& aKeySystem,
                  const nsAString& aVideoType,
                  DecoderDoctorDiagnostics* aDiagnostics)
 {
-  return IsH264ContentType(aVideoType) &&
-         (GMPDecryptsAndDecodesH264(aGMPService, aKeySystem, aDiagnostics) ||
-          GMPDecryptsAndGeckoDecodesH264(aGMPService, aKeySystem, aVideoType, aDiagnostics));
+  if (IsH264ContentType(aVideoType)) {
+    return GMPDecryptsAndDecodesH264(aGMPService, aKeySystem, aDiagnostics) ||
+           GMPDecryptsAndGeckoDecodesH264(aGMPService, aKeySystem, aVideoType, aDiagnostics);
+  }
+  if (IsVP8ContentType(aVideoType) && aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+    return GMPDecryptsAndGeckoDecodesVP8(aGMPService, aKeySystem, aVideoType, aDiagnostics);
+  }
+  if (IsVP9ContentType(aVideoType) && aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+    return GMPDecryptsAndGeckoDecodesVP9(aGMPService, aKeySystem, aVideoType, aDiagnostics);
+  }
+  return false;
 }
 
 static bool
 IsSupported(mozIGeckoMediaPluginService* aGMPService,
             const nsAString& aKeySystem,
             const MediaKeySystemConfiguration& aConfig,
             DecoderDoctorDiagnostics* aDiagnostics)
 {
@@ -469,17 +535,17 @@ IsSupportedInitDataType(const nsString& 
   // All supported keySystems can handle "cenc" initDataType.
   // ClearKey also supports "keyids" and "webm" initDataTypes.
   return aCandidate.EqualsLiteral("cenc") ||
     ((aKeySystem.EqualsLiteral("org.w3.clearkey")
 #ifdef MOZ_WIDEVINE_EME
     || aKeySystem.EqualsLiteral("com.widevine.alpha")
 #endif
     ) &&
-    (aCandidate.EqualsLiteral("keyids") || aCandidate.EqualsLiteral("webm)")));
+    (aCandidate.EqualsLiteral("keyids") || aCandidate.EqualsLiteral("webm")));
 }
 
 static bool
 GetSupportedConfig(mozIGeckoMediaPluginService* aGMPService,
                    const nsAString& aKeySystem,
                    const MediaKeySystemConfiguration& aCandidate,
                    MediaKeySystemConfiguration& aOutConfig,
                    DecoderDoctorDiagnostics* aDiagnostics)
--- a/dom/media/mediasource/TrackBuffersManager.cpp
+++ b/dom/media/mediasource/TrackBuffersManager.cpp
@@ -1045,17 +1045,18 @@ TrackBuffersManager::OnDemuxerInitDone(n
   }
 
   UniquePtr<EncryptionInfo> crypto = mInputDemuxer->GetCrypto();
   if (crypto && crypto->IsEncrypted()) {
 #ifdef MOZ_EME
     // Try and dispatch 'encrypted'. Won't go if ready state still HAVE_NOTHING.
     for (uint32_t i = 0; i < crypto->mInitDatas.Length(); i++) {
       NS_DispatchToMainThread(
-        new DispatchKeyNeededEvent(mParentDecoder, crypto->mInitDatas[i].mInitData, NS_LITERAL_STRING("cenc")));
+        new DispatchKeyNeededEvent(mParentDecoder, crypto->mInitDatas[i].mInitData,
+                                   crypto->mInitDatas[i].mType));
     }
 #endif // MOZ_EME
     info.mCrypto = *crypto;
     // We clear our crypto init data array, so the MediaFormatReader will
     // not emit an encrypted event for the same init data again.
     info.mCrypto.mInitDatas.Clear();
     mEncrypted = true;
   }