Handle clearkey encrypted WebMs. Requesting feedback: r?cpearce draft
authorBryce Van Dyk <bvandyk@mozilla.com>
Tue, 26 Apr 2016 15:13:47 +1200
changeset 371147 254ce4056298a991b0e2ce7f1d414c39b1af28b7
parent 371146 4760d75856af777243f002495dd0a5de518acb00
child 521926 de4f557cfb5ed8f5d51d8143ebecc195b689fcb9
push id19253
push userbvandyk@mozilla.com
push dateThu, 26 May 2016 01:17:42 +0000
reviewerscpearce
milestone49.0a1
Handle clearkey encrypted WebMs. Requesting feedback: r?cpearce Make sure an encrypted event is created for WebMs. Handle encrypted WebM streams for the clearkey case. 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
dom/media/webm/WebMDecoder.cpp
dom/media/webm/WebMDecoder.h
dom/media/webm/WebMDemuxer.cpp
dom/media/webm/WebMDemuxer.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -333,17 +333,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
@@ -493,9 +493,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
@@ -323,20 +323,35 @@ private:
 };
 
 void
 LogToBrowserConsole(const nsAString& aMsg);
 
 bool
 ParseCodecsString(const nsAString& aCodecs, nsTArray<nsString>& aOutCodecs);
 
+// Should only be called from main thread due to nsContentTypeParser
 bool
 IsH264ContentType(const nsAString& aContentType);
 
+// Should only be called from main thread due to nsContentTypeParser
 bool
 IsAACContentType(const nsAString& aContentType);
 
+// Should only be called from main thread due to nsContentTypeParser
 bool
 IsAACCodecString(const nsAString& aCodec);
 
+// Should only be called from main thread due to nsContentTypeParser
+bool
+IsVorbisContentType(const nsAString& aContentType);
+
+// Should only be called from main thread due to nsContentTypeParser
+bool
+IsVP8ContentType(const nsAString& aContentType);
+
+// Should only be called from main thread due to nsContentTypeParser
+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,102 @@ 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)) {
+    // 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)) {
+    return GMPDecryptsAndDecodesVP8(aGMPService, aKeySystem, aDiagnostics) ||
+           GMPDecryptsAndGeckoDecodesVP8(aGMPService, aKeySystem, aVideoType, aDiagnostics);
+  }
+  if (IsVP9ContentType(aVideoType)) {
+    return GMPDecryptsAndDecodesVP9(aGMPService, aKeySystem, aDiagnostics) ||
+           GMPDecryptsAndGeckoDecodesVP9(aGMPService, aKeySystem, aVideoType, aDiagnostics);
+  }
+  return false;
 }
 
 static bool
 IsSupported(mozIGeckoMediaPluginService* aGMPService,
             const nsAString& aKeySystem,
             const MediaKeySystemConfiguration& aConfig,
             DecoderDoctorDiagnostics* aDiagnostics)
 {
@@ -469,17 +565,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,17 @@ 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;
   }
--- a/dom/media/webm/WebMDecoder.cpp
+++ b/dom/media/webm/WebMDecoder.cpp
@@ -4,16 +4,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "mozilla/Preferences.h"
 #include "MediaDecoderStateMachine.h"
 #include "WebMDemuxer.h"
 #include "WebMDecoder.h"
 #include "VideoUtils.h"
+#include "nsContentTypeParser.h"
 
 namespace mozilla {
 
 MediaDecoderStateMachine* WebMDecoder::CreateStateMachine()
 {
   mReader =
     new MediaFormatReader(this, new WebMDemuxer(GetResource()), GetVideoFrameContainer());
   return new MediaDecoderStateMachine(this, mReader);
@@ -65,16 +66,32 @@ WebMDecoder::CanHandleMediaType(const ns
       continue;
     }
     // Some unsupported codec.
     return false;
   }
   return true;
 }
 
+/* static */ bool
+WebMDecoder::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);
+
+  return CanHandleMediaType(NS_ConvertUTF16toUTF8(mimeType),
+                            codecs);
+}
+
 void
 WebMDecoder::GetMozDebugReaderData(nsAString& aString)
 {
   if (mReader) {
     mReader->GetMozDebugReaderData(aString);
   }
 }
 
