Bug 1628779 - Update the latency periodically on main thread. r=achronop
authorPaul Adenot <paul@paul.cx>
Thu, 28 May 2020 09:50:20 +0000
changeset 532722 61336bfc758449a130882874551df6c358ecd0ae
parent 532721 3a5f3040fbef3e0c98aa1462c5c3525b96e3a76d
child 532723 99be85a6ec5c09982fbda237c9c5c9d80889473d
push id37457
push usernerli@mozilla.com
push dateThu, 28 May 2020 15:51:15 +0000
treeherdermozilla-central@272e3c98d002 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersachronop
bugs1628779
milestone78.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 1628779 - Update the latency periodically on main thread. r=achronop We're doing it async, triggered during the stable state runnable, and then the values are cached in atomic variables, so that the rendering thread can have the info as well, for using during processing. Differential Revision: https://phabricator.services.mozilla.com/D75331
dom/media/GraphDriver.cpp
dom/media/GraphDriver.h
dom/media/MediaTrackGraph.cpp
dom/media/MediaTrackGraphImpl.h
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -1227,16 +1227,27 @@ TimeDuration AudioCallbackDriver::AudioO
   if (rv || mSampleRate == 0) {
     return TimeDuration::FromSeconds(0.0);
   }
 
   return TimeDuration::FromSeconds(static_cast<double>(latencyFrames) /
                                    mSampleRate);
 }
 
+TimeDuration AudioCallbackDriver::AudioInputLatency() {
+  uint32_t latencyFrames;
+  int rv = cubeb_stream_get_input_latency(mAudioStream, &latencyFrames);
+  if (rv || mSampleRate == 0) {
+    return TimeDuration::FromSeconds(0.0);
+  }
+
+  return TimeDuration::FromSeconds(static_cast<double>(latencyFrames) /
+                                   mSampleRate);
+}
+
 void AudioCallbackDriver::FallbackToSystemClockDriver() {
   MOZ_ASSERT(!ThreadRunning());
   MOZ_ASSERT(mAudioStreamState == AudioStreamState::None ||
              mAudioStreamState == AudioStreamState::Errored ||
              mAudioStreamState == AudioStreamState::Pending);
   MOZ_ASSERT(mFallbackDriverState == FallbackDriverState::None);
   LOG(LogLevel::Debug,
       ("%p: AudioCallbackDriver %p Falling back to SystemClockDriver.", Graph(),
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -641,16 +641,18 @@ class AudioCallbackDriver : public Graph
   /* Whether the underlying cubeb stream has been started. See comment for
    * mStarted for details. */
   bool IsStarted();
 
   void CompleteAudioContextOperations(AsyncCubebOperation aOperation);
 
   // Returns the output latency for the current audio output stream.
   TimeDuration AudioOutputLatency();
+  // Returns the input latency for the current audio output stream.
+  TimeDuration AudioInputLatency();
 
  private:
   /**
    * On certain MacBookPro, the microphone is located near the left speaker.
    * We need to pan the sound output to the right speaker if we are using the
    * mic and the built-in speaker, or we will have terrible echo.  */
   void PanOutputIfNeeded(bool aMicrophoneActive);
   /**
--- a/dom/media/MediaTrackGraph.cpp
+++ b/dom/media/MediaTrackGraph.cpp
@@ -905,19 +905,19 @@ void MediaTrackGraphImpl::DeviceChanged(
     // This is a special case where the origin of this event cannot control the
     // lifetime of the graph, because the graph is controling the lifetime of
     // the AudioCallbackDriver where the event originated.
     // We know the graph is soon going away, so there's no need to notify about
     // this device change.
     return;
   }
 
-  // Reset the latency, it will get fetched again next time it's queried.
   MOZ_ASSERT(NS_IsMainThread());
-  mAudioOutputLatency = 0.0;
+  // Have the latency re-queried next stable state.
+  mLatencyQueryCounter = LATENCY_QUERYING_INTERVAL;
 
   // Dispatch to the bg thread to do the (potentially expensive) query of the
   // maximum channel count, and then dispatch back to the main thread, then to
   // the graph, with the new info.
   RefPtr<MediaTrackGraphImpl> self = this;
   NS_DispatchBackgroundTask(NS_NewRunnableFunction(
       "MaxChannelCountUpdateOnBgThread", [self{move(self)}]() {
         uint32_t maxChannelCount = CubebUtils::MaxNumberOfChannels();
@@ -1682,16 +1682,21 @@ void MediaTrackGraphImpl::RunInStableSta
         "LIFECYCLE_WAITING_FOR_MAIN_THREAD_CLEANUP",
         "LIFECYCLE_WAITING_FOR_THREAD_SHUTDOWN",
         "LIFECYCLE_WAITING_FOR_TRACK_DESTRUCTION"};
 
     if (LifecycleStateRef() != LIFECYCLE_RUNNING) {
       LOG(LogLevel::Debug,
           ("%p: Running stable state callback. Current state: %s", this,
            LifecycleState_str[LifecycleStateRef()]));
+    } else {
+      if (mLatencyQueryCounter++ == LATENCY_QUERYING_INTERVAL) {
+        UpdateAudioLatencies();
+        mLatencyQueryCounter = 0;
+      }
     }
 
     runnables.SwapElements(mUpdateRunnables);
     for (uint32_t i = 0; i < mTrackUpdates.Length(); ++i) {
       TrackUpdate* update = &mTrackUpdates[i];
       if (update->mTrack) {
         ApplyTrackUpdate(update);
       }
@@ -2982,16 +2987,17 @@ MediaTrackGraphImpl::MediaTrackGraphImpl
       mGlobalVolume(CubebUtils::GetVolumeScale())
 #ifdef DEBUG
       ,
       mCanRunMessagesSynchronously(false)
 #endif
       ,
       mMainThreadGraphTime(0, "MediaTrackGraphImpl::mMainThreadGraphTime"),
       mAudioOutputLatency(0.0),
+      mAudioInputLatency(0.0),
       mMaxOutputChannelCount(std::min(8u, CubebUtils::MaxNumberOfChannels())) {
   if (aRunTypeRequested == SINGLE_THREAD && !mGraphRunner) {
     // Failed to create thread.  Jump to the last phase of the lifecycle.
     mLifecycleState = LIFECYCLE_WAITING_FOR_TRACK_DESTRUCTION;
 #ifdef DEBUG
     mCanRunMessagesSynchronously = true;
 #endif
     return;
@@ -3619,37 +3625,53 @@ uint32_t MediaTrackGraphImpl::AudioOutpu
     if (CurrentDriver()->AsAudioCallbackDriver()) {
       return CurrentDriver()->AsAudioCallbackDriver()->OutputChannelCount();
     }
     return 2;
   }
 }
 
 double MediaTrackGraph::AudioOutputLatency() {
-  return static_cast<MediaTrackGraphImpl*>(this)->AudioOutputLatency();
+  return static_cast<MediaTrackGraphImpl*>(this)->CachedAudioOutputLatency();
 }
 
-double MediaTrackGraphImpl::AudioOutputLatency() {
+double MediaTrackGraphImpl::CachedAudioOutputLatency() {
+  MOZ_ASSERT(NS_IsMainThread());
+  return mAudioOutputLatency;
+}
+
+double MediaTrackGraphImpl::CachedAudioInputLatency() {
   MOZ_ASSERT(NS_IsMainThread());
-  if (mAudioOutputLatency != 0.0) {
-    return mAudioOutputLatency;
-  }
-  MonitorAutoLock lock(mMonitor);
-  if (CurrentDriver()->AsAudioCallbackDriver()) {
-    mAudioOutputLatency = CurrentDriver()
-                              ->AsAudioCallbackDriver()
-                              ->AudioOutputLatency()
-                              .ToSeconds();
-  } else {
-    // Failure mode: return 0.0 if running on a normal thread.
-    mAudioOutputLatency = 0.0;
-  }
-
+  return mAudioInputLatency;
+}
+
+void MediaTrackGraphImpl::UpdateAudioLatencies() {
+  RefPtr<MediaTrackGraphImpl> self = this;
+  NS_DispatchBackgroundTask(
+      NS_NewRunnableFunction("UpdateLatency", [self{std::move(self)}]() {
+        MonitorAutoLock lock(self->mMonitor);
+        if (self->CurrentDriver()->AsAudioCallbackDriver()) {
+          AudioCallbackDriver* driver =
+              self->CurrentDriver()->AsAudioCallbackDriver();
+          if (driver->InputChannelCount()) {
+            self->mAudioInputLatency = driver->AudioInputLatency().ToSeconds();
+          }
+          self->mAudioOutputLatency = driver->AudioOutputLatency().ToSeconds();
+        }
+      }));
+}
+
+double MediaTrackGraphImpl::AudioOutputLatencyGraphThread() {
+  AssertOnGraphThreadOrNotRunning();
   return mAudioOutputLatency;
 }
+double MediaTrackGraphImpl::AudioInputLatencyGraphThread() {
+  AssertOnGraphThreadOrNotRunning();
+  return mAudioInputLatency;
+}
 
 bool MediaTrackGraph::IsNonRealtime() const {
   return !static_cast<const MediaTrackGraphImpl*>(this)->mRealtime;
 }
 
 void MediaTrackGraph::StartNonRealtimeProcessing(uint32_t aTicksToProcess) {
   MOZ_ASSERT(NS_IsMainThread(), "main thread only");
 
--- a/dom/media/MediaTrackGraphImpl.h
+++ b/dom/media/MediaTrackGraphImpl.h
@@ -491,17 +491,24 @@ class MediaTrackGraphImpl : public Media
     mTrackOrderDirty = true;
   }
 
   // Get the current maximum channel count. Graph thread only.
   uint32_t AudioOutputChannelCount() const;
   // Set a new maximum channel count. Graph thread only.
   void SetMaxOutputChannelCount(uint32_t aMaxChannelCount);
 
-  double AudioOutputLatency();
+  // Main thread only, query and update the cached latency figure.
+  double CachedAudioOutputLatency();
+  double CachedAudioInputLatency();
+  void UpdateAudioLatencies();
+
+  // Graph thread only, get the cached latency figure.
+  double AudioOutputLatencyGraphThread();
+  double AudioInputLatencyGraphThread();
 
   /**
    * The audio input channel count for a MediaTrackGraph is the max of all the
    * channel counts requested by the listeners. The max channel count is
    * delivered to the listeners themselves, and they take care of downmixing.
    */
   uint32_t AudioInputChannelCount() {
     MOZ_ASSERT(OnGraphThreadOrNotRunning());
@@ -1019,20 +1026,36 @@ class MediaTrackGraphImpl : public Media
 
   /**
    * Set based on mProcessedTime at end of iteration.
    * Read by stable state runnable on main thread. Protected by mMonitor.
    */
   GraphTime mNextMainThreadGraphTime = 0;
 
   /**
-   * Cached audio output latency, in seconds. Main thread only. This is reset
+   * Cached audio output latency, in seconds. This is reset
+   * whenever the audio device running this MediaTrackGraph changes.
+   */
+  std::atomic<double> mAudioOutputLatency;
+  /**
+   * Cached audio input latency, in seconds. This is reset
    * whenever the audio device running this MediaTrackGraph changes.
    */
-  double mAudioOutputLatency;
+  std::atomic<double> mAudioInputLatency;
+
+  /**
+   * Only query the latency once every LATENCY_QUERYING_INTERVAL stable state.
+   */
+  const uint32_t LATENCY_QUERYING_INTERVAL = 100;
+
+  /**
+   * Counter to only query the audio latencies every N iterations. Main thread
+   * only.
+   */
+  uint32_t mLatencyQueryCounter = 0;
 
   /**
    * The max audio output channel count the default audio output device
    * supports. This is cached here because it can be expensive to query. The
    * cache is invalidated when the device is changed. This is initialized in the
    * ctor, and the read/write only on the graph thread.
    */
   uint32_t mMaxOutputChannelCount;