Bug 1395922 - [P4] Make MDSM enter buffering state when MediaKeys is removed and resume the playback if setting same MediaKeys back. draft
authorKilik Kuo <kikuo@mozilla.com>
Fri, 03 Nov 2017 20:14:49 +0800
changeset 694023 6b8e97af4b54eb4e2b3c3fbc1835276f9a054e1e
parent 694022 d91340274257e8ce662468a065a92eed75669636
child 694024 ec3eef7c56392e947334d6844659f78eac541734
push id88026
push userkikuo@mozilla.com
push dateTue, 07 Nov 2017 09:43:50 +0000
bugs1395922
milestone58.0a1
Bug 1395922 - [P4] Make MDSM enter buffering state when MediaKeys is removed and resume the playback if setting same MediaKeys back. 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