Bug 1395922 - [P4] Make MDSM enter buffering state when MediaKeys is removed and resume the playback if setting same MediaKeys back. r=cpearce,jya
authorKilik Kuo <kikuo@mozilla.com>
Fri, 03 Nov 2017 20:14:49 +0800
changeset 443278 9cd31c6a8e2c30132865e7d2a73a91ebb5faffa6
parent 443277 d46f952f94f8428f6d4bea54f8dd54b42546da5d
child 443279 f59a7e727f39201d8af28b9cb08b26a45d22007e
push id1618
push userCallek@gmail.com
push dateThu, 11 Jan 2018 17:45:48 +0000
treeherdermozilla-release@882ca853e05a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce, jya
bugs1395922
milestone58.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1395922 - [P4] Make MDSM enter buffering state when MediaKeys is removed and resume the playback if setting same MediaKeys back. r=cpearce,jya MozReview-Commit-ID: KdmeGqoVgak
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1317,31 +1317,63 @@ MediaFormatReader::Init()
 
   mVideo.mTaskQueue = new TaskQueue(
     GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
     "MFR::mVideo::mTaskQueue");
 
   return NS_OK;
 }
 
-void
+bool
 MediaFormatReader::ResolveSetCDMPromiseIfDone(TrackType aTrack)
 {
   // When a CDM proxy is set, MFR would shutdown the existing MediaDataDecoder
   // and would create new one for specific track in the next Update.
   MOZ_ASSERT(OnTaskQueue());
 
+  if (mSetCDMPromise.IsEmpty()) {
+    return true;
+  }
+
+  MOZ_ASSERT(mCDMProxy);
   if (mSetCDMForTracks.contains(aTrack)) {
-    MOZ_ASSERT(!mSetCDMPromise.IsEmpty());
-
     mSetCDMForTracks -= aTrack;
-    if (mSetCDMForTracks.isEmpty()) {
-      mSetCDMPromise.Resolve(/* aIgnored = */ true, __func__);
-    }
+  }
+
+  if (mSetCDMForTracks.isEmpty()) {
+    LOGV("%s : Done ", __func__);
+    mSetCDMPromise.Resolve(/* aIgnored = */ true, __func__);
+    ScheduleUpdate(TrackInfo::kAudioTrack);
+    ScheduleUpdate(TrackInfo::kVideoTrack);
+    return true;
   }
+  LOGV("%s : %s track is ready.", __func__, TrackTypeToStr(aTrack));
+  return false;
+}
+
+void
+MediaFormatReader::PrepareToSetCDMForTrack(TrackType aTrack)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  LOGV("%s : %s", __func__, TrackTypeToStr(aTrack));
+
+  mSetCDMForTracks += aTrack;
+  if (mCDMProxy) {
+    // An old cdm proxy exists, so detaching old cdm proxy by shutting down
+    // MediaDataDecoder.
+    ShutdownDecoder(aTrack);
+  }
+  ScheduleUpdate(aTrack);
+}
+
+bool
+MediaFormatReader::IsDecoderWaitingForCDM(TrackType aTrack)
+{
+  MOZ_ASSERT(OnTaskQueue());
+  return IsEncrypted() && mSetCDMForTracks.contains(aTrack) && !mCDMProxy;
 }
 
 RefPtr<SetCDMPromise>
 MediaFormatReader::SetCDMProxy(CDMProxy* aProxy)
 {
   MOZ_ASSERT(OnTaskQueue());
   LOGV("SetCDMProxy (%p)", aProxy);
 
@@ -1352,37 +1384,39 @@ MediaFormatReader::SetCDMProxy(CDMProxy*
       __func__);
   }
 
   mSetCDMPromise.RejectIfExists(
     MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
                 "Another new CDM proxy is being set."),
     __func__);
 
-  if (HasAudio()) {
-    mSetCDMForTracks += TrackInfo::kAudioTrack;
-    ScheduleUpdate(TrackInfo::kAudioTrack);
-  }
-  if (HasVideo()) {
-    mSetCDMForTracks += TrackInfo::kVideoTrack;
-    ScheduleUpdate(TrackInfo::kVideoTrack);
-  }
-
   // Shutdown all decoders as switching CDM proxy indicates that it's
   // inappropriate for the existing decoders to continue decoding via the old
   // CDM proxy.
