Bug 996465 - add ability to delay running state machine cycles for synchronization between decoding/main and state machine threads. r=cpearce.
☠☠ backed out by b22e5c7852b7 ☠ ☠
authorJW Wang <jwwang@mozilla.com>
Thu, 10 Jul 2014 03:22:00 +0200
changeset 215479 aa529bac2a92d02a8282691434bd65d81bc5707e
parent 215478 bbcfcf00f84e20c33d3680d61ead28c2a385c689
child 215480 19f686d29ae6b05c599372c7c9c055afe7491bbe
push id515
push userraliiev@mozilla.com
push dateMon, 06 Oct 2014 12:51:51 +0000
treeherdermozilla-release@267c7a481bef [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerscpearce
bugs996465
milestone33.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 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
@@ -89,18 +89,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() + UsecsToDuration(aUsecs);
   if (!mTimeout.IsNull() && timeout >= mTimeout) {
     // We've already scheduled a timer set to expire at or before this time,
     // or have an event dispatched to run the state machine.
@@ -159,16 +166,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
@@ -189,9 +199,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.