--- a/dom/media/webm/WebMDecoder.h
+++ b/dom/media/webm/WebMDecoder.h
@@ -28,16 +28,18 @@ public:
 
   // Returns true if aMIMEType is a type that we think we can render with the
   // a WebM 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 whether the codecs string contains Opus/Vorbis or VP8/VP9.
   static bool CanHandleMediaType(const nsACString& aMIMETypeExcludingCodecs,
                                  const nsAString& aCodecs);
 
+  static bool CanHandleMediaType(const nsAString& aContentType);
+
   void GetMozDebugReaderData(nsAString& aString) override;
 
 private:
   RefPtr<MediaFormatReader> mReader;
 };
 
 } // namespace mozilla
 
--- a/dom/media/webm/WebMDemuxer.cpp
+++ b/dom/media/webm/WebMDemuxer.cpp
@@ -359,16 +359,17 @@ WebMDemuxer::ReadMetadata()
           mInfo.mVideo.mStereoMode = StereoMode::RIGHT_LEFT;
           break;
       }
       uint64_t duration = 0;
       r = nestegg_duration(mContext, &duration);
       if (!r) {
         mInfo.mVideo.mDuration = media::TimeUnit::FromNanoseconds(duration).ToMicroseconds();
       }
+      mInfo.mVideo.mCrypto = GetTrackCrypto(track);
     } else if (type == NESTEGG_TRACK_AUDIO && !mHasAudio) {
       nestegg_audio_params params;
       r = nestegg_track_audio_params(mContext, track, &params);
       if (r == -1) {
         return NS_ERROR_FAILURE;
       }
 
       mAudioTrack = track;
@@ -421,16 +422,17 @@ WebMDemuxer::ReadMetadata()
         mInfo.mAudio.mCodecSpecificConfig->AppendElements(headers[0],
                                                           headerLens[0]);
       }
       uint64_t duration = 0;
       r = nestegg_duration(mContext, &duration);
       if (!r) {
         mInfo.mAudio.mDuration = media::TimeUnit::FromNanoseconds(duration).ToMicroseconds();
       }
+      mInfo.mAudio.mCrypto = GetTrackCrypto(track);
     }
   }
   return NS_OK;
 }
 
 bool
 WebMDemuxer::IsSeekable() const
 {
@@ -481,19 +483,72 @@ WebMDemuxer::NotifyDataRemoved()
     mBufferedState->NotifyDataArrived(mInitData->Elements(), mInitData->Length(), 0);
   }
   mNeedReIndex = true;
 }
 
 UniquePtr<EncryptionInfo>
 WebMDemuxer::GetCrypto()
 {
+  unsigned int ntracks = 0;
+  bool has_encrypted_track = false;
+
+  int r = nestegg_track_count(mContext, &ntracks);
+
+  if (r == -1) {
+    WEBM_DEBUG("nestegg_track_count failed r=%d", r);
+    return nullptr;
+  }
+
+  auto crypto = MakeUnique<EncryptionInfo>();
+  for (unsigned int track = 0; track < ntracks; ++track) {
+    // For every track if we have valid crypto info add the key ID to the
+    // overall crypto information returned for this demuxer
+    CryptoTrack trackCrypto = GetTrackCrypto(track);
+    if(trackCrypto.mValid) {
+      has_encrypted_track = true;
+      crypto->AddInitData(NS_LITERAL_STRING("webm"), Move(trackCrypto.mKeyId));
+    }
+  }
+
+  if (has_encrypted_track) {
+    return crypto;
+  }
   return nullptr;
 }
 
+CryptoTrack
+WebMDemuxer::GetTrackCrypto(size_t aTrackNumber) {
+  const int WEBM_IV_SIZE = 16;
+  const unsigned char * content_enc_key_id;
+  size_t content_enc_key_id_length;
+  CryptoTrack crypto;
+
+  int r = nestegg_track_content_enc_key_id(mContext, aTrackNumber, &content_enc_key_id, &content_enc_key_id_length);
+  if (r == -1) {
+    WEBM_DEBUG("nestegg_track_content_enc_key_id failed r=%d", r);
+    return crypto;
+  }
+
+  uint32_t i;
+  nsTArray<uint8_t> initData;
+  for (i = 0; i < content_enc_key_id_length; i++) {
+    initData.AppendElement(content_enc_key_id[i]);
+  }
+
+  if (!initData.IsEmpty()) {
+    crypto.mValid = true;
+    // crypto.mMode is not used for WebMs
+    crypto.mIVSize = WEBM_IV_SIZE;
+    crypto.mKeyId = Move(initData);
+  }
+
+  return crypto;
+}
+
 bool
 WebMDemuxer::GetNextPacket(TrackInfo::TrackType aType, MediaRawDataQueue *aSamples)
 {
   if (mIsMediaSource) {
     // To ensure mLastWebMBlockOffset is properly up to date.
     EnsureUpToDateIndex();
   }
 
@@ -551,51 +606,58 @@ WebMDemuxer::GetNextPacket(TrackInfo::Tr
 
   if (mIsMediaSource && next_tstamp == INT64_MIN) {
     return false;
   }
 
   int64_t discardPadding = 0;
   (void) nestegg_packet_discard_padding(holder->Packet(), &discardPadding);
 
+  int packet_encryption = nestegg_packet_encryption(holder->Packet());
+
   for (uint32_t i = 0; i < count; ++i) {
     unsigned char* data;
     size_t length;
     r = nestegg_packet_data(holder->Packet(), i, &data, &length);
     if (r == -1) {
       WEBM_DEBUG("nestegg_packet_data failed r=%d", r);
       return false;
     }
     bool isKeyframe = false;
     if (aType == TrackInfo::kAudioTrack) {
       isKeyframe = true;
     } else if (aType == TrackInfo::kVideoTrack) {
-      vpx_codec_stream_info_t si;
-      PodZero(&si);
-      si.sz = sizeof(si);
-      switch (mVideoCodec) {
+      if (packet_encryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
+        // Packet is encrypted, can't peek, use packet info
+        isKeyframe = nestegg_packet_has_keyframe(holder->Packet()) == NESTEGG_PACKET_HAS_KEYFRAME_TRUE;
+      } else {
+        vpx_codec_stream_info_t si;
+        PodZero(&si);
+        si.sz = sizeof(si);
+        switch (mVideoCodec) {
         case NESTEGG_CODEC_VP8:
           vpx_codec_peek_stream_info(vpx_codec_vp8_dx(), data, length, &si);
           break;
         case NESTEGG_CODEC_VP9:
           vpx_codec_peek_stream_info(vpx_codec_vp9_dx(), data, length, &si);
           break;
-      }
-      isKeyframe = si.is_kf;
-      if (isKeyframe) {
-        // We only look for resolution changes on keyframes for both VP8 and
-        // VP9. Other resolution changes are invalid.
-        if (mLastSeenFrameWidth.isSome() && mLastSeenFrameHeight.isSome() &&
+        }
+        isKeyframe = si.is_kf;
+        if (isKeyframe) {
+          // We only look for resolution changes on keyframes for both VP8 and
+          // VP9. Other resolution changes are invalid.
+          if (mLastSeenFrameWidth.isSome() && mLastSeenFrameHeight.isSome() &&
             (si.w != mLastSeenFrameWidth.value() ||
-             si.h != mLastSeenFrameHeight.value())) {
-          mInfo.mVideo.mDisplay = nsIntSize(si.w, si.h);
-          mSharedVideoTrackInfo = new SharedTrackInfo(mInfo.mVideo, ++sStreamSourceID);
+              si.h != mLastSeenFrameHeight.value())) {
+            mInfo.mVideo.mDisplay = nsIntSize(si.w, si.h);
+            mSharedVideoTrackInfo = new SharedTrackInfo(mInfo.mVideo, ++sStreamSourceID);
+          }
+          mLastSeenFrameWidth = Some(si.w);
+          mLastSeenFrameHeight = Some(si.h);
         }
-        mLastSeenFrameWidth = Some(si.w);
-        mLastSeenFrameHeight = Some(si.h);
       }
     }
 
     WEBM_DEBUG("push sample tstamp: %ld next_tstamp: %ld length: %ld kf: %d",
                tstamp, next_tstamp, length, isKeyframe);
     RefPtr<MediaRawData> sample = new MediaRawData(data, length);
     sample->mTimecode = tstamp;
     sample->mTime = tstamp;
@@ -603,16 +665,40 @@ WebMDemuxer::GetNextPacket(TrackInfo::Tr
     sample->mOffset = holder->Offset();
     sample->mKeyframe = isKeyframe;
     if (discardPadding && i == count - 1) {
       uint8_t c[8];
       BigEndian::writeInt64(&c[0], discardPadding);
       sample->mExtraData = new MediaByteBuffer;
       sample->mExtraData->AppendElements(&c[0], 8);
     }
+
+    if (packet_encryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_UNENCRYPTED ||
+        packet_encryption == NESTEGG_PACKET_HAS_SIGNAL_BYTE_ENCRYPTED) {
+      nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
+      unsigned char const* iv;
+      size_t ivLength;
+      nestegg_packet_iv(holder->Packet(), &iv, &ivLength);
+      writer->mCrypto.mValid = true;
+      writer->mCrypto.mIVSize = ivLength;
+      if (ivLength == 0) {
+        // Frame is not encrypted
+        writer->mCrypto.mPlainSizes.AppendElement(length);
+        writer->mCrypto.mEncryptedSizes.AppendElement(0);
+      } else {
+        // Frame is encrypted
+        writer->mCrypto.mIV.AppendElements(iv, 8);
+        // Iv from samples is 64 bits, must be padded with 64 bits more 0s in compliance with spec
+        for (uint32_t i = 0; i < 8; i++) {
+          writer->mCrypto.mIV.AppendElement(0);
+        }
+        writer->mCrypto.mPlainSizes.AppendElement(0);
+        writer->mCrypto.mEncryptedSizes.AppendElement(length);
+      }
+    }
     if (aType == TrackInfo::kVideoTrack) {
       sample->mTrackInfo = mSharedVideoTrackInfo;
     }
     aSamples->Push(sample);
   }
   return true;
 }
 
@@ -969,16 +1055,25 @@ WebMTrackDemuxer::Reset()
   } else {
     mNextKeyframeTime.reset();
   }
 }
 
 void
 WebMTrackDemuxer::UpdateSamples(nsTArray<RefPtr<MediaRawData>>& aSamples)
 {
+  for (size_t i = 0; i < aSamples.Length(); i++) {
+    MediaRawData* sample = aSamples[i];
+    if (sample->mCrypto.mValid) {
+      nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter());
+      writer->mCrypto.mMode = mInfo->mCrypto.mMode;
+      writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize;
+      writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId);
+    }
+  }
   if (mNextKeyframeTime.isNothing() ||
       aSamples.LastElement()->mTime >= mNextKeyframeTime.value().ToMicroseconds()) {
     SetNextKeyFrameTime();
   }
 }
 
 nsresult
 WebMTrackDemuxer::GetNextRandomAccessPoint(media::TimeUnit* aTime)
--- a/dom/media/webm/WebMDemuxer.h
+++ b/dom/media/webm/WebMDemuxer.h
@@ -138,16 +138,17 @@ private:
   void Cleanup();
   void InitBufferedState();
   nsresult ReadMetadata();
   void NotifyDataArrived() override;
   void NotifyDataRemoved() override;
   void EnsureUpToDateIndex();
   media::TimeIntervals GetBuffered();
   nsresult SeekInternal(const media::TimeUnit& aTarget);
+  CryptoTrack GetTrackCrypto(size_t aTrackNumber);
 
   // Read a packet from the nestegg file. Returns nullptr if all packets for
   // the particular track have been read. Pass TrackInfo::kVideoTrack or
   // TrackInfo::kVideoTrack to indicate the type of the packet we want to read.
   RefPtr<NesteggPacketHolder> NextPacket(TrackInfo::TrackType aType);
 
   // Internal method that demuxes the next packet from the stream. The caller
   // is responsible for making sure it doesn't get lost.