Bug 1395922 - [P2] Make MediaFormatReader::SetCDMProxy asynchronously with a promise. draft
authorKilik Kuo <kikuo@mozilla.com>
Fri, 03 Nov 2017 20:12:39 +0800
changeset 694021 4724534e8454eaba9152959c8b31502e21cee326
parent 694020 efc2d8bad3cabc2e9a52d49c129b222105386ba5
child 694022 d91340274257e8ce662468a065a92eed75669636
push id88026
push userkikuo@mozilla.com
push dateTue, 07 Nov 2017 09:43:50 +0000
bugs1395922
milestone58.0a1
Bug 1395922 - [P2] Make MediaFormatReader::SetCDMProxy asynchronously with a promise. MozReview-Commit-ID: 7RarmmlA0lo
dom/media/MediaFormatReader.cpp
dom/media/MediaFormatReader.h
dom/media/MediaPromiseDefs.h
dom/media/moz.build
--- a/dom/media/MediaFormatReader.cpp
+++ b/dom/media/MediaFormatReader.cpp
@@ -1223,16 +1223,20 @@ MediaFormatReader::Shutdown()
   MOZ_ASSERT(OnTaskQueue());
   LOG("");
 
   mDemuxerInitRequest.DisconnectIfExists();
   mNotifyDataArrivedPromise.DisconnectIfExists();
   mMetadataPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mSeekPromise.RejectIfExists(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   mSkipRequest.DisconnectIfExists();
+  mSetCDMPromise.RejectIfExists(
+    MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
+                "MediaFormatReader is shutting down"),
+    __func__);
 
   if (mAudio.HasPromise()) {
     mAudio.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   }
   if (mVideo.HasPromise()) {
     mVideo.RejectPromise(NS_ERROR_DOM_MEDIA_CANCELED, __func__);
   }
 
@@ -1314,32 +1318,76 @@ MediaFormatReader::Init()
   mVideo.mTaskQueue = new TaskQueue(
     GetMediaThreadPool(MediaThreadType::PLATFORM_DECODER),
     "MFR::mVideo::mTaskQueue");
 
   return NS_OK;
 }
 
 void
+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 (mSetCDMForTracks.contains(aTrack)) {
+    MOZ_ASSERT(!mSetCDMPromise.IsEmpty());
+
+    mSetCDMForTracks -= aTrack;
+    if (mSetCDMForTracks.isEmpty()) {
+      mSetCDMPromise.Resolve(/* aIgnored = */ true, __func__);
+    }
+  }
+}
+
+RefPtr<SetCDMPromise>
 MediaFormatReader::SetCDMProxy(CDMProxy* aProxy)
 {
-  RefPtr<CDMProxy> proxy = aProxy;
-  RefPtr<MediaFormatReader> self = this;
-  nsCOMPtr<nsIRunnable> r =
-    NS_NewRunnableFunction("MediaFormatReader::SetCDMProxy", [=]() {
-      MOZ_ASSERT(self->OnTaskQueue());
-      self->mCDMProxy = proxy;
-      if (HasAudio()) {
-        self->ScheduleUpdate(TrackInfo::kAudioTrack);
-      }
-      if (HasVideo()) {
-        self->ScheduleUpdate(TrackInfo::kVideoTrack);
-      }
-    });
-  OwnerThread()->Dispatch(r.forget());
+  MOZ_ASSERT(OnTaskQueue());
+  LOGV("SetCDMProxy (%p)", aProxy);
+
+  if (mShutdown) {
+    return SetCDMPromise::CreateAndReject(
+      MediaResult(NS_ERROR_DOM_INVALID_STATE_ERR,
+                  "MediaFormatReader is shutting down"),
+      __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();
+  }
+
+  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.
+    return SetCDMPromise::CreateAndResolve(/* aIgnored = */ true, __func__);
+  }
+
+  RefPtr<SetCDMPromise> p = mSetCDMPromise.Ensure(__func__);
+  return p;
 }
 
 bool
 MediaFormatReader::IsWaitingOnCDMResource()
 {
   MOZ_ASSERT(OnTaskQueue());
   return IsEncrypted() && !mCDMProxy;
 }
@@ -2238,16 +2286,18 @@ 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;
   }
--- a/dom/media/MediaFormatReader.h
+++ b/dom/media/MediaFormatReader.h
@@ -13,16 +13,17 @@
 #include "mozilla/TaskQueue.h"
 #include "mozilla/Mutex.h"
 
 #include "FrameStatistics.h"
 #include "MediaEventSource.h"
 #include "MediaDataDemuxer.h"
 #include "MediaMetadataManager.h"
 #include "MediaPrefs.h"
+#include "MediaPromiseDefs.h"
 #include "nsAutoPtr.h"
 #include "PDMFactory.h"
 #include "SeekTarget.h"
 
 namespace mozilla {
 
 class CDMProxy;
 class GMPCrashHelper;
@@ -86,17 +87,16 @@ struct MOZ_STACK_CLASS MediaFormatReader
   MediaDecoderOwnerID mMediaDecoderOwnerID = nullptr;
 };
 
 class MediaFormatReader final
 {
   static const bool IsExclusive = true;
   typedef TrackInfo::TrackType TrackType;
   typedef MozPromise<bool, MediaResult, IsExclusive> NotifyDataArrivedPromise;
-
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MediaFormatReader)
 
 public:
   using TrackSet = EnumSet<TrackInfo::TrackType>;
   using MetadataPromise = MozPromise<MetadataHolder, MediaResult, IsExclusive>;
 
   template<typename Type>
   using DataPromise = MozPromise<RefPtr<Type>, MediaResult, IsExclusive>;
@@ -189,17 +189,18 @@ 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 SetCDMProxy(CDMProxy* aProxy);
+  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
   // queued in the original video decoder.
@@ -787,13 +788,16 @@ private:
   MediaEventProducer<void> mOnWaitingForKey;
 
   MediaEventProducer<MediaResult> mOnDecodeWarning;
 
   RefPtr<FrameStatistics> mFrameStats;
 
   // Used in bug 1393399 for telemetry.
   const MediaDecoderOwnerID mMediaDecoderOwnerID;
+
+  MozPromiseHolder<SetCDMPromise> mSetCDMPromise;
+  TrackSet mSetCDMForTracks{};
 };
 
 } // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/dom/media/MediaPromiseDefs.h
@@ -0,0 +1,19 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim:set ts=2 sw=2 sts=2 et cindent: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * 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/. */
+#ifndef MediaPromiseDefs_h_
+#define MediaPromiseDefs_h_
+
+#include "MediaResult.h"
+#include "mozilla/MozPromise.h"
+
+namespace mozilla {
+
+using SetCDMPromise =
+  MozPromise<bool /* aIgnored */, MediaResult, /* IsExclusive */ true>;
+
+} // namespace mozilla
+
+#endif
--- a/dom/media/moz.build
+++ b/dom/media/moz.build
@@ -116,16 +116,17 @@ EXPORTS += [
     'MediaDecoderOwner.h',
     'MediaDecoderStateMachine.h',
     'MediaEventSource.h',
     'MediaFormatReader.h',
     'MediaInfo.h',
     'MediaMetadataManager.h',
     'MediaMIMETypes.h',
     'MediaPrefs.h',
+    'MediaPromiseDefs.h',
     'MediaQueue.h',
     'MediaRecorder.h',
     'MediaResource.h',
     'MediaResourceCallback.h',
     'MediaResult.h',
     'MediaSegment.h',
     'MediaShutdownManager.h',
     'MediaStatistics.h',