-  if (!mSetCDMForTracks.isEmpty()) {
-    ReleaseResources();
+  if (HasAudio()) {
+    PrepareToSetCDMForTrack(TrackInfo::kAudioTrack);
+  }
+  if (HasVideo()) {
+    PrepareToSetCDMForTrack(TrackInfo::kVideoTrack);
   }
 
   mCDMProxy = aProxy;
 
-  if (!mInitDone || mSetCDMForTracks.isEmpty()) {
-    // MFR is not initialized yet or demuxer is initialized without active
-    // audio and video, the promise can be resolved directly.
+  if (IsEncrypted() && !mCDMProxy) {
+    // Release old PDMFactory which contains an EMEDecoderModule.
+    mPlatform = nullptr;
+  }
+
+  if (!mInitDone || mSetCDMForTracks.isEmpty() || !mCDMProxy) {
+    // 1) MFR is not initialized yet or
+    // 2) Demuxer is initialized without active audio and video or
+    // 3) A null cdm proxy is set
+    // the promise can be resolved directly.
+    mSetCDMForTracks.clear();
     return SetCDMPromise::CreateAndResolve(/* aIgnored = */ true, __func__);
   }
 
   RefPtr<SetCDMPromise> p = mSetCDMPromise.Ensure(__func__);
   return p;
 }
 
 bool
@@ -2286,18 +2320,16 @@ MediaFormatReader::Update(TrackType aTra
   }
 
   LOGV("Processing update for %s", TrackTypeToStr(aTrack));
 
   bool needOutput = false;
   auto& decoder = GetDecoderData(aTrack);
   decoder.mUpdateScheduled = false;
 
-  ResolveSetCDMPromiseIfDone(aTrack);
-
   if (!mInitDone) {
     return;
   }
 
   if (aTrack == TrackType::kVideoTrack && mSkipRequest.Exists()) {
     LOGV("Skipping in progress, nothing more to do");
     return;
   }
@@ -2436,16 +2468,23 @@ MediaFormatReader::Update(TrackType aTra
       // There is no more samples left to be decoded and we are already in
       // EOS state. We can immediately reject the data promise.
       LOG("Rejecting %s promise: EOS", TrackTypeToStr(aTrack));
       decoder.RejectPromise(NS_ERROR_DOM_MEDIA_END_OF_STREAM, __func__);
     } else if (decoder.mWaitingForKey) {
       LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for key",
           TrackTypeToStr(aTrack));
       decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
+    } else if (IsDecoderWaitingForCDM(aTrack)) {
+      // Rejecting the promise could lead to entering buffering state for MDSM,
+      // once a qualified(with the same key system and sessions created by the
+      // same InitData) new cdm proxy is set, decoding can be resumed.
+      LOG("Rejecting %s promise: WAITING_FOR_DATA due to waiting for CDM",
+          TrackTypeToStr(aTrack));
+      decoder.RejectPromise(NS_ERROR_DOM_MEDIA_WAITING_FOR_DATA, __func__);
     }
   }
 
   if (decoder.mDrainState == DrainState::DrainRequested ||
       decoder.mDrainState == DrainState::PartialDrainPending) {
     if (decoder.mOutput.IsEmpty()) {
       DrainDecoder(aTrack);
     }
@@ -2493,43 +2532,46 @@ MediaFormatReader::Update(TrackType aTra
     }
     return;
   }
 
   bool needInput = NeedInput(decoder);
 
   LOGV("Update(%s) ni=%d no=%d in:%" PRIu64 " out:%" PRIu64
        " qs=%u decoding:%d flushing:%d desc:%s pending:%u waiting:%d eos:%d "
-       "ds:%d sid:%u",
+       "ds:%d sid:%u waitcdm:%d",
        TrackTypeToStr(aTrack),
        needInput,
        needOutput,
        decoder.mNumSamplesInput,
        decoder.mNumSamplesOutput,
        uint32_t(size_t(decoder.mSizeOfQueue)),
        decoder.mDecodeRequest.Exists(),
        decoder.mFlushing,
        decoder.mDescription.get(),
        uint32_t(decoder.mOutput.Length()),
        decoder.mWaitingForData,
        decoder.mDemuxEOS,
        int32_t(decoder.mDrainState),
-       decoder.mLastStreamSourceID);
-
-  if (IsWaitingOnCDMResource()) {
+       decoder.mLastStreamSourceID,
+       IsDecoderWaitingForCDM(aTrack));
+
+  if (IsWaitingOnCDMResource() || !ResolveSetCDMPromiseIfDone(aTrack)) {
     // If the content is encrypted, MFR won't start to create decoder until
     // CDMProxy is set.
     return;
   }
 
   if ((decoder.mWaitingForData &&
        (!decoder.mTimeThreshold || decoder.mTimeThreshold.ref().mWaiting)) ||
       (decoder.mWaitingForKey && decoder.mDecodeRequest.Exists())) {
     // Nothing more we can do at present.
-    LOGV("Still waiting for data or key.");
+    LOGV("Still waiting for data or key. data(%d)/key(%d)",
+         decoder.mWaitingForData,
+         decoder.mWaitingForKey);
     return;
   }
 
   if (decoder.CancelWaitingForKey()) {
     LOGV("No longer waiting for key. Resolving waiting promise");
     return;
   }
 
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -189,17 +189,16 @@ public:
   RefPtr<WaitForDataPromise> WaitForData(MediaData::Type aType);
 
   // The MediaDecoderStateMachine uses various heuristics that assume that
   // raw media data is arriving sequentially from a network channel. This
   // makes sense in the <video src="foo"> case, but not for more advanced use
   // cases like MSE.
   bool UseBufferingHeuristics() const { return mTrackDemuxersMayBlock; }
 
-  void ResolveSetCDMPromiseIfDone(TrackType aTrack);
   RefPtr<SetCDMPromise> SetCDMProxy(CDMProxy* aProxy);
 
   // Returns a string describing the state of the decoder data.
   // Used for debugging purposes.
   void GetMozDebugReaderData(nsACString& aString);
 
   // Switch the video decoder to NullDecoderModule. It might takes effective
   // since a few samples later depends on how much demuxed samples are already
@@ -789,15 +788,18 @@ private:
 
   MediaEventProducer<MediaResult> mOnDecodeWarning;
 
   RefPtr<FrameStatistics> mFrameStats;
 
   // Used in bug 1393399 for telemetry.
   const MediaDecoderOwnerID mMediaDecoderOwnerID;
 
+  bool ResolveSetCDMPromiseIfDone(TrackType aTrack);
+  void PrepareToSetCDMForTrack(TrackType aTrack);
   MozPromiseHolder<SetCDMPromise> mSetCDMPromise;
   TrackSet mSetCDMForTracks{};
+  bool IsDecoderWaitingForCDM(TrackType aTrack);
 };
 
 } // namespace mozilla
 
 #endif