Bug 1059052 - centralize state transitions of MediaDecoderStateMachine. r=cpearce
authorJW Wang <jwwang@mozilla.com>
Thu, 28 Aug 2014 02:46:00 +0200
changeset 202327 9ef1298abf22cd46e74dbb6723195da51fa663c6
parent 202326 a68f1bafdf74d8f2d920353b9e6310f340676c26
child 202328 0849d066f121d01720597584b511b8e9178909fb
push id27395
push usercbook@mozilla.com
push dateFri, 29 Aug 2014 12:55:06 +0000
treeherdermozilla-central@5f66dd3d63f2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs1059052
milestone34.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 1059052 - centralize state transitions of MediaDecoderStateMachine. r=cpearce
content/media/MediaDecoderStateMachine.cpp
content/media/MediaDecoderStateMachine.h
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -1044,17 +1044,17 @@ MediaDecoderStateMachine::CheckIfDecodeC
       mState == DECODER_STATE_COMPLETED) {
     // Don't change our state if we've already been shutdown, or we're seeking,
     // since we don't want to abort the shutdown or seek processes.
     return;
   }
   if (!IsVideoDecoding() && !IsAudioDecoding()) {
     // We've finished decoding all active streams,
     // so move to COMPLETED state.
-    mState = DECODER_STATE_COMPLETED;
+    SetState(DECODER_STATE_COMPLETED);
     DispatchDecodeTasksIfNeeded();
     ScheduleStateMachine();
   }
   DECODER_LOG("CheckIfDecodeComplete %scompleted",
               ((mState == DECODER_STATE_COMPLETED) ? "" : "NOT "));
 }
 
 bool MediaDecoderStateMachine::IsPlaying()
@@ -1215,16 +1215,40 @@ MediaDecoderOwner::NextFrameStatus Media
   if (IsBuffering() || IsSeeking()) {
     return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
   } else if (HaveNextFrameData()) {
     return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
   }
   return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
 }
 
+static const char* const gMachineStateStr[] = {
+  "NONE",
+  "DECODING_METADATA",
+  "WAIT_FOR_RESOURCES",
+  "DORMANT",
+  "DECODING",
+  "SEEKING",
+  "BUFFERING",
+  "COMPLETED",
+  "SHUTDOWN"
+};
+
+void MediaDecoderStateMachine::SetState(State aState)
+{
+  AssertCurrentThreadInMonitor();
+  if (mState == aState) {
+    return;
+  }
+  DECODER_LOG("Change machine state from %s to %s",
+              gMachineStateStr[mState], gMachineStateStr[aState]);
+
+  mState = aState;
+}
+
 void MediaDecoderStateMachine::SetVolume(double volume)
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   mVolume = volume;
   if (mAudioSink) {
     mAudioSink->SetVolume(mVolume);
   }
@@ -1322,54 +1346,54 @@ void MediaDecoderStateMachine::SetDorman
   if (!mReader) {
     return;
   }
 
   DECODER_LOG("SetDormant=%d", aDormant);
 
   if (aDormant) {
     ScheduleStateMachine();
-    mState = DECODER_STATE_DORMANT;
+    SetState(DECODER_STATE_DORMANT);
     mDecoder->GetReentrantMonitor().NotifyAll();
   } else if ((aDormant != true) && (mState == DECODER_STATE_DORMANT)) {
     ScheduleStateMachine();
     mStartTime = 0;
     mCurrentFrameTime = 0;
-    mState = DECODER_STATE_DECODING_NONE;
+    SetState(DECODER_STATE_DECODING_NONE);
     mDecoder->GetReentrantMonitor().NotifyAll();
   }
 }
 
 void MediaDecoderStateMachine::Shutdown()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
 
   // Once we've entered the shutdown state here there's no going back.
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
 
   // Change state before issuing shutdown request to threads so those
   // threads can start exiting cleanly during the Shutdown call.
   DECODER_LOG("Changed state to SHUTDOWN");
-  mState = DECODER_STATE_SHUTDOWN;
+  SetState(DECODER_STATE_SHUTDOWN);
   mScheduler->ScheduleAndShutdown();
   if (mAudioSink) {
     mAudioSink->PrepareToShutdown();
   }
   mDecoder->GetReentrantMonitor().NotifyAll();
 }
 
 void MediaDecoderStateMachine::StartDecoding()
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mState == DECODER_STATE_DECODING) {
     return;
   }
