Bug 1395922 - [P2] Make MediaFormatReader::SetCDMProxy asynchronously with a promise. r=cpearce,jya
authorKilik Kuo <kikuo@mozilla.com>
Fri, 03 Nov 2017 20:12:39 +0800
changeset 443928 2849269a64be100c949b4917fab599979d224f2e
parent 443927 8c1397fea2dd3f56fd17338ea147e2ecc0c514ae
child 443929 d013a75c11c802e11feade059f611167f46c7273
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 - [P2] Make MediaFormatReader::SetCDMProxy asynchronously with a promise. r=cpearce,jya 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',