Bug 836599 - Part 7: Implement a non-realtime API in MediaStreamGraph; r=roc
authorEhsan Akhgari <ehsan@mozilla.com>
Wed, 08 May 2013 07:44:07 -0400
changeset 131238 c97a67b4ec08925d4aa96215f9a96c2efc8f52e0
parent 131237 5971dba36391e2fbfcea5555ad2b22f19bcfe7f6
child 131239 3710924cf7ea15bdf97b97abb549c312a6df526f
push id27746
push usereakhgari@mozilla.com
push dateWed, 08 May 2013 11:44:14 +0000
treeherdermozilla-inbound@c97a67b4ec08 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersroc
bugs836599
milestone23.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 836599 - Part 7: Implement a non-realtime API in MediaStreamGraph; r=roc
content/media/MediaStreamGraph.cpp
content/media/MediaStreamGraph.h
content/media/MediaStreamGraphImpl.h
--- a/content/media/MediaStreamGraph.cpp
+++ b/content/media/MediaStreamGraph.cpp
@@ -671,16 +671,18 @@ MediaStreamGraphImpl::NotifyHasCurrentDa
     aStream->mNotifiedHasCurrentData = true;
   }
 }
 
 void
 MediaStreamGraphImpl::CreateOrDestroyAudioStreams(GraphTime aAudioOutputStartTime,
                                                   MediaStream* aStream)
 {
+  MOZ_ASSERT(mRealtime, "Should only attempt to create audio streams in real-time mode");
+
   nsAutoTArray<bool,2> audioOutputStreamsFound;
   for (uint32_t i = 0; i < aStream->mAudioOutputStreams.Length(); ++i) {
     audioOutputStreamsFound.AppendElement(false);
   }
 
   if (!aStream->mAudioOutputs.IsEmpty()) {
     for (StreamBuffer::TrackIter tracks(aStream->GetStreamBuffer(), MediaSegment::AUDIO);
          !tracks.IsEnded(); tracks.Next()) {
@@ -727,16 +729,18 @@ MediaStreamGraphImpl::CreateOrDestroyAud
     }
   }
 }
 
 void
 MediaStreamGraphImpl::PlayAudio(MediaStream* aStream,
                                 GraphTime aFrom, GraphTime aTo)
 {
+  MOZ_ASSERT(mRealtime, "Should only attempt to play audio in realtime mode");
+
   if (aStream->mAudioOutputStreams.IsEmpty()) {
     return;
   }
 
   // When we're playing multiple copies of this stream at the same time, they're
   // perfectly correlated so adding volumes is the right thing to do.
   float volume = 0.0f;
   for (uint32_t i = 0; i < aStream->mAudioOutputs.Length(); ++i) {
@@ -800,16 +804,18 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
       t = end;
     }
   }
 }
 
 void
 MediaStreamGraphImpl::PlayVideo(MediaStream* aStream)
 {
+  MOZ_ASSERT(mRealtime, "Should only attempt to play video in realtime mode");
+
   if (aStream->mVideoOutputs.IsEmpty())
     return;
 
   // Display the next frame a bit early. This is better than letting the current
   // frame be displayed for too long.
   GraphTime framePosition = mCurrentTime + MEDIA_GRAPH_TARGET_PERIOD_MS;
   NS_ASSERTION(framePosition >= aStream->mBufferStartTime, "frame position before buffer?");
   StreamTime frameBufferTime = GraphTimeToStreamTime(aStream, framePosition);
@@ -1005,71 +1011,76 @@ MediaStreamGraphImpl::RunThread()
             ps->ProduceOutput(prevComputedTime, mStateComputedTime);
             NS_ASSERTION(stream->mBuffer.GetEnd() >=
                          GraphTimeToStreamTime(stream, mStateComputedTime),
                        "Stream did not produce enough data");
           }
         }
       }
       NotifyHasCurrentData(stream);
-      CreateOrDestroyAudioStreams(prevComputedTime, stream);
-      PlayAudio(stream, prevComputedTime, mStateComputedTime);
-      audioStreamsActive += stream->mAudioOutputStreams.Length();
-      PlayVideo(stream);
+      if (mRealtime) {
+        // Only playback audio and video in real-time mode
+        CreateOrDestroyAudioStreams(prevComputedTime, stream);
+        PlayAudio(stream, prevComputedTime, mStateComputedTime);
+        audioStreamsActive += stream->mAudioOutputStreams.Length();
+        PlayVideo(stream);
+      }
       SourceMediaStream* is = stream->AsSourceStream();
       if (is) {
         UpdateBufferSufficiencyState(is);
       }
       GraphTime end;
       if (!stream->mBlocked.GetAt(mCurrentTime, &end) || end < GRAPH_TIME_MAX) {
         allBlockedForever = false;
       }
     }
     if (ensureNextIteration || !allBlockedForever || audioStreamsActive > 0) {
       EnsureNextIteration();
     }
 
     // Send updates to the main thread and wait for the next control loop
     // iteration.
     {
-      // Not using MonitorAutoLock since we need to unlock in a way
-      // that doesn't match lexical scopes.
       MonitorAutoLock lock(mMonitor);
       PrepareUpdatesToMainThreadState();
       if (mForceShutDown || (IsEmpty() && mMessageQueue.IsEmpty())) {
         // Enter shutdown mode. The stable-state handler will detect this
         // and complete shutdown. Destroy any streams immediately.
         LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p waiting for main thread cleanup", this));
         // Commit to shutting down this graph object.
         mLifecycleState = LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP;
         // No need to Destroy streams here. The main-thread owner of each
         // stream is responsible for calling Destroy them.
         return;
       }
 
-      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
-        // least once a minute, if we need to wake up at all
-        timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
-        timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
-        LOG(PR_LOG_DEBUG, ("Waiting for next iteration; at %f, timeout=%f",
-                           (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
-        mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
-      } else {
-        mWaitState = WAITSTATE_WAITING_INDEFINITELY;
-      }
-      if (timeout > 0) {
-        mMonitor.Wait(timeout);
-        LOG(PR_LOG_DEBUG, ("Resuming after timeout; at %f, elapsed=%f",
-                           (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
-                           (TimeStamp::Now() - now).ToSeconds()));
+      // No need to wait in non-realtime mode, just churn through the input as soon
+      // as possible.
+      if (mRealtime) {
+        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
+          // least once a minute, if we need to wake up at all
+          timeoutMS = std::max<int64_t>(0, std::min<int64_t>(timeoutMS, 60*1000));
+          timeout = PR_MillisecondsToInterval(uint32_t(timeoutMS));
+          LOG(PR_LOG_DEBUG, ("Waiting for next iteration; at %f, timeout=%f",
+                             (now - mInitialTimeStamp).ToSeconds(), timeoutMS/1000.0));
+          mWaitState = WAITSTATE_WAITING_FOR_NEXT_ITERATION;
+        } else {
+          mWaitState = WAITSTATE_WAITING_INDEFINITELY;
+        }
+        if (timeout > 0) {
+          mMonitor.Wait(timeout);
+          LOG(PR_LOG_DEBUG, ("Resuming after timeout; at %f, elapsed=%f",
+                             (TimeStamp::Now() - mInitialTimeStamp).ToSeconds(),
+                             (TimeStamp::Now() - now).ToSeconds()));
+        }
       }
       mWaitState = WAITSTATE_RUNNING;
       mNeedAnotherIteration = false;
       messageQueue.SwapElements(mMessageQueue);
     }
   }
 }
 
@@ -1934,29 +1945,30 @@ ProcessedMediaStream::DestroyImpl()
 }
 
 /**
  * We make the initial mCurrentTime nonzero so that zero times can have
  * special meaning if necessary.
  */
 static const int32_t INITIAL_CURRENT_TIME = 1;
 
-MediaStreamGraphImpl::MediaStreamGraphImpl()
+MediaStreamGraphImpl::MediaStreamGraphImpl(bool aRealtime)
   : mCurrentTime(INITIAL_CURRENT_TIME)
   , mStateComputedTime(INITIAL_CURRENT_TIME)
   , mProcessingGraphUpdateIndex(0)
   , mPortCount(0)
   , mMonitor("MediaStreamGraphImpl")
   , mLifecycleState(LIFECYCLE_THREAD_NOT_STARTED)
   , mWaitState(WAITSTATE_RUNNING)
   , mNeedAnotherIteration(false)
   , mForceShutDown(false)
   , mPostedRunInStableStateEvent(false)
   , mDetectedNotRunning(false)
   , mPostedRunInStableState(false)
+  , mRealtime(aRealtime)
 {
 #ifdef PR_LOGGING
   if (!gMediaStreamGraphLog) {
     gMediaStreamGraphLog = PR_NewLogModule("MediaStreamGraph");
   }
 #endif
 
   mCurrentTimeStamp = mInitialTimeStamp = TimeStamp::Now();
@@ -1987,23 +1999,43 @@ MediaStreamGraph::GetInstance()
   NS_ASSERTION(NS_IsMainThread(), "Main thread only");
 
   if (!gGraph) {
     if (!gShutdownObserverRegistered) {
       gShutdownObserverRegistered = true;
       nsContentUtils::RegisterShutdownObserver(new MediaStreamGraphShutdownObserver());
     }
 
-    gGraph = new MediaStreamGraphImpl();
+    gGraph = new MediaStreamGraphImpl(true);
     LOG(PR_LOG_DEBUG, ("Starting up MediaStreamGraph %p", gGraph));
   }
 
   return gGraph;
 }
 
+MediaStreamGraph*
+MediaStreamGraph::CreateNonRealtimeInstance()
+{
+  NS_ASSERTION(NS_IsMainThread(), "Main thread only");
+
+  MediaStreamGraphImpl* graph = new MediaStreamGraphImpl(false);
+  return graph;
+}
+
+void
+MediaStreamGraph::DestroyNonRealtimeInstance(MediaStreamGraph* aGraph)
+{
+  NS_ASSERTION(NS_IsMainThread(), "Main thread only");
+  MOZ_ASSERT(aGraph != gGraph, "Should not destroy the global graph here");
+
+  MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(aGraph);
+  graph->ForceShutDown();
+  delete graph;
+}
+
 SourceMediaStream*
 MediaStreamGraph::CreateSourceStream(DOMMediaStream* aWrapper)
 {
   SourceMediaStream* stream = new SourceMediaStream(aWrapper);
   NS_ADDREF(stream);
   MediaStreamGraphImpl* graph = static_cast<MediaStreamGraphImpl*>(this);
   stream->SetGraphImpl(graph);
   graph->AppendMessage(new CreateMessage(stream));
--- a/content/media/MediaStreamGraph.h
+++ b/content/media/MediaStreamGraph.h
@@ -882,27 +882,31 @@ protected:
   bool mInCycle;
 };
 
 // Returns ideal audio rate for processing
 inline TrackRate IdealAudioRate() { return 48000; }
 
 /**
  * Initially, at least, we will have a singleton MediaStreamGraph per
- * process.
+ * process.  Each OfflineAudioContext object creates its own MediaStreamGraph
+ * object too.
  */
 class MediaStreamGraph {
 public:
   // We ensure that the graph current time advances in multiples of
   // IdealAudioBlockSize()/IdealAudioRate(). A stream that never blocks
   // and has a track with the ideal audio rate will produce audio in
   // multiples of the block size.
 
   // Main thread only
   static MediaStreamGraph* GetInstance();
+  static MediaStreamGraph* CreateNonRealtimeInstance();
+  static void DestroyNonRealtimeInstance(MediaStreamGraph* aGraph);
+
   // Control API.
   /**
    * Create a stream that a media decoder (or some other source of
    * media data, such as a camera) can write to.
    */
   SourceMediaStream* CreateSourceStream(DOMMediaStream* aWrapper);
   /**
    * Create a stream that will form the union of the tracks of its input
--- a/content/media/MediaStreamGraphImpl.h
+++ b/content/media/MediaStreamGraphImpl.h
@@ -98,21 +98,29 @@ protected:
   MediaStream* mStream;
 };
 
 /**
  * The implementation of a media stream graph. This class is private to this
  * file. It's not in the anonymous namespace because MediaStream needs to
  * be able to friend it.
  *
- * Currently we only have one per process.
+ * Currently we have one global instance per process, and one per each
+ * OfflineAudioContext object.
  */
 class MediaStreamGraphImpl : public MediaStreamGraph {
 public:
-  MediaStreamGraphImpl();
+  /**
+   * Set aRealtime to true in order to create a MediaStreamGraph which provides
+   * support for real-time audio and video.  Set it to false in order to create
+   * a non-realtime instance which just churns through its inputs and produces
+   * output.  Those objects currently only support audio, and are used to
+   * implement OfflineAudioContext.  They do not support MediaStream inputs.
+   */
+  explicit MediaStreamGraphImpl(bool aRealtime);
   ~MediaStreamGraphImpl()
   {
     NS_ASSERTION(IsEmpty(),
                  "All streams should have been destroyed by messages from the main thread");
     LOG(PR_LOG_DEBUG, ("MediaStreamGraph %p destroyed", this));
   }
 
   // Main thread only.
@@ -501,13 +509,18 @@ public:
    * LIFECYCLE_RUNNING, this can be relied on to not change unexpectedly.
    */
   bool mDetectedNotRunning;
   /**
    * True when a stable state runner has been posted to the appshell to run
    * RunInStableState at the next stable state.
    */
   bool mPostedRunInStableState;
+  /**
+   * True when processing real-time audio/video.  False when processing non-realtime
+   * audio.
+   */
+  bool mRealtime;
 };
 
 }
 
 #endif /* MEDIASTREAMGRAPHIMPL_H_ */