-  mState = DECODER_STATE_DECODING;
+  SetState(DECODER_STATE_DECODING);
 
   mDecodeStartTime = TimeStamp::Now();
 
   CheckIfDecodeComplete();
   if (mState == DECODER_STATE_COMPLETED) {
     return;
   }
 
@@ -1383,44 +1407,44 @@ void MediaDecoderStateMachine::StartDeco
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::StartWaitForResources()
 {
   NS_ASSERTION(OnStateMachineThread() || OnDecodeThread(),
                "Should be on state machine or decode thread.");
   AssertCurrentThreadInMonitor();
-  mState = DECODER_STATE_WAIT_FOR_RESOURCES;
+  SetState(DECODER_STATE_WAIT_FOR_RESOURCES);
   DECODER_LOG("StartWaitForResources");
 }
 
 void MediaDecoderStateMachine::NotifyWaitingForResourcesStatusChanged()
 {
   AssertCurrentThreadInMonitor();
   if (mState != DECODER_STATE_WAIT_FOR_RESOURCES ||
       mReader->IsWaitingMediaResources()) {
     return;
   }
   DECODER_LOG("NotifyWaitingForResourcesStatusChanged");
   // The reader is no longer waiting for resources (say a hardware decoder),
   // we can now proceed to decode metadata.
-  mState = DECODER_STATE_DECODING_NONE;
+  SetState(DECODER_STATE_DECODING_NONE);
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::Play()
 {
   NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
   // When asked to play, switch to decoding state only if
   // we are currently buffering. In other cases, we'll start playing anyway
   // when the state machine notices the decoder's state change to PLAYING.
   ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor());
   if (mState == DECODER_STATE_BUFFERING) {
     DECODER_LOG("Changed state from BUFFERING to DECODING");
-    mState = DECODER_STATE_DECODING;
+    SetState(DECODER_STATE_DECODING);
     mDecodeStartTime = TimeStamp::Now();
   }
   // Once we start playing, we don't want to minimize our prerolling, as we
   // assume the user is likely to want to keep playing in future.
   mMinimizePreroll = false;
   ScheduleStateMachine();
 }
 
@@ -1495,17 +1519,17 @@ void MediaDecoderStateMachine::Seek(cons
   int64_t seekTime = aTarget.mTime + mStartTime;
   seekTime = std::min(seekTime, mEndTime);
   seekTime = std::max(mStartTime, seekTime);
   NS_ASSERTION(seekTime >= mStartTime && seekTime <= mEndTime,
                "Can only seek in range [0,duration]");
   mSeekTarget = SeekTarget(seekTime, aTarget.mType);
 
   DECODER_LOG("Changed state to SEEKING (to %lld)", mSeekTarget.mTime);
-  mState = DECODER_STATE_SEEKING;
+  SetState(DECODER_STATE_SEEKING);
   if (mDecoder->GetDecodedStream()) {
     mDecoder->RecreateDecodedStream(seekTime - mStartTime);
   }
   ScheduleStateMachine();
 }
 
 void MediaDecoderStateMachine::StopAudioThread()
 {
@@ -1752,17 +1776,17 @@ MediaDecoderStateMachine::StartAudioThre
   mStopAudioThread = false;
   if (HasAudio() && !mAudioSink) {
     mAudioCompleted = false;
     mAudioSink = new AudioSink(this,
                                mAudioStartTime, mInfo.mAudio, mDecoder->GetAudioChannel());
     nsresult rv = mAudioSink->Init();
     if (NS_FAILED(rv)) {
       DECODER_WARN("Changed state to SHUTDOWN because audio sink initialization failed");
-      mState = DECODER_STATE_SHUTDOWN;
+      SetState(DECODER_STATE_SHUTDOWN);
       mScheduler->ScheduleAndShutdown();
       return rv;
     }
 
     mAudioSink->SetVolume(mVolume);
     mAudioSink->SetPlaybackRate(mPlaybackRate);
     mAudioSink->SetPreservesPitch(mPreservesPitch);
   }
@@ -1831,17 +1855,17 @@ MediaDecoderStateMachine::DecodeError()
     // Already shutdown.
     return;
   }
 
   // Change state to shutdown before sending error report to MediaDecoder
   // and the HTMLMediaElement, so that our pipeline can start exiting
   // cleanly during the sync dispatch below.
   DECODER_WARN("Decode error, changed state to SHUTDOWN due to error");
