Bug 996465 - Add ability to delay running state machine cycles for synchronization between decoding/main and state machine threads. r=cpearce
authorJW Wang <jwwang@mozilla.com>
Fri, 11 Jul 2014 03:11:00 -0400
changeset 193587 6331bc47ce774e69017c16b7017b038cdbf9b5c6
parent 193586 e6843f1ad69745cc5707e465470d9f86e96b4eda
child 193588 8c9cdf2b5ab4b1747afb4febedb50edf1f22578e
push idunknown
push userunknown
push dateunknown
reviewerscpearce
bugs996465
milestone33.0a1
Bug 996465 - Add ability to delay running state machine cycles for synchronization between decoding/main and state machine threads. r=cpearce
content/media/MediaDecoderStateMachine.cpp
content/media/MediaDecoderStateMachineScheduler.cpp
content/media/MediaDecoderStateMachineScheduler.h
--- a/content/media/MediaDecoderStateMachine.cpp
+++ b/content/media/MediaDecoderStateMachine.cpp
@@ -2157,27 +2157,31 @@ MediaDecoderStateMachine::SeekCompleted(
   UpdatePlaybackPositionInternal(newCurrentTime);
   if (mDecoder->GetDecodedStream()) {
     SetSyncPointForMediaStream();
   }
 
   // Try to decode another frame to detect if we're at the end...
   DECODER_LOG(PR_LOG_DEBUG, "Seek completed, mCurrentFrameTime=%lld", mCurrentFrameTime);
 
+  // Prevent changes in playback position before 'seeked' is fired for we
+  // expect currentTime equals seek target in 'seeked' callback.
+  mScheduler->FreezeScheduling();
   {
     ReentrantMonitorAutoExit exitMon(mDecoder->GetReentrantMonitor());
     NS_DispatchToMainThread(stopEvent, NS_DISPATCH_SYNC);
   }
 
   // Reset quick buffering status. This ensures that if we began the
   // seek while quick-buffering, we won't bypass quick buffering mode
   // if we need to buffer after the seek.
   mQuickBuffering = false;
 
   ScheduleStateMachine();
+  mScheduler->ThawScheduling();
 }
 
 // Runnable to dispose of the decoder and state machine on the main thread.
 class nsDecoderDisposeEvent : public nsRunnable {
 public:
   nsDecoderDisposeEvent(already_AddRefed<MediaDecoder> aDecoder,
                         already_AddRefed<MediaDecoderStateMachine> aStateMachine)
     : mDecoder(aDecoder), mStateMachine(aStateMachine) {}
--- a/content/media/MediaDecoderStateMachineScheduler.cpp
+++ b/content/media/MediaDecoderStateMachineScheduler.cpp
@@ -84,18 +84,25 @@ MediaDecoderStateMachineScheduler::Init(
   return NS_OK;
 }
 
 nsresult
 MediaDecoderStateMachineScheduler::Schedule(int64_t aUsecs)
 {
   mMonitor.AssertCurrentThreadIn();
 
-  if (mState == SCHEDULER_STATE_SHUTDOWN) {
+  switch(mState) {
+  case SCHEDULER_STATE_SHUTDOWN:
     return NS_ERROR_FAILURE;
+  case SCHEDULER_STATE_FROZEN:
+    mState = SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
+  case SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK:
+    return NS_OK;
+  case SCHEDULER_STATE_NONE:
+    break;
   }
 
   aUsecs = std::max<int64_t>(aUsecs, 0);
 
   TimeStamp timeout = TimeStamp::Now() +
     TimeDuration::FromMilliseconds(static_cast<double>(aUsecs) / USECS_PER_MS);
 
   if (!mTimeout.IsNull() && timeout >= mTimeout) {
@@ -156,16 +163,19 @@ MediaDecoderStateMachineScheduler::Timeo
 
   return rv;
 }
 
 void
 MediaDecoderStateMachineScheduler::ScheduleAndShutdown()
 {
   mMonitor.AssertCurrentThreadIn();
+  if (IsFrozen()) {
+    ThawScheduling();
+  }
   // Schedule next cycle to handle SHUTDOWN in state machine thread.
   Schedule();
   // This must be set after calling Schedule()
   // which does nothing in shutdown state.
   mState = SCHEDULER_STATE_SHUTDOWN;
 }
 
 bool
@@ -186,9 +196,38 @@ MediaDecoderStateMachineScheduler::IsSch
 void
 MediaDecoderStateMachineScheduler::ResetTimer()
 {
   mMonitor.AssertCurrentThreadIn();
   mTimer->Cancel();
   mTimeout = TimeStamp();
 }
 
+void MediaDecoderStateMachineScheduler::FreezeScheduling()
+{
+  mMonitor.AssertCurrentThreadIn();
+  if (mState == SCHEDULER_STATE_SHUTDOWN) {
+    return;
+  }
+  MOZ_ASSERT(mState == SCHEDULER_STATE_NONE);
+  mState = !IsScheduled() ? SCHEDULER_STATE_FROZEN :
+                            SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
+  // Nullify pending timer task if any.
+  ++mTimerId;
+  mTimeout = TimeStamp();
+}
+
+void MediaDecoderStateMachineScheduler::ThawScheduling()
+{
+  mMonitor.AssertCurrentThreadIn();
+  if (mState == SCHEDULER_STATE_SHUTDOWN) {
+    return;
+  }
+  // We should be in frozen state and no pending timer task.
+  MOZ_ASSERT(IsFrozen() && !IsScheduled());
+  bool pendingTask = mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
+  mState = SCHEDULER_STATE_NONE;
+  if (pendingTask) {
+    Schedule();
+  }
+}
+
 } // namespace mozilla
--- a/content/media/MediaDecoderStateMachineScheduler.h
+++ b/content/media/MediaDecoderStateMachineScheduler.h
@@ -16,39 +16,48 @@ class nsIEventTarget;
 
 namespace mozilla {
 
 class ReentrantMonitor;
 
 class MediaDecoderStateMachineScheduler {
   enum State {
     SCHEDULER_STATE_NONE,
+    SCHEDULER_STATE_FROZEN,
+    SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK,
     SCHEDULER_STATE_SHUTDOWN
   };
 public:
   MediaDecoderStateMachineScheduler(ReentrantMonitor& aMonitor,
                                     nsresult (*aTimeoutCallback)(void*),
                                     void* aClosure, bool aRealTime);
   ~MediaDecoderStateMachineScheduler();
   nsresult Init();
   nsresult Schedule(int64_t aUsecs = 0);
   void ScheduleAndShutdown();
   nsresult TimeoutExpired(int aTimerId);
+  void FreezeScheduling();
+  void ThawScheduling();
 
   bool OnStateMachineThread() const;
   bool IsScheduled() const;
 
   bool IsRealTime() const {
     return mRealTime;
   }
 
   nsIEventTarget* GetStateMachineThread() const {
     return mEventTarget;
   }
 
+  bool IsFrozen() const {
+    return mState == SCHEDULER_STATE_FROZEN ||
+           mState == SCHEDULER_STATE_FROZEN_WITH_PENDING_TASK;
+  }
+
 private:
   void ResetTimer();
 
   // Callback function provided by MediaDecoderStateMachine to run
   // state machine cycles.
   nsresult (*const mTimeoutCallback)(void*);
   // Since StateMachineScheduler will never outlive the state machine,
   // it is safe to keep a raw pointer only to avoid reference cycles.