Bug 848954 - Part 3 - Separate interval time calculation and actual processing, and give an audio-callback compatible interface to the Process function. r=roc
authorPaul Adenot <paul@paul.cx>
Fri, 25 Apr 2014 18:04:23 +0200
changeset 223493 37cd9c64ce972071d50a15b34bc134f3fe88d770
parent 223492 78ad22d3b5f3de3028ffb6d633a84332b0d7d01b
child 223494 6d2711c7fc5ce797850d5bc8261ed53a11148885
push id3979
push userraliiev@mozilla.com
push dateMon, 13 Oct 2014 16:35:44 +0000
treeherdermozilla-beta@30f2cc610691 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs848954
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 848954 - Part 3 - Separate interval time calculation and actual processing, and give an audio-callback compatible interface to the Process function. r=roc
content/media/GraphDriver.cpp
content/media/GraphDriver.h
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraphImpl.h
--- a/content/media/GraphDriver.cpp
+++ b/content/media/GraphDriver.cpp
@@ -118,17 +118,31 @@ SystemClockDriver::RunThread()
   nsTArray<MessageBlock> messageQueue;
   {
     MonitorAutoLock lock(mMonitor);
     messageQueue.SwapElements(mGraphImpl->MessageQueue());
   }
   NS_ASSERTION(!messageQueue.IsEmpty(),
                "Shouldn't have started a graph with empty message queue!");
 
-  while(mGraphImpl->OneIteration(messageQueue));
+  bool stillProcessing = true;
+  while (stillProcessing) {
+    GraphTime prevCurrentTime, nextCurrentTime;
+    GetIntervalForIteration(prevCurrentTime, nextCurrentTime);
+
+    GraphTime nextStateComputedTime =
+      mGraphImpl->RoundUpToNextAudioBlock(
+          IterationEnd() + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS));
+
+    stillProcessing = mGraphImpl->OneIteration(prevCurrentTime,
+                                               nextCurrentTime,
+                                               StateComputedTime(),
+                                               nextStateComputedTime,
+                                               messageQueue);
+  }
 }
 
 void
 SystemClockDriver::GetIntervalForIteration(GraphTime& aFrom, GraphTime& aTo)
 {
   TimeStamp now = TimeStamp::Now();
   aFrom = mIterationStart = IterationEnd();
   aTo = mIterationEnd = mGraphImpl->SecondsToMediaTime((now - mCurrentTimeStamp).ToSeconds()) + IterationEnd();
@@ -160,22 +174,16 @@ SystemClockDriver::GetCurrentTime()
 
 TimeStamp
 SystemClockDriver::GetCurrentTimeStamp()
 {
   return mCurrentTimeStamp;
 }
 
 void
-SystemClockDriver::DoIteration(nsTArray<MessageBlock>& aMessageQueue)
-{
-  mGraphImpl->DoIteration(aMessageQueue);
-}
-
-void
 SystemClockDriver::WaitForNextIteration()
 {
   PRIntervalTime timeout = PR_INTERVAL_NO_TIMEOUT;
   TimeStamp now = TimeStamp::Now();
   if (mNeedAnotherIteration) {
     int64_t timeoutMS = MEDIA_GRAPH_TARGET_PERIOD_MS -
       int64_t((now - mCurrentTimeStamp).ToMilliseconds());
     // Make sure timeoutMS doesn't overflow 32 bits by waking up at
@@ -250,17 +258,33 @@ OfflineClockDriver::RunThread()
   nsTArray<MessageBlock> messageQueue;
   {
     MonitorAutoLock lock(mMonitor);
     messageQueue.SwapElements(mGraphImpl->MessageQueue());
   }
   NS_ASSERTION(!messageQueue.IsEmpty(),
                "Shouldn't have started a graph with empty message queue!");
 
-  while(mGraphImpl->OneIteration(messageQueue));
+  bool stillProcessing = true;
+
+  while(stillProcessing) {
+    GraphTime prevCurrentTime, nextCurrentTime;
+    GetIntervalForIteration(prevCurrentTime, nextCurrentTime);
+
+    GraphTime nextStateComputedTime =
+      mGraphImpl->RoundUpToNextAudioBlock(
+          IterationEnd() + mGraphImpl->MillisecondsToMediaTime(AUDIO_TARGET_MS));
+
+
+    stillProcessing = mGraphImpl->OneIteration(prevCurrentTime,
+                                               nextCurrentTime,
+                                               StateComputedTime(),
+                                               nextStateComputedTime,
+                                               messageQueue);
+  }
 }
 
 void
 OfflineClockDriver::GetIntervalForIteration(GraphTime& aFrom, GraphTime& aTo)
 {
   aFrom = mIterationStart = IterationEnd();
   aTo = mIterationEnd = IterationEnd() + mGraphImpl->MillisecondsToMediaTime(mSlice);
   PR_LOG(gMediaStreamGraphLog, PR_LOG_DEBUG+1, ("Updating offline current time to %f (%f)",
@@ -281,22 +305,16 @@ OfflineClockDriver::GetIntervalForIterat
 
 GraphTime
 OfflineClockDriver::GetCurrentTime()
 {
   return mIterationEnd;
 }
 
 void
-OfflineClockDriver::DoIteration(nsTArray<MessageBlock>& aMessageQueue)
-{
-  mGraphImpl->DoIteration(aMessageQueue);
-}
-
-void
 OfflineClockDriver::WaitForNextIteration()
 {
   // No op: we want to go as fast as possible when we are offline
 }
 
 void
 OfflineClockDriver::WakeUp()
 {
--- a/content/media/GraphDriver.h
+++ b/content/media/GraphDriver.h
@@ -45,19 +45,16 @@ public:
   virtual void RunThread() = 0;
   /* When the graph wakes up to do an iteration, this returns the range of time
    * that will be processed. */
   virtual void GetIntervalForIteration(GraphTime& aFrom,
                                        GraphTime& aTo) = 0;
   /* Returns the current time for this graph. This is the end of the current
    * iteration. */
   virtual GraphTime GetCurrentTime() = 0;
-  /* Do one iteration of the graph. aMessageQueue are the messages that will be
-   * processed at the beginning of the iteration. */
-  virtual void DoIteration(nsTArray<MessageBlock>& aMessageQueue) = 0;
   /* For real-time graphs, this waits until it's time to process more data. For
    * offline graphs, this is a no-op. */
   virtual void WaitForNextIteration() = 0;
   /* Wakes up the graph if it is waiting. */
   virtual void WakeUp() = 0;
   /* Start the graph, creating the thread. */
   virtual void Start() = 0;
   /* Stop the graph, shutting down the thread. */
@@ -108,16 +105,17 @@ public:
     mMonitor.AssertCurrentThreadOwns();
     mWaitState = WAITSTATE_WAKING_UP;
     mMonitor.Notify();
   }
 
   /**
    * Call this to indicate that another iteration of the control loop is
    * required on its regular schedule. The monitor must not be held.
+   * This function has to be idempotent.
    */
   void EnsureNextIteration() {
     MonitorAutoLock lock(mMonitor);
     EnsureNextIterationLocked();
   }
 
   /**
    * Same thing, but not locked.
@@ -207,17 +205,16 @@ public:
   virtual ~SystemClockDriver();
   virtual void Start() MOZ_OVERRIDE;
   virtual void Stop() MOZ_OVERRIDE;
   virtual void Dispatch(nsIRunnable* aEvent) MOZ_OVERRIDE;
   virtual void RunThread() MOZ_OVERRIDE;
   virtual void GetIntervalForIteration(GraphTime& aFrom,
                                        GraphTime& aTo) MOZ_OVERRIDE;
   virtual GraphTime GetCurrentTime() MOZ_OVERRIDE;
-  virtual void DoIteration(nsTArray<MessageBlock>& aMessageQueue) MOZ_OVERRIDE;
   virtual void WaitForNextIteration() MOZ_OVERRIDE;
   virtual void WakeUp() MOZ_OVERRIDE;
 
   virtual TimeStamp GetCurrentTimeStamp() MOZ_OVERRIDE;
 
 private:
   TimeStamp mInitialTimeStamp;
   TimeStamp mLastTimeStamp;
@@ -236,17 +233,16 @@ public:
   virtual ~OfflineClockDriver();
   virtual void Start() MOZ_OVERRIDE;
   virtual void Stop() MOZ_OVERRIDE;
   virtual void Dispatch(nsIRunnable* aEvent) MOZ_OVERRIDE;
   virtual void RunThread() MOZ_OVERRIDE;
   virtual void GetIntervalForIteration(GraphTime& aFrom,
                                        GraphTime& aTo) MOZ_OVERRIDE;
   virtual GraphTime GetCurrentTime() MOZ_OVERRIDE;
-  virtual void DoIteration(nsTArray<MessageBlock>& aMessageQueue) MOZ_OVERRIDE;
   virtual void WaitForNextIteration() MOZ_OVERRIDE;
   virtual void WakeUp() MOZ_OVERRIDE;
 
 private:
   // Time, in GraphTime, for each iteration
   GraphTime mSlice;
   nsCOMPtr<nsIThread> mThread;
 };
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -72,17 +72,17 @@ void
 MediaStreamGraphImpl::FinishStream(MediaStream* aStream)
 {
   if (aStream->mFinished)
     return;
   STREAM_LOG(PR_LOG_DEBUG, ("MediaStream %p will finish", aStream));
   aStream->mFinished = true;
   aStream->mBuffer.AdvanceKnownTracksTime(STREAM_TIME_MAX);
   // Force at least one more iteration of the control loop, since we rely
-  // on UpdateCurrentTime to notify our listeners once the stream end
+  // on UpdateCurrentTimeForStreams to notify our listeners once the stream end
   // has been reached.
   CurrentDriver()->EnsureNextIteration();
 
   SetStreamOrderDirty();
 }
 
 void
 MediaStreamGraphImpl::AddStream(MediaStream* aStream)
@@ -347,71 +347,66 @@ MediaStreamGraphImpl::GetAudioPosition(M
 
 GraphTime
 MediaStreamGraphImpl::IterationEnd()
 {
   return CurrentDriver()->IterationEnd();
 }
 
 void
-MediaStreamGraphImpl::UpdateCurrentTime()
+MediaStreamGraphImpl::UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime, GraphTime aNextCurrentTime)
 {
-  GraphTime prevCurrentTime, nextCurrentTime;
-
-  CurrentDriver()->GetIntervalForIteration(prevCurrentTime, nextCurrentTime);
-
   nsTArray<MediaStream*> streamsReadyToFinish;
   nsAutoTArray<bool,800> streamHasOutput;
   streamHasOutput.SetLength(mStreams.Length());
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
 
     // Calculate blocked time and fire Blocked/Unblocked events
     GraphTime blockedTime = 0;
-    GraphTime t = prevCurrentTime;
+    GraphTime t = aPrevCurrentTime;
     // include |nextCurrentTime| to ensure NotifyBlockingChanged() is called
     // before NotifyEvent(this, EVENT_FINISHED) when |nextCurrentTime == stream end time|
-    while (t <= nextCurrentTime) {
+    while (t <= aNextCurrentTime) {
       GraphTime end;
       bool blocked = stream->mBlocked.GetAt(t, &end);
       if (blocked) {
-        blockedTime += std::min(end, nextCurrentTime) - t;
+        blockedTime += std::min(end, aNextCurrentTime) - t;
       }
       if (blocked != stream->mNotifiedBlocked) {
         for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
           MediaStreamListener* l = stream->mListeners[j];
           l->NotifyBlockingChanged(this,
               blocked ? MediaStreamListener::BLOCKED : MediaStreamListener::UNBLOCKED);
         }
         stream->mNotifiedBlocked = blocked;
       }
       t = end;
     }
 
 
-    stream->AdvanceTimeVaryingValuesToCurrentTime(nextCurrentTime, blockedTime);
+    stream->AdvanceTimeVaryingValuesToCurrentTime(aNextCurrentTime, blockedTime);
     // Advance mBlocked last so that implementations of
     // AdvanceTimeVaryingValuesToCurrentTime can rely on the value of mBlocked.
-    stream->mBlocked.AdvanceCurrentTime(nextCurrentTime);
+    stream->mBlocked.AdvanceCurrentTime(aNextCurrentTime);
 
-    streamHasOutput[i] = blockedTime < nextCurrentTime - prevCurrentTime;
+    streamHasOutput[i] = blockedTime < aNextCurrentTime - aPrevCurrentTime;
     // Make this an assertion when bug 957832 is fixed.
     NS_WARN_IF_FALSE(!streamHasOutput[i] || !stream->mNotifiedFinished,
       "Shouldn't have already notified of finish *and* have output!");
 
     if (stream->mFinished && !stream->mNotifiedFinished) {
       streamsReadyToFinish.AppendElement(stream);
     }
     STREAM_LOG(PR_LOG_DEBUG+1, ("MediaStream %p bufferStartTime=%f blockedTime=%f",
                                 stream, MediaTimeToSeconds(stream->mBufferStartTime),
                                 MediaTimeToSeconds(blockedTime)));
   }
 
 
-  // Do these after setting mCurrentTime so that StreamTimeToGraphTime works properly.
   for (uint32_t i = 0; i < streamHasOutput.Length(); ++i) {
     if (!streamHasOutput[i]) {
       continue;
     }
     MediaStream* stream = mStreams[i];
     for (uint32_t j = 0; j < stream->mListeners.Length(); ++j) {
       MediaStreamListener* l = stream->mListeners[j];
       l->NotifyOutput(this, IterationEnd());
@@ -980,21 +975,21 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
         audioOutput.mLastTickWritten != offset) {
       // If there is a global underrun of the MSG, this property won't hold, and
       // we reset the sample count tracking.
       if (offset - audioOutput.mLastTickWritten == 1) {
         offset = audioOutput.mLastTickWritten;
       }
     }
 
-    // We don't update aStream->mBufferStartTime here to account for
-    // time spent blocked. Instead, we'll update it in UpdateCurrentTime after the
-    // blocked period has completed. But we do need to make sure we play from the
-    // right offsets in the stream buffer, even if we've already written silence for
-    // some amount of blocked time after the current time.
+    // We don't update aStream->mBufferStartTime here to account for time spent
+    // blocked. Instead, we'll update it in UpdateCurrentTimeForStreams after
+    // the blocked period has completed. But we do need to make sure we play
+    // from the right offsets in the stream buffer, even if we've already
+    // written silence for some amount of blocked time after the current time.
     GraphTime t = aFrom;
     while (ticksNeeded) {
       GraphTime end;
       bool blocked = aStream->mBlocked.GetAt(t, &end);
       end = std::min(end, aTo);
 
       // Check how many ticks of sound we can provide if we are blocked some
       // time in the middle of this cycle.
@@ -1177,18 +1172,18 @@ MediaStreamGraphImpl::PrepareUpdatesToMa
 }
 
 /**
  * Returns smallest value of t such that
  * TimeToTicksRoundUp(aSampleRate, t) is a multiple of WEBAUDIO_BLOCK_SIZE
  * and floor(TimeToTicksRoundUp(aSampleRate, t)/WEBAUDIO_BLOCK_SIZE) >
  * floor(TimeToTicksRoundUp(aSampleRate, aTime)/WEBAUDIO_BLOCK_SIZE).
  */
-static GraphTime
-RoundUpToNextAudioBlock(TrackRate aSampleRate, GraphTime aTime)
+GraphTime
+MediaStreamGraphImpl::RoundUpToNextAudioBlock(GraphTime aTime)
 {
   TrackTicks ticks = aTime;
   uint64_t block = ticks >> WEBAUDIO_BLOCK_SIZE_BITS;
   uint64_t nextBlock = block + 1;
   TrackTicks nextTicks = nextBlock << WEBAUDIO_BLOCK_SIZE_BITS;
   return nextTicks;
 }
 
@@ -1197,17 +1192,17 @@ MediaStreamGraphImpl::ProduceDataForStre
                                                         TrackRate aSampleRate,
                                                         GraphTime aFrom,
                                                         GraphTime aTo)
 {
   MOZ_ASSERT(aStreamIndex <= mFirstCycleBreaker,
              "Cycle breaker is not AudioNodeStream?");
   GraphTime t = aFrom;
   while (t < aTo) {
-    GraphTime next = RoundUpToNextAudioBlock(aSampleRate, t);
+    GraphTime next = RoundUpToNextAudioBlock(t);
     for (uint32_t i = mFirstCycleBreaker; i < mStreams.Length(); ++i) {
       auto ns = static_cast<AudioNodeStream*>(mStreams[i]);
       MOZ_ASSERT(ns->AsAudioNodeStream());
       ns->ProduceOutputBeforeInput(t);
     }
     for (uint32_t i = aStreamIndex; i < mStreams.Length(); ++i) {
       ProcessedMediaStream* ps = mStreams[i]->AsProcessedStream();
       if (ps) {
@@ -1269,17 +1264,18 @@ MediaStreamGraphImpl::ResumeAllAudioOutp
       s->mAudioOutputStreams[j].mStream->Resume();
     }
   }
 
   mAudioOutputsPaused = false;
 }
 
 void
-MediaStreamGraphImpl::DoIteration(nsTArray<MessageBlock>& aMessageQueue)
+MediaStreamGraphImpl::UpdateGraph(nsTArray<MessageBlock>& aMessageQueue,
+                                  GraphTime aEndBlockingDecision)
 {
   // Calculate independent action times for each batch of messages (each
   // batch corresponding to an event loop task). This isolates the performance
   // of different scripts to some extent.
   for (uint32_t i = 0; i < aMessageQueue.Length(); ++i) {
     mProcessingGraphUpdateIndex = aMessageQueue[i].mGraphUpdateIndex;
     nsTArray<nsAutoPtr<ControlMessage> >& messages = aMessageQueue[i].mMessages;
 
@@ -1288,43 +1284,45 @@ MediaStreamGraphImpl::DoIteration(nsTArr
     }
   }
   aMessageQueue.Clear();
 
   if (mStreamOrderDirty) {
     UpdateStreamOrder();
   }
 
-  GraphTime endBlockingDecisions =
-    RoundUpToNextAudioBlock(mSampleRate, IterationEnd() + MillisecondsToMediaTime(AUDIO_TARGET_MS));
   bool ensureNextIteration = false;
 
   // Grab pending stream input.
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     SourceMediaStream* is = mStreams[i]->AsSourceStream();
     if (is) {
       UpdateConsumptionState(is);
-      ExtractPendingInput(is, endBlockingDecisions, &ensureNextIteration);
+      ExtractPendingInput(is, aEndBlockingDecision, &ensureNextIteration);
     }
   }
 
   // The loop is woken up so soon that IterationEnd() barely advances and we
-  // end up having endBlockingDecisions == CurrentDriver()->StateComputedTime().
+  // end up having aEndBlockingDecision == CurrentDriver()->StateComputedTime().
   // Since stream blocking is computed in the interval of
-  // [CurrentDriver()->StateComputedTime(), endBlockingDecisions), it won't be computed at all.
+  // [CurrentDriver()->StateComputedTime(), aEndBlockingDecision), it won't be computed at all.
   // We should ensure next iteration so that pending blocking changes will be
   // computed in next loop.
-  if (endBlockingDecisions == CurrentDriver()->StateComputedTime()) {
-    ensureNextIteration = true;
+  if (ensureNextIteration ||
+      aEndBlockingDecision == CurrentDriver()->StateComputedTime()) {
+    CurrentDriver()->EnsureNextIteration();
   }
 
   // Figure out which streams are blocked and when.
-  GraphTime prevComputedTime = CurrentDriver()->StateComputedTime();
-  RecomputeBlocking(endBlockingDecisions);
+  RecomputeBlocking(aEndBlockingDecision);
+}
 
+void
+MediaStreamGraphImpl::Process(GraphTime aFrom, GraphTime aTo)
+{
   // Play stream contents.
   bool allBlockedForever = true;
   // True when we've done ProcessInput for all processed streams.
   bool doneAllProducing = false;
   // This is the number of frame that are written to the AudioStreams, for
   // this cycle.
   TrackTicks ticksPlayed = 0;
   // Figure out what each stream wants to do
@@ -1342,68 +1340,68 @@ MediaStreamGraphImpl::DoIteration(nsTArr
             if (nextStream) {
               MOZ_ASSERT(n->SampleRate() == nextStream->SampleRate(),
                          "All AudioNodeStreams in the graph must have the same sampling rate");
             }
           }
 #endif
           // Since an AudioNodeStream is present, go ahead and
           // produce audio block by block for all the rest of the streams.
-          ProduceDataForStreamsBlockByBlock(i, n->SampleRate(), prevComputedTime, CurrentDriver()->StateComputedTime());
-          TimeToTicksRoundDown(n->SampleRate(), CurrentDriver()->StateComputedTime() - prevComputedTime);
+          ProduceDataForStreamsBlockByBlock(i, n->SampleRate(), aFrom, aTo);
           doneAllProducing = true;
         } else {
-          ps->ProcessInput(prevComputedTime, CurrentDriver()->StateComputedTime(),
-                           ProcessedMediaStream::ALLOW_FINISH);
+          ps->ProcessInput(aFrom, aTo, ProcessedMediaStream::ALLOW_FINISH);
           NS_WARN_IF_FALSE(stream->mBuffer.GetEnd() >=
-                           GraphTimeToStreamTime(stream, CurrentDriver()->StateComputedTime()),
+                           GraphTimeToStreamTime(stream, aTo),
                            "Stream did not produce enough data");
         }
       }
     }
     NotifyHasCurrentData(stream);
     if (mRealtime) {
       // Only playback audio and video in real-time mode
-      CreateOrDestroyAudioStreams(prevComputedTime, stream);
-      TrackTicks ticksPlayedForThisStream = PlayAudio(stream, prevComputedTime, CurrentDriver()->StateComputedTime());
+      CreateOrDestroyAudioStreams(aFrom, stream);
+      TrackTicks ticksPlayedForThisStream = PlayAudio(stream, aFrom, aTo);
       if (!ticksPlayed) {
         ticksPlayed = ticksPlayedForThisStream;
       } else {
         MOZ_ASSERT(!ticksPlayedForThisStream || ticksPlayedForThisStream == ticksPlayed,
             "Each stream should have the same number of frame.");
       }
       PlayVideo(stream);
     }
     SourceMediaStream* is = stream->AsSourceStream();
     if (is) {
       UpdateBufferSufficiencyState(is);
     }
     GraphTime end;
-    if (!stream->mBlocked.GetAt(IterationEnd(), &end) || end < GRAPH_TIME_MAX) {
+    if (!stream->mBlocked.GetAt(aTo, &end) || end < GRAPH_TIME_MAX) {
       allBlockedForever = false;
     }
   }
 
   if (mMixer) {
     mMixer->FinishMixing();
   }
 
-  if (ensureNextIteration || !allBlockedForever) {
+  if (!allBlockedForever) {
     CurrentDriver()->EnsureNextIteration();
   }
 }
 
 bool
-MediaStreamGraphImpl::OneIteration(nsTArray<MessageBlock>& aMessageQueue)
+MediaStreamGraphImpl::OneIteration(GraphTime aFrom, GraphTime aTo,
+                                   GraphTime aStateFrom, GraphTime aStateEnd,
+                                   nsTArray<MessageBlock>& aMessageQueue)
 {
-  // Update IterationEnd() to the min of the playing audio times, or using the
-  // wall-clock time change if no audio is playing.
-  UpdateCurrentTime();
+  UpdateCurrentTimeForStreams(aFrom, aTo);
 
-  CurrentDriver()->DoIteration(aMessageQueue);
+  UpdateGraph(aMessageQueue, aStateEnd);
+
+  Process(aStateFrom, aStateEnd);
 
   // Send updates to the main thread and wait for the next control loop
   // iteration.
   {
     MonitorAutoLock lock(CurrentDriver()->GetThreadMonitor());
     bool finalUpdate = mForceShutDown ||
       (IterationEnd() >= mEndTime && AllFinishedStreamsNotified()) ||
       (IsEmpty() && mMessageQueue.IsEmpty());
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -172,31 +172,30 @@ public:
   void ShutdownThreads();
 
   /**
    * Called before the thread runs.
    */
   void Init();
   // The following methods run on the graph thread (or possibly the main thread if
   // mLifecycleState > LIFECYCLE_RUNNING)
-  /**
-   * Do one full iteration of the graph. Returns false if the graph should
-   * stop, true otherwise.
-   */
-  bool OneIteration(nsTArray<MessageBlock>& aMessageQueue);
   /*
    * This does the actual iteration: Message processing, MediaStream ordering,
    * blocking computation and processing.
    */
   nsTArray<MessageBlock>& MessageQueue() {
     CurrentDriver()->GetThreadMonitor().AssertCurrentThreadOwns();
     return mMessageQueue;
   }
   void DoIteration(nsTArray<MessageBlock>& aMessageQueue);
 
+  bool OneIteration(GraphTime aFrom, GraphTime aTo,
+                    GraphTime aStateFrom, GraphTime aStateEnd,
+                    nsTArray<MessageBlock>& aMessageQueue);
+
   /* This is the end of the current iteration, that is, the current time of the
    * graph. */
   GraphTime IterationEnd();
   /**
    * Ensure there is an event posted to the main thread to run RunInStableState.
    * mMonitor must be held.
    * See EnsureRunInStableState
    */
@@ -214,20 +213,30 @@ public:
   /**
    * If we are rendering in non-realtime mode, we don't want to send messages to
    * the main thread at each iteration for performance reasons. We instead
    * notify the main thread at the same rate
    */
   bool ShouldUpdateMainThread();
   // The following methods are the various stages of RunThread processing.
   /**
-   * Compute a new current time for the graph and advance all on-graph-thread
-   * state to the new current time.
+   * Advance all stream state to the new current time.
    */
-  void UpdateCurrentTime();
+  void UpdateCurrentTimeForStreams(GraphTime aPrevCurrentTime,
+                                   GraphTime aNextCurrentTime);
+  /**
+   * Process graph message for this iteration, update stream processing order,
+   * and recompute stream blocking until aEndBlockingDecisions.
+   */
+  void UpdateGraph(nsTArray<MessageBlock>& aMessageQueue,
+                   GraphTime aEndBlockingDecisions);
+  /**
+   * Do all the processing and play the audio and video, ffrom aFrom to aTo.
+   */
+  void Process(GraphTime aFrom, GraphTime aTo);
   /**
    * Update the consumption state of aStream to reflect whether its data
    * is needed or not.
    */
   void UpdateConsumptionState(SourceMediaStream* aStream);
   /**
    * Extract any state updates pending in aStream, and apply them.
    */
@@ -274,16 +283,23 @@ public:
    * If this results in decisions that need to be revisited at some point
    * in the future, *aEnd will be reduced to the first time in the future to
    * recompute those decisions.
    */
   void RecomputeBlockingAt(const nsTArray<MediaStream*>& aStreams,
                            GraphTime aTime, GraphTime aEndBlockingDecisions,
                            GraphTime* aEnd);
   /**
+   * Returns smallest value of t such that
+   * TimeToTicksRoundUp(aSampleRate, t) is a multiple of WEBAUDIO_BLOCK_SIZE
+   * and floor(TimeToTicksRoundUp(aSampleRate, t)/WEBAUDIO_BLOCK_SIZE) >
+   * floor(TimeToTicksRoundUp(aSampleRate, aTime)/WEBAUDIO_BLOCK_SIZE).
+   */
+  GraphTime RoundUpToNextAudioBlock(GraphTime aTime);
+  /**
    * Produce data for all streams >= aStreamIndex for the given time interval.
    * Advances block by block, each iteration producing data for all streams
    * for a single block.
    * This is called whenever we have an AudioNodeStream in the graph.
    */
   void ProduceDataForStreamsBlockByBlock(uint32_t aStreamIndex,
                                          TrackRate aSampleRate,
                                          GraphTime aFrom,
@@ -291,16 +307,17 @@ public:
   /**
    * Returns true if aStream will underrun at aTime for its own playback.
    * aEndBlockingDecisions is when we plan to stop making blocking decisions.
    * *aEnd will be reduced to the first time in the future to recompute these
    * decisions.
    */
   bool WillUnderrun(MediaStream* aStream, GraphTime aTime,
                     GraphTime aEndBlockingDecisions, GraphTime* aEnd);
+
   /**
    * Given a graph time aTime, convert it to a stream time taking into
    * account the time during which aStream is scheduled to be blocked.
    */
   StreamTime GraphTimeToStreamTime(MediaStream* aStream, GraphTime aTime);
   /**
    * Given a graph time aTime, convert it to a stream time taking into
    * account the time during which aStream is scheduled to be blocked, and