-  mState = DECODER_STATE_SHUTDOWN;
+  SetState(DECODER_STATE_SHUTDOWN);
   mScheduler->ScheduleAndShutdown();
   mDecoder->GetReentrantMonitor().NotifyAll();
 
   // Dispatch the event to call DecodeError synchronously. This ensures
   // we're in shutdown state by the time we exit the decode thread.
   // If we just moved to shutdown state here on the decode thread, we may
   // cause the state machine to shutdown/free memory without closing its
   // media stream properly, and we'll get callbacks from the media stream
@@ -2156,17 +2180,17 @@ MediaDecoderStateMachine::SeekCompleted(
   if (GetMediaTime() == mEndTime && !isLiveStream) {
     // Seeked to end of media, move to COMPLETED state. Note we don't do
     // this if we're playing a live stream, since the end of media will advance
     // once we download more data!
     DECODER_LOG("Changed state from SEEKING (to %lld) to COMPLETED", seekTime);
     stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStoppedAtEnd);
     // Explicitly set our state so we don't decode further, and so
     // we report playback ended to the media element.
-    mState = DECODER_STATE_COMPLETED;
+    SetState(DECODER_STATE_COMPLETED);
     DispatchDecodeTasksIfNeeded();
   } else {
     DECODER_LOG("Changed state from SEEKING (to %lld) to DECODING", seekTime);
     stopEvent = NS_NewRunnableMethod(mDecoder, &MediaDecoder::SeekingStopped);
     StartDecoding();
   }
 
   // Ensure timestamps are up to date.
@@ -2322,17 +2346,17 @@ nsresult MediaDecoderStateMachine::RunSt
       return NS_OK;
     }
 
     case DECODER_STATE_WAIT_FOR_RESOURCES: {
       return NS_OK;
     }
 
     case DECODER_STATE_DECODING_NONE: {
-      mState = DECODER_STATE_DECODING_METADATA;
+      SetState(DECODER_STATE_DECODING_METADATA);
       // Ensure we have a decode thread to decode metadata.
       return EnqueueDecodeMetadataTask();
     }
 
     case DECODER_STATE_DECODING_METADATA: {
       return NS_OK;
     }
 
@@ -2909,17 +2933,17 @@ void MediaDecoderStateMachine::StartBuff
   // eventually fires the "waiting" event. The problem is that
   // there might be pending main-thread events, such as "data
   // received" notifications, that mean we're not actually still
   // buffering by the time this runnable executes. So instead
   // we just trigger UpdateReadyStateForData; when it runs, it
   // will check the current state and decide whether to tell
   // the element we're buffering or not.
   UpdateReadyState();
-  mState = DECODER_STATE_BUFFERING;
+  SetState(DECODER_STATE_BUFFERING);
   DECODER_LOG("Changed state from DECODING to BUFFERING, decoded for %.3lfs",
               decodeDuration.ToSeconds());
 #ifdef PR_LOGGING
   MediaDecoder::Statistics stats = mDecoder->GetStatistics();
   DECODER_LOG("Playback rate: %.1lfKB/s%s download rate: %.1lfKB/s%s",
               stats.mPlaybackRate/1024, stats.mPlaybackRateReliable ? "" : " (unreliable)",
               stats.mDownloadRate/1024, stats.mDownloadRateReliable ? "" : " (unreliable)");
 #endif
--- a/content/media/MediaDecoderStateMachine.h
+++ b/content/media/MediaDecoderStateMachine.h
@@ -350,16 +350,18 @@ public:
   void OnVideoEOS();
   void OnDecodeError();
 
 protected:
   virtual ~MediaDecoderStateMachine();
 
   void AssertCurrentThreadInMonitor() const { mDecoder->GetReentrantMonitor().AssertCurrentThreadIn(); }
 
+  void SetState(State aState);
+
   // Inserts MediaData* samples into their respective MediaQueues.
   // aSample must not be null.
   void Push(AudioData* aSample);
   void Push(VideoData* aSample);
 
   class WakeDecoderRunnable : public nsRunnable {
   public:
     WakeDecoderRunnable(MediaDecoderStateMachine* aSM)