Bug 1404977 - Part 9 - Propagate the changes to the GraphDrivers, simplifying them, and brokering all access through the MSG. r?pehrsons draft
authorPaul Adenot <paul@paul.cx>
Mon, 30 Apr 2018 16:01:56 +0200
changeset 824650 f3f2e26d2d215a3d39efaaee44c91c02e515b152
parent 824649 2488a5f07872d837d65fc1816278213470dd54ae
child 824651 df30601c4ac9f7e02dec32ac62d1621c23708a13
push id117965
push userpaul@paul.cx
push dateTue, 31 Jul 2018 14:52:35 +0000
reviewerspehrsons
bugs1404977
milestone63.0a1
Bug 1404977 - Part 9 - Propagate the changes to the GraphDrivers, simplifying them, and brokering all access through the MSG. r?pehrsons For an AudioCallbackDriver, the number of input channels is immutable, and passed at construction, so that it's less necessary to rely on global state. MozReview-Commit-ID: F9TL1H92z3W
dom/media/GraphDriver.cpp
dom/media/GraphDriver.h
dom/media/MediaStreamGraph.cpp
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -69,17 +69,24 @@ void GraphDriver::SwitchAtNextIteration(
   MOZ_ASSERT(aNextDriver);
   GraphImpl()->GetMonitor().AssertCurrentThreadOwns();
 
   LOG(LogLevel::Debug,
       ("Switching to new driver: %p (%s)",
        aNextDriver,
        aNextDriver->AsAudioCallbackDriver() ? "AudioCallbackDriver"
                                             : "SystemClockDriver"));
-
+  if (mNextDriver &&
+      mNextDriver != GraphImpl()->CurrentDriver()) {
+    LOG(LogLevel::Debug,
+        ("Discarding previous next driver: %p (%s)",
+         mNextDriver.get(),
+         mNextDriver->AsAudioCallbackDriver() ? "AudioCallbackDriver"
+                                              : "SystemClockDriver"));
+  }
   SetNextDriver(aNextDriver);
 }
 
 GraphTime
 GraphDriver::StateComputedTime() const
 {
   return mGraphImpl->mStateComputedTime;
 }
@@ -533,29 +540,27 @@ StreamAndPromiseForOperation::StreamAndP
                                           dom::AudioContextOperation aOperation)
   : mStream(aStream)
   , mPromise(aPromise)
   , mOperation(aOperation)
 {
   // MOZ_ASSERT(aPromise);
 }
 
-AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl)
+AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl, uint32_t aInputChannelCount)
   : GraphDriver(aGraphImpl)
   , mOutputChannels(0)
   , mSampleRate(0)
-  , mInputChannels(1)
+  , mInputChannelCount(aInputChannelCount)
   , mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS)
   , mStarted(false)
-  , mAudioInput(nullptr)
   , mInitShutdownThread(SharedThreadPool::Get(NS_LITERAL_CSTRING("CubebOperation"), 1))
   , mAddedMixer(false)
   , mAudioThreadId(std::thread::id())
   , mAudioThreadRunning(false)
-  , mMicrophoneActive(false)
   , mShouldFallbackIfError(false)
   , mFromFallback(false)
 {
   LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
 
   const uint32_t kIdleThreadTimeoutMs = 2000;
   mInitShutdownThread->
       SetIdleThreadTimeout(PR_MillisecondsToInterval(kIdleThreadTimeoutMs));
@@ -632,17 +637,17 @@ AudioCallbackDriver::Init()
 
   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
     output.format = CUBEB_SAMPLE_S16NE;
   } else {
     output.format = CUBEB_SAMPLE_FLOAT32NE;
   }
 
   // Query and set the number of channels this AudioCallbackDriver will use.
-  mOutputChannels = mGraphImpl->AudioChannelCount();
+  mOutputChannels = GraphImpl()->AudioOutputChannelCount();
   if (!mOutputChannels) {
     LOG(LogLevel::Warning, ("Output number of channels is 0."));
     MonitorAutoLock lock(GraphImpl()->GetMonitor());
     FallbackToSystemClockDriver();
     return true;
   }
 
   mBuffer = AudioCallbackBufferWrapper<AudioDataValue>(mOutputChannels);
@@ -656,91 +661,72 @@ AudioCallbackDriver::Init()
 
   // Macbook and MacBook air don't have enough CPU to run very low latency
   // MediaStreamGraphs, cap the minimal latency to 512 frames int this case.
   if (IsMacbookOrMacbookAir()) {
     latency_frames = std::max((uint32_t) 512, latency_frames);
   }
 
   input = output;
-  input.channels = mInputChannels;
+  input.channels = mInputChannelCount;
   input.layout = CUBEB_LAYOUT_UNDEFINED;
 
-#ifdef MOZ_WEBRTC
-  if (mGraphImpl->mInputWanted) {
-    StaticMutexAutoLock lock(AudioInputCubeb::Mutex());
-    uint32_t userChannels = 0;
-    AudioInputCubeb::GetUserChannelCount(mGraphImpl->mInputDeviceID, userChannels);
-    input.channels = mInputChannels = std::min<uint32_t>(8, userChannels);
-  }
-#endif
-
   cubeb_stream* stream = nullptr;
-  CubebUtils::AudioDeviceID input_id = nullptr, output_id = nullptr;
-  // We have to translate the deviceID values to cubeb devid's since those can be
-  // freed whenever enumerate is called.
-  {
-#ifdef MOZ_WEBRTC
-    StaticMutexAutoLock lock(AudioInputCubeb::Mutex());
-#endif
-    if ((!mGraphImpl->mInputWanted
-#ifdef MOZ_WEBRTC
-         || AudioInputCubeb::GetDeviceID(mGraphImpl->mInputDeviceID, input_id)
-#endif
-         ) &&
-        (mGraphImpl->mOutputDeviceID == -1 // pass nullptr for ID for default output
-#ifdef MOZ_WEBRTC
-         // XXX we should figure out how we would use a deviceID for output without webrtc.
-         // Currently we don't set this though, so it's ok
-         || AudioInputCubeb::GetDeviceID(mGraphImpl->mOutputDeviceID, output_id)
-#endif
-         ) &&
-        // XXX Only pass input input if we have an input listener.  Always
-        // set up output because it's easier, and it will just get silence.
-        // XXX Add support for adding/removing an input listener later.
-        cubeb_stream_init(cubebContext, &stream,
-                          "AudioCallbackDriver",
-                          input_id,
-                          mGraphImpl->mInputWanted ? &input : nullptr,
-                          output_id,
-                          mGraphImpl->mOutputWanted ? &output : nullptr, latency_frames,
-                          DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
-      mAudioStream.own(stream);
-      DebugOnly<int> rv = cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());
-      NS_WARNING_ASSERTION(
-        rv == CUBEB_OK,
-        "Could not set the audio stream volume in GraphDriver.cpp");
-      CubebUtils::ReportCubebBackendUsed();
-    } else {
-#ifdef MOZ_WEBRTC
-      StaticMutexAutoUnlock unlock(AudioInputCubeb::Mutex());
-#endif
-      NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling back to a SystemClockDriver");
-      // Only report failures when we're not coming from a driver that was
-      // created itself as a fallback driver because of a previous audio driver
-      // failure.
-      if (!mFromFallback) {
-        CubebUtils::ReportCubebStreamInitFailure(firstStream);
-      }
-      MonitorAutoLock lock(GraphImpl()->GetMonitor());
-      FallbackToSystemClockDriver();
-      return true;
+  bool inputWanted = mInputChannelCount > 0;
+  CubebUtils::AudioDeviceID output_id = GraphImpl()->mOutputDeviceID;
+  CubebUtils::AudioDeviceID input_id = GraphImpl()->mInputDeviceID;
+
+  // XXX Only pass input input if we have an input listener.  Always
+  // set up output because it's easier, and it will just get silence.
+  if (cubeb_stream_init(cubebContext,
+                        &stream,
+                        "AudioCallbackDriver",
+                        input_id,
+                        inputWanted ? &input : nullptr,
+                        output_id,
+                        &output,
+                        latency_frames,
+                        DataCallback_s,
+                        StateCallback_s,
+                        this) == CUBEB_OK) {
+    mAudioStream.own(stream);
+    DebugOnly<int> rv =
+      cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());
+    NS_WARNING_ASSERTION(
+      rv == CUBEB_OK,
+      "Could not set the audio stream volume in GraphDriver.cpp");
+    CubebUtils::ReportCubebBackendUsed();
+  } else {
+    NS_WARNING("Could not create a cubeb stream for MediaStreamGraph, falling "
+        "back to a SystemClockDriver");
+    // Only report failures when we're not coming from a driver that was
+    // created itself as a fallback driver because of a previous audio driver
+    // failure.
+    if (!mFromFallback) {
+      CubebUtils::ReportCubebStreamInitFailure(firstStream);
     }
+    MonitorAutoLock lock(GraphImpl()->GetMonitor());
+    FallbackToSystemClockDriver();
+    return true;
   }
   SetMicrophoneActive(mGraphImpl->mInputWanted);
 
-  cubeb_stream_register_device_changed_callback(mAudioStream,
-                                                AudioCallbackDriver::DeviceChangedCallback_s);
+#ifdef XP_MACOSX
+   PanOutputIfNeeded(inputWanted);
+#endif
+
+  cubeb_stream_register_device_changed_callback(
+    mAudioStream, AudioCallbackDriver::DeviceChangedCallback_s);
 
   if (!StartStream()) {
-    LOG(LogLevel::Warning, ("AudioCallbackDriver couldn't start stream."));
+    LOG(LogLevel::Warning, ("%p: AudioCallbackDriver couldn't start a cubeb stream.", GraphImpl()));
     return false;
   }
 
-  LOG(LogLevel::Debug, ("AudioCallbackDriver started."));
+  LOG(LogLevel::Debug, ("%p: AudioCallbackDriver started.", GraphImpl()));
   return true;
 }
 
 void
 AudioCallbackDriver::Start()
 {
   MOZ_ASSERT(!IsStarted());
   MOZ_ASSERT(NS_IsMainThread() || OnCubebOperationThread() ||
@@ -1145,31 +1131,19 @@ void AudioCallbackDriver::PanOutputIfNee
 
 void
 AudioCallbackDriver::DeviceChangedCallback()
 {
   MOZ_ASSERT(!OnThread());
   // Tell the audio engine the device has changed, it might want to reset some
   // state.
   MonitorAutoLock mon(mGraphImpl->GetMonitor());
-  if (mAudioInput) {
-    mAudioInput->DeviceChanged();
-  }
+  GraphImpl()->DeviceChanged();
 #ifdef XP_MACOSX
-  PanOutputIfNeeded(mMicrophoneActive);
-#endif
-}
-
-void
-AudioCallbackDriver::SetMicrophoneActive(bool aActive)
-{
-  mMicrophoneActive = aActive;
-
-#ifdef XP_MACOSX
-  PanOutputIfNeeded(mMicrophoneActive);
+  PanOutputIfNeeded(mInputChannelCount);
 #endif
 }
 
 uint32_t
 AudioCallbackDriver::IterationDuration()
 {
   MOZ_ASSERT(OnThread());
   // The real fix would be to have an API in cubeb to give us the number. Short
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -258,18 +258,17 @@ public:
   void Revive() override;
   void Shutdown() override;
   /**
    * Runs main control loop on the graph thread. Normally a single invocation
    * of this runs for the entire lifetime of the graph thread.
    */
   void RunThread();
   friend class MediaStreamGraphInitThreadRunnable;
-  uint32_t IterationDuration() override
-  {
+  uint32_t IterationDuration() override {
     return MEDIA_GRAPH_TARGET_PERIOD_MS;
   }
 
   bool OnThread() override
   {
     return mThread && mThread->EventTarget()->IsOnCurrentThread();
   }
 
@@ -395,17 +394,18 @@ enum AsyncCubebOperation {
  */
 class AudioCallbackDriver : public GraphDriver,
                             public MixerCallbackReceiver
 #if defined(XP_WIN)
                             , public audio::DeviceChangeListener
 #endif
 {
 public:
-  explicit AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl);
+  /** If aInputChannelCount is zero, then this driver is output-only. */
+  AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl, uint32_t aInputChannelCount);
   virtual ~AudioCallbackDriver();
 
   void Start() override;
   void Revive() override;
   void WaitForNextIteration() override;
   void WakeUp() override;
   void Shutdown() override;
 #if defined(XP_WIN)
@@ -413,25 +413,26 @@ public:
 #endif
 
   /* Static wrapper function cubeb calls back. */
   static long DataCallback_s(cubeb_stream * aStream,
                              void * aUser,
                              const void * aInputBuffer,
                              void * aOutputBuffer,
                              long aFrames);
-  static void StateCallback_s(cubeb_stream* aStream, void * aUser,
-                              cubeb_state aState);
+  static void StateCallback_s(cubeb_stream* aStream, void * aUser, cubeb_state aState);
   static void DeviceChangedCallback_s(void * aUser);
   /* This function is called by the underlying audio backend when a refill is
    * needed. This is what drives the whole graph when it is used to output
    * audio. If the return value is exactly aFrames, this function will get
    * called again. If it is less than aFrames, the stream will go in draining
    * mode, and this function will not be called again. */
-  long DataCallback(const AudioDataValue* aInputBuffer, AudioDataValue* aOutputBuffer, long aFrames);
+  long DataCallback(const AudioDataValue* aInputBuffer,
+                    AudioDataValue* aOutputBuffer,
+                    long aFrames);
   /* This function is called by the underlying audio backend, but is only used
    * for informational purposes at the moment. */
   void StateCallback(cubeb_state aState);
   /* This is an approximation of the number of millisecond there are between two
    * iterations of the graph. */
   uint32_t IterationDuration() override;
 
   /* This function gets called when the graph has produced the audio frames for
@@ -458,16 +459,21 @@ public:
   }
 
   uint32_t OutputChannelCount()
   {
     MOZ_ASSERT(mOutputChannels != 0 && mOutputChannels <= 8);
     return mOutputChannels;
   }
 
+  uint32_t InputChannelCount()
+  {
+    return mInputChannelCount;
+  }
+
   /* Enqueue a promise that is going to be resolved when a specific operation
    * occurs on the cubeb stream. */
   void EnqueueStreamAndPromiseForOperation(MediaStream* aStream,
                                          void* aPromise,
                                          dom::AudioContextOperation aOperation);
 
   bool OnThread() override
   {
@@ -478,20 +484,16 @@ public:
   {
     return mAudioThreadRunning;
   }
 
   /* Whether the underlying cubeb stream has been started. See comment for
    * mStarted for details. */
   bool IsStarted();
 
-  /* Tell the driver whether this process is using a microphone or not. This is
-   * thread safe. */
-  void SetMicrophoneActive(bool aActive);
-
   void CompleteAudioContextOperations(AsyncCubebOperation aOperation);
 
 private:
   /* Remove Mixer callbacks when switching */
   void RemoveMixerCallback();
   /* Add this driver in Mixer callbacks. */
   void AddMixerCallback();
   /**
@@ -531,19 +533,19 @@ private:
    * callback thread. */
   AudioCallbackBufferWrapper<AudioDataValue> mBuffer;
   /* cubeb stream for this graph. This is guaranteed to be non-null after Init()
    * has been called, and is synchronized internaly. */
   nsAutoRef<cubeb_stream> mAudioStream;
   /* The sample rate for the aforementionned cubeb stream. This is set on
    * initialization and can be read safely afterwards. */
   uint32_t mSampleRate;
-  /* The number of input channels from cubeb.  Should be set before opening cubeb
-   * and then be static. */
-  uint32_t mInputChannels;
+  /* The number of input channels from cubeb. Set before opening cubeb. If it is
+   * zero then the driver is output-only. */
+  const uint32_t mInputChannelCount;
   /* Approximation of the time between two callbacks. This is used to schedule
    * video frames. This is in milliseconds. Only even used (after
    * inizatialization) on the audio callback thread. */
   uint32_t mIterationDurationMS;
   /* cubeb_stream_init calls the audio callback to prefill the buffers. The
    * previous driver has to be kept alive until the audio stream has been
    * started, because it is responsible to call cubeb_stream_start, so we delay
    * the cleanup of the previous driver until it has started the audio stream.
@@ -553,18 +555,16 @@ private:
    * This is written on the previous driver's thread (if switching) or main
    * thread (if this driver is the first one).
    * This is read on previous driver's thread (during callbacks from
    * cubeb_stream_init) and the audio thread (when switching away from this
    * driver back to a SystemClockDriver).
    * This is synchronized by the Graph's monitor.
    * */
   Atomic<bool> mStarted;
-  /* Listener for mic input, if any. */
-  RefPtr<AudioDataListener> mAudioInput;
 
   struct AutoInCallback
   {
     explicit AutoInCallback(AudioCallbackDriver* aDriver);
     ~AutoInCallback();
     AudioCallbackDriver* mDriver;
   };
 
@@ -580,20 +580,16 @@ private:
   Atomic<bool> mAddedMixer;
 
   /* Contains the id of the audio thread for as long as the callback
    * is taking place, after that it is reseted to an invalid value. */
   std::atomic<std::thread::id> mAudioThreadId;
   /* True when audio thread is running. False before
    * starting and after stopping it the audio thread. */
   Atomic<bool> mAudioThreadRunning;
-  /**
-   * True if microphone is being used by this process. This is synchronized by
-   * the graph's monitor. */
-  Atomic<bool> mMicrophoneActive;
   /* Indication of whether a fallback SystemClockDriver should be started if
    * StateCallback() receives an error.  No mutex need be held during access.
    * The transition to true happens before cubeb_stream_start() is called.
    * After transitioning to false on the last DataCallback(), the stream is
    * not accessed from another thread until the graph thread either signals
    * main thread cleanup or dispatches an event to switch to another
    * driver. */
   bool mShouldFallbackIfError;
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -406,17 +406,17 @@ MediaStreamGraphImpl::UpdateStreamOrder(
     switching = CurrentDriver()->Switching();
   }
 
   if (audioTrackPresent && mRealtime &&
       !CurrentDriver()->AsAudioCallbackDriver() &&
       !switching) {
     MonitorAutoLock mon(mMonitor);
     if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
-      AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+      AudioCallbackDriver* driver = new AudioCallbackDriver(this, AudioInputChannelCount());
       CurrentDriver()->SwitchAtNextIteration(driver);
     }
   }
 
   if (!mStreamOrderDirty) {
     return;
   }
 
@@ -661,17 +661,17 @@ MediaStreamGraphImpl::CreateOrDestroyAud
         MonitorAutoLock lock(mMonitor);
         switching = CurrentDriver()->Switching();
       }
 
       if (!CurrentDriver()->AsAudioCallbackDriver() &&
           !switching) {
         MonitorAutoLock mon(mMonitor);
         if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
-          AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+          AudioCallbackDriver* driver = new AudioCallbackDriver(this, AudioInputChannelCount());
           CurrentDriver()->SwitchAtNextIteration(driver);
         }
       }
     }
   }
 
   for (int32_t i = audioOutputStreamsFound.Length() - 1; i >= 0; --i) {
     if (!audioOutputStreamsFound[i]) {
@@ -817,18 +817,17 @@ MediaStreamGraphImpl::OpenAudioInputImpl
   if (listeners.Length() == 1) { // first open for this device
     mInputDeviceID = aID;
     // Switch Drivers since we're adding input (to input-only or full-duplex)
     MonitorAutoLock mon(mMonitor);
     if (LifecycleStateRef() == LIFECYCLE_RUNNING) {
       AudioCallbackDriver* driver = new AudioCallbackDriver(this, AudioInputChannelCount());
       LOG(
         LogLevel::Debug,
-        ("OpenAudioInput: starting new AudioCallbackDriver(input) %p", driver));
-      driver->SetInputListener(aListener);
+        ("%p OpenAudioInput: starting new AudioCallbackDriver(input) %p", this, driver));
       CurrentDriver()->SwitchAtNextIteration(driver);
    } else {
      LOG(LogLevel::Error, ("OpenAudioInput in shutdown!"));
      MOZ_ASSERT_UNREACHABLE("Can't open cubeb inputs in shutdown");
     }
   }
 }
 
@@ -3658,18 +3657,18 @@ MediaStreamGraphImpl::MediaStreamGraphIm
   , mSelfRef(this)
   , mOutputChannels(std::min<uint32_t>(8, CubebUtils::MaxNumberOfChannels()))
 #ifdef DEBUG
   , mCanRunMessagesSynchronously(false)
 #endif
 {
   if (mRealtime) {
     if (aDriverRequested == AUDIO_THREAD_DRIVER) {
-      AudioCallbackDriver* driver = new AudioCallbackDriver(this);
-      mDriver = driver;
+      // Always start with zero input channels.
+      mDriver = new AudioCallbackDriver(this, 0);
     } else {
       mDriver = new SystemClockDriver(this);
     }
 
 #ifdef TRACING
     // This is a noop if the logger has not been enabled.
     gMSGTraceLogger.Start();
     gMSGTraceLogger.Log("[");
@@ -4188,17 +4187,17 @@ MediaStreamGraphImpl::ApplyAudioContextO
   // anyways, but doing this now save some time.
   if (aOperation == AudioContextOperation::Resume) {
     if (!CurrentDriver()->AsAudioCallbackDriver()) {
       AudioCallbackDriver* driver;
       if (switching) {
         MOZ_ASSERT(nextDriver->AsAudioCallbackDriver());
         driver = nextDriver->AsAudioCallbackDriver();
       } else {
-        driver = new AudioCallbackDriver(this);
+        driver = new AudioCallbackDriver(this, AudioInputChannelCount());
         MonitorAutoLock lock(mMonitor);
         CurrentDriver()->SwitchAtNextIteration(driver);
       }
       driver->EnqueueStreamAndPromiseForOperation(aDestinationStream,
           aPromise, aOperation);
     } else {
       // We are resuming a context, but we are already using an
       // AudioCallbackDriver, we can resolve the promise now.