Bug 1310140. Part 13 - handle resuming video decoding in various state objects. r=kaku,kamidphish
authorJW Wang <jwwang@mozilla.com>
Tue, 18 Oct 2016 14:56:36 +0800
changeset 318481 5e74cf7dec1b68c0f0bb601452497b2c564c138c
parent 318480 385c13b85702c3349b4fb402927cdeb597a89573
child 318482 2d160c26138431569ae79e70bc57f925bd80ba6f
push id30841
push usercbook@mozilla.com
push dateWed, 19 Oct 2016 14:59:31 +0000
treeherdermozilla-central@8b2d92343bcb [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskaku, kamidphish
bugs1310140
milestone52.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 1310140. Part 13 - handle resuming video decoding in various state objects. r=kaku,kamidphish Note StateObject::HandleResumeVideoDecoding() doesn't check mIsReaderSuspended because if the reader is suspended, MDSM must be in the dormant state and won't resume video decoding. MozReview-Commit-ID: FY4rdFmk56C
dom/media/MediaDecoderStateMachine.cpp
--- a/dom/media/MediaDecoderStateMachine.cpp
+++ b/dom/media/MediaDecoderStateMachine.cpp
@@ -212,16 +212,18 @@ public:
   virtual RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) = 0;
 
   virtual bool HandleAudioCaptured() { return false; }
 
   virtual RefPtr<ShutdownPromise> HandleShutdown();
 
   virtual void HandleVideoSuspendTimeout() = 0;
 
+  virtual void HandleResumeVideoDecoding();
+
   virtual void DumpDebugInfo() {}
 
 protected:
   using Master = MediaDecoderStateMachine;
   explicit StateObject(Master* aPtr) : mMaster(aPtr) {}
   TaskQueue* OwnerThread() const { return mMaster->mTaskQueue; }
   MediaResource* Resource() const { return mMaster->mResource; }
   MediaDecoderReaderWrapper* Reader() const { return mMaster->mReader; }
@@ -269,16 +271,17 @@ protected:
 class MediaDecoderStateMachine::DecodeMetadataState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
   explicit DecodeMetadataState(Master* aPtr) : StateObject(aPtr) {}
 
   void Enter()
   {
+    MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
     MOZ_ASSERT(!mMetadataRequest.Exists());
     SLOG("Dispatching AsyncReadMetadata");
 
     // Set mode to METADATA since we are about to read metadata.
     Resource()->SetReadMode(MediaCacheStream::MODE_METADATA);
 
     // We disconnect mMetadataRequest in Exit() so it is fine to capture
     // a raw pointer here.
@@ -314,16 +317,22 @@ public:
     return MediaDecoder::SeekPromise::CreateAndReject(true, __func__);
   }
 
   void HandleVideoSuspendTimeout() override
   {
     // Do nothing since no decoders are created yet.
   }
 
+  void HandleResumeVideoDecoding() override
+  {
+    // We never suspend video decoding in this state.
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   void OnMetadataRead(MetadataHolder* aMetadata);
 
   void OnMetadataNotRead(const MediaResult& aError)
   {
     mMetadataRequest.Complete();
     SWARN("Decode metadata failed, shutting down decoder");
     mMaster->DecodeError(aError);
@@ -338,17 +347,21 @@ private:
 };
 
 class MediaDecoderStateMachine::WaitForCDMState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
   explicit WaitForCDMState(Master* aPtr) : StateObject(aPtr) {}
 
-  void Enter(bool aPendingDormant) { mPendingDormant = aPendingDormant; }
+  void Enter(bool aPendingDormant)
+  {
+    MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
+    mPendingDormant = aPendingDormant;
+  }
 
   State GetState() const override
   {
     return DECODER_STATE_WAIT_FOR_CDM;
   }
 
   bool HandleDormant(bool aDormant) override;
 
@@ -362,16 +375,22 @@ public:
     return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
   }
 
   void HandleVideoSuspendTimeout() override
   {
     // Do nothing since no decoders are created yet.
   }
 
+  void HandleResumeVideoDecoding() override
+  {
+    // We never suspend video decoding in this state.
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   bool mPendingDormant = false;
 };
 
 class MediaDecoderStateMachine::DormantState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
@@ -400,16 +419,21 @@ public:
     mMaster->mQueuedSeek.mTarget = aTarget;
     return mMaster->mQueuedSeek.mPromise.Ensure(__func__);
   }
 
   void HandleVideoSuspendTimeout() override
   {
     // Do nothing since we've released decoders in Enter().
   }
+
+  void HandleResumeVideoDecoding() override
+  {
+    // Do nothing since we won't resume decoding until exiting dormant.
+  }
 };
 
 class MediaDecoderStateMachine::DecodingFirstFrameState
   : public MediaDecoderStateMachine::StateObject
 {
 public:
   explicit DecodingFirstFrameState(Master* aPtr) : StateObject(aPtr) {}
 
@@ -442,16 +466,22 @@ public:
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
 
   void HandleVideoSuspendTimeout() override
   {
     // Do nothing for we need to decode the 1st video frame to get the dimensions.
   }
 
+  void HandleResumeVideoDecoding() override
+  {
+    // We never suspend video decoding in this state.
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   // Notify FirstFrameLoaded if having decoded first frames and
   // transition to SEEKING if there is any pending seek, or DECODING otherwise.
   void MaybeFinishDecodeFirstFrame();
 };
 
 class MediaDecoderStateMachine::DecodingState
   : public MediaDecoderStateMachine::StateObject
@@ -724,16 +754,22 @@ public:
 
   RefPtr<MediaDecoder::SeekPromise> HandleSeek(SeekTarget aTarget) override;
 
   void HandleVideoSuspendTimeout() override
   {
     // Do nothing since we want a valid video frame to show when seek is done.
   }
 
+  void HandleResumeVideoDecoding() override
+  {
+    // We set mVideoDecodeSuspended to false in Enter().
+    MOZ_ASSERT(false, "Shouldn't have suspended video decoding.");
+  }
+
 private:
   void OnSeekTaskResolved(const SeekTaskResolveValue& aValue)
   {
     mSeekTaskRequest.Complete();
 
     if (aValue.mSeekedAudioData) {
       mMaster->Push(aValue.mSeekedAudioData, MediaData::AUDIO_DATA);
       mMaster->mDecodedAudioEndTime = std::max(
@@ -963,16 +999,21 @@ public:
     MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
     return nullptr;
   }
 
   void HandleVideoSuspendTimeout() override
   {
     MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
   }
+
+  void HandleResumeVideoDecoding() override
+  {
+    MOZ_DIAGNOSTIC_ASSERT(false, "Already shutting down.");
+  }
 };
 
 bool
 MediaDecoderStateMachine::
 StateObject::HandleDormant(bool aDormant)
 {
   if (!aDormant) {
     return true;
@@ -991,16 +1032,88 @@ StateObject::HandleDormant(bool aDormant
 
 RefPtr<ShutdownPromise>
 MediaDecoderStateMachine::
 StateObject::HandleShutdown()
 {
   return SetState<ShutdownState>();
 }
 
+static void
+ReportRecoveryTelemetry(const TimeStamp& aRecoveryStart,
+                        const MediaInfo& aMediaInfo,
+                        bool aIsHardwareAccelerated)
+{
+  MOZ_ASSERT(NS_IsMainThread());
+  if (!aMediaInfo.HasVideo()) {
+    return;
+  }
+
+  // Keyed by audio+video or video alone, hardware acceleration,
+  // and by a resolution range.
+  nsCString key(aMediaInfo.HasAudio() ? "AV" : "V");
+  key.AppendASCII(aIsHardwareAccelerated ? "(hw)," : ",");
+  static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
+    {  240, "0-240" },
+    {  480, "241-480" },
+    {  720, "481-720" },
+    { 1080, "721-1080" },
+    { 2160, "1081-2160" }
+  };
+  const char* resolution = "2161+";
+  int32_t height = aMediaInfo.mVideo.mImage.height;
+  for (const auto& res : sResolutions) {
+    if (height <= res.mH) {
+      resolution = res.mRes;
+      break;
+    }
+  }
+  key.AppendASCII(resolution);
+
+  TimeDuration duration = TimeStamp::Now() - aRecoveryStart;
+  double duration_ms = duration.ToMilliseconds();
+  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
+                        key,
+                        uint32_t(duration_ms + 0.5));
+  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
+                        NS_LITERAL_CSTRING("All"),
+                        uint32_t(duration_ms + 0.5));
+}
+
+void
+MediaDecoderStateMachine::
+StateObject::HandleResumeVideoDecoding()
+{
+  MOZ_ASSERT(mMaster->mVideoDecodeSuspended);
+
+  // Start counting recovery time from right now.
+  TimeStamp start = TimeStamp::Now();
+
+  // Local reference to mInfo, so that it will be copied in the lambda below.
+  auto& info = Info();
+  bool hw = Reader()->VideoIsHardwareAccelerated();
+
+  // Start video-only seek to the current time.
+  SeekJob seekJob;
+
+  const SeekTarget::Type type = mMaster->HasAudio()
+                                ? SeekTarget::Type::Accurate
+                                : SeekTarget::Type::PrevSyncPoint;
+
+  seekJob.mTarget = SeekTarget(mMaster->GetMediaTime(),
+                               type,
+                               MediaDecoderEventVisibility::Suppressed,
+                               true /* aVideoOnly */);
+
+  SetState<SeekingState>(Move(seekJob))->Then(
+    AbstractThread::MainThread(), __func__,
+    [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
+    [](){});
+}
+
 void
 MediaDecoderStateMachine::
 DecodeMetadataState::OnMetadataRead(MetadataHolder* aMetadata)
 {
   mMetadataRequest.Complete();
 
   // Set mode to PLAYBACK after reading metadata.
   Resource()->SetReadMode(MediaCacheStream::MODE_PLAYBACK);
@@ -1103,16 +1216,18 @@ DecodingFirstFrameState::Enter()
   }
 
   // Transition to DECODING if we've decoded first frames.
   if (mMaster->mSentFirstFrameLoadedEvent) {
     SetState<DecodingState>();
     return;
   }
 
+  MOZ_ASSERT(!mMaster->mVideoDecodeSuspended);
+
   // Dispatch tasks to decode first frames.
   mMaster->DispatchDecodeTasksIfNeeded();
 }
 
 RefPtr<MediaDecoder::SeekPromise>
 MediaDecoderStateMachine::
 DecodingFirstFrameState::HandleSeek(SeekTarget aTarget)
 {
@@ -2267,57 +2382,16 @@ void MediaDecoderStateMachine::PlayState
   {
     DECODER_LOG("Unexpected state - Bailing out of PlayInternal()");
     return;
   }
 
   ScheduleStateMachine();
 }
 
-static void
-ReportRecoveryTelemetry(const TimeStamp& aRecoveryStart,
-                        const MediaInfo& aMediaInfo,
-                        bool aIsHardwareAccelerated)
-{
-  MOZ_ASSERT(NS_IsMainThread());
-  if (!aMediaInfo.HasVideo()) {
-    return;
-  }
-
-  // Keyed by audio+video or video alone, hardware acceleration,
-  // and by a resolution range.
-  nsCString key(aMediaInfo.HasAudio() ? "AV" : "V");
-  key.AppendASCII(aIsHardwareAccelerated ? "(hw)," : ",");
-  static const struct { int32_t mH; const char* mRes; } sResolutions[] = {
-    {  240, "0-240" },
-    {  480, "241-480" },
-    {  720, "481-720" },
-    { 1080, "721-1080" },
-    { 2160, "1081-2160" }
-  };
-  const char* resolution = "2161+";
-  int32_t height = aMediaInfo.mVideo.mImage.height;
-  for (const auto& res : sResolutions) {
-    if (height <= res.mH) {
-      resolution = res.mRes;
-      break;
-    }
-  }
-  key.AppendASCII(resolution);
-
-  TimeDuration duration = TimeStamp::Now() - aRecoveryStart;
-  double duration_ms = duration.ToMilliseconds();
-  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
-                        key,
-                        uint32_t(duration_ms + 0.5));
-  Telemetry::Accumulate(Telemetry::VIDEO_SUSPEND_RECOVERY_TIME_MS,
-                        NS_LITERAL_CSTRING("All"),
-                        uint32_t(duration_ms + 0.5));
-}
-
 void MediaDecoderStateMachine::VisibilityChanged()
 {
   MOZ_ASSERT(OnTaskQueue());
   DECODER_LOG("VisibilityChanged: mIsVisible=%d, "
               "mVideoDecodeSuspended=%c, mIsReaderSuspended=%d",
               mIsVisible.Ref(), mVideoDecodeSuspended ? 'T' : 'F', mIsReaderSuspended.Ref());
 
   // Start timer to trigger suspended decoding state when going invisible.
@@ -2332,49 +2406,17 @@ void MediaDecoderStateMachine::Visibilit
   }
 
   // Resuming from suspended decoding
 
   // If suspend timer exists, destroy it.
   mVideoDecodeSuspendTimer.Reset();
 
   if (mVideoDecodeSuspended) {
-
-    if (mIsReaderSuspended) {
-      return;
-    }
-
-    // If an existing seek is in flight don't bother creating a new
-    // one to catch up.
-    if (mState == DECODER_STATE_SEEKING || mQueuedSeek.Exists()) {
-      return;
-    }
-
-    // Start counting recovery time from right now.
-    TimeStamp start = TimeStamp::Now();
-    // Local reference to mInfo, so that it will be copied in the lambda below.
-    auto& info = Info();
-    bool hw = mReader->VideoIsHardwareAccelerated();
-
-    // Start video-only seek to the current time.
-    SeekJob seekJob;
-
-    const SeekTarget::Type type = HasAudio()
-                                  ? SeekTarget::Type::Accurate
-                                  : SeekTarget::Type::PrevSyncPoint;
-
-    seekJob.mTarget = SeekTarget(GetMediaTime(),
-                                 type,
-                                 MediaDecoderEventVisibility::Suppressed,
-                                 true /* aVideoOnly */);
-
-    mStateObj->SetState<SeekingState>(Move(seekJob))->Then(
-      AbstractThread::MainThread(), __func__,
-      [start, info, hw](){ ReportRecoveryTelemetry(start, info, hw); },
-      [](){});
+    mStateObj->HandleResumeVideoDecoding();
   }
 }
 
 void MediaDecoderStateMachine::BufferedRangeUpdated()
 {
   MOZ_ASSERT(OnTaskQueue());
 
   // While playing an unseekable stream of unknown duration, mObservedDuration