Bug 1257716 - Handle clearkey encrypted WebMs. Put in place widevine checks. r?cpearce draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Tue, 26 Apr 2016 15:13:47 +1200
changeset 374959 a9af6cd7bcf472a85c2c601f24764bfaada77deb
parent 374958 74327b194c6de32e8215967eea1f2790b260001d
child 374960 ada71bfe11d9a115217660a07b30b73d89ebdab9
push id20121
push userbvandyk@mozilla.com
push dateFri, 03 Jun 2016 02:58:07 +0000
reviewerscpearce
bugs1257716
milestone49.0a1
Bug 1257716 - Handle clearkey encrypted WebMs. Put in place widevine checks. 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: BSvSOHoNbQA
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
@@ -328,11 +328,20 @@ bool
 IsH264ContentType(const nsAString& aContentType);
 
 bool
 IsAACContentType(const nsAString& aContentType);
 
 bool
 IsAACCodecString(const nsAString& aCodec);
 
+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)
@@ -350,16 +351,44 @@ GMPDecryptsAndDecodesH264(mozIGeckoMedia
                         NS_ConvertUTF16toUTF8(aKeySystem),
                         NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
   return HaveGMPFor(aGMPS,
                     NS_ConvertUTF16toUTF8(aKeySystem),
                     NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
                     NS_LITERAL_CSTRING("h264"));
 }
 
+static bool
+GMPDecryptsAndDecodesVP8(mozIGeckoMediaPluginService* aGMPS,
+                         const nsAString& aKeySystem,
+                         DecoderDoctorDiagnostics* aDiagnostics)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPS,
+                        NS_ConvertUTF16toUTF8(aKeySystem),
+                        NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  return HaveGMPFor(aGMPS,
+                    NS_ConvertUTF16toUTF8(aKeySystem),
+                    NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                    NS_LITERAL_CSTRING("vp8"));
+}
+
+static bool
+GMPDecryptsAndDecodesVP9(mozIGeckoMediaPluginService* aGMPS,
+                         const nsAString& aKeySystem,
+                         DecoderDoctorDiagnostics* aDiagnostics)
+{
+  MOZ_ASSERT(HaveGMPFor(aGMPS,
+                        NS_ConvertUTF16toUTF8(aKeySystem),
+                        NS_LITERAL_CSTRING(GMP_API_DECRYPTOR)));
+  return HaveGMPFor(aGMPS,
+                    NS_ConvertUTF16toUTF8(aKeySystem),
+                    NS_LITERAL_CSTRING(GMP_API_VIDEO_DECODER),
+                    NS_LITERAL_CSTRING("vp9"));
+}
+
 // 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.
 static bool
 GMPDecryptsAndGeckoDecodesH264(mozIGeckoMediaPluginService* aGMPService,
                                const nsAString& aKeySystem,
                                const nsAString& aContentType,
                                DecoderDoctorDiagnostics* aDiagnostics)
@@ -407,35 +436,108 @@ 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)) {
+    if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+      return GMPDecryptsAndGeckoDecodesVP8(aGMPService, aKeySystem, aVideoType, aDiagnostics);
+    } else if (aKeySystem.EqualsLiteral("come.widevine.alpha")) {
+      GMPDecryptsAndDecodesVP8(aGMPService, aKeySystem, aDiagnostics);
+    }
+  }
+  if (IsVP9ContentType(aVideoType)) {
+    if (aKeySystem.EqualsLiteral("org.w3.clearkey")) {
+      return GMPDecryptsAndGeckoDecodesVP9(aGMPService, aKeySystem, aVideoType, aDiagnostics);
+    } else if (aKeySystem.EqualsLiteral("come.widevine.alpha")) {
+      GMPDecryptsAndDecodesVP9(aGMPService, aKeySystem, aDiagnostics);
+    }
+  }
+  return false;
 }
 
 static bool
 IsSupported(mozIGeckoMediaPluginService* aGMPService,
             const nsAString& aKeySystem,
             const MediaKeySystemConfiguration& aConfig,
             DecoderDoctorDiagnostics* aDiagnostics)
 {
@@ -469,17 +571,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;
   }