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 798186 a7e2e50a131fdd1932704746e8867ddb79308c2b
parent 798185 aa4fa933490da6fcc97462f52094d5124d630836
child 798187 0f2308e92a16ccac5ffd06f22eae32caa7deeb20
push id110687
push userachronop@gmail.com
push dateTue, 22 May 2018 14:13:17 +0000
reviewerspehrsons
bugs1404977
milestone62.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
@@ -526,27 +526,25 @@ 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)
   , mAddedMixer(false)
   , mAudioThreadId(std::thread::id())
-  , mMicrophoneActive(false)
   , mShouldFallbackIfError(false)
   , mFromFallback(false)
 {
   LOG(LogLevel::Debug, ("AudioCallbackDriver ctor for graph %p", aGraphImpl));
 #if defined(XP_WIN)
   if (XRE_IsContentProcess()) {
     audio::AudioNotificationReceiver::Register(this);
   }
@@ -584,16 +582,18 @@ bool IsMacbookOrMacbookAir()
       }
     }
     return false;
   }
 #endif
   return false;
 }
 
+bool IsSome(cubeb_devid id) {return id;}
+
 bool
 AudioCallbackDriver::Init()
 {
   cubeb* cubebContext = CubebUtils::GetCubebContext();
   if (!cubebContext) {
     NS_WARNING("Could not get cubeb context.");
     LOG(LogLevel::Warning, ("%s: Could not get cubeb context", __func__));
     if (!mFromFallback) {
@@ -630,17 +630,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);
@@ -654,91 +654,75 @@ 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)
+  bool inputWanted = !!mInputChannelCount;
+  CubebUtils::AudioDeviceID output_id = IsSome(forcedOutputDeviceId)
+                                         ? forcedOutputDeviceId
+                                         : 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;
+   }
+
+
+#ifdef XP_MACOSX
+   PanOutputIfNeeded(inputWanted);
 #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,
-                          forcedOutputDeviceId ? forcedOutputDeviceId : 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;
-    }
-  }
-  SetMicrophoneActive(mGraphImpl->mInputWanted);
 
-  cubeb_stream_register_device_changed_callback(mAudioStream,
-                                                AudioCallbackDriver::DeviceChangedCallback_s);
+  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()
 {
   if (mPreviousDriver) {
     if (mPreviousDriver->AsAudioCallbackDriver()) {
@@ -971,21 +955,19 @@ AudioCallbackDriver::DataCallback(const 
   if (stateComputedTime < mIterationEnd) {
     LOG(LogLevel::Error, ("Media graph global underrun detected"));
     MOZ_ASSERT_UNREACHABLE("We should not underrun in full duplex");
     mIterationEnd = stateComputedTime;
   }
 
   // Process mic data if any/needed
   if (aInputBuffer) {
-    if (mAudioInput) { // for this specific input-only or full-duplex stream
-      mAudioInput->NotifyInputData(mGraphImpl, aInputBuffer,
-                                   static_cast<size_t>(aFrames),
-                                   mSampleRate, mInputChannels);
-    }
+    MOZ_ASSERT(mInputChannelCount);
+    GraphImpl()->NotifyInputData(aInputBuffer, static_cast<size_t>(aFrames),
+                                mSampleRate, mInputChannelCount);
   }
 
   bool stillProcessing;
   if (mBuffer.Available()) {
     // We totally filled the buffer (and mScratchBuffer isn't empty).
     // We don't need to run an iteration and if we do so we may overflow.
     stillProcessing = mGraphImpl->OneIteration(nextStateComputedTime);
   } else {
@@ -1113,32 +1095,20 @@ void AudioCallbackDriver::PanOutputIfNee
   }
 #endif
 }
 
 void
 AudioCallbackDriver::DeviceChangedCallback() {
   // Tell the audio engine the device has changed, it might want to reset some
   // state.
-  MonitorAutoLock mon(mGraphImpl->GetMonitor());
-  if (mAudioInput) {
-    mAudioInput->DeviceChanged();
-  }
+  MonitorAutoLock mon(GraphImpl()->GetMonitor());
+  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()
 {
   // The real fix would be to have an API in cubeb to give us the number. Short
   // of that, we approximate it here. bug 1019507
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -376,17 +376,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)
@@ -394,80 +395,71 @@ 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
    * this iteration. */
   void MixerCallback(AudioDataValue* aMixedBuffer,
                      AudioSampleFormat aFormat,
                      uint32_t aChannels,
                      uint32_t aFrames,
                      uint32_t aSampleRate) override;
 
-  // These are invoked on the MSG thread (we don't call this if not LIFECYCLE_RUNNING)
-  virtual void SetInputListener(AudioDataListener *aListener) {
-    MOZ_ASSERT(!IsStarted());
-    mAudioInput = aListener;
-  }
-  // XXX do we need the param?  probably no
-  virtual void RemoveInputListener(AudioDataListener *aListener) {
-    MOZ_ASSERT(OnThread());
-    mAudioInput = nullptr;
-  }
-
   AudioCallbackDriver* AsAudioCallbackDriver() override {
     return this;
   }
 
   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
   {
     return mAudioThreadId.load() == std::this_thread::get_id();
   }
 
   /* 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);
 
   /* Fetch, or create a shared thread pool with up to one thread for
    * AsyncCubebTask. */
   SharedThreadPool* GetInitShutdownThread();
 
 private:
   /* Remove Mixer callbacks when switching */
@@ -504,18 +496,18 @@ private:
   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;
+   * and then be static. 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.
@@ -525,18 +517,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;
   };
 
@@ -546,20 +536,16 @@ private:
   /* This must be accessed with the graph monitor held. */
   AutoTArray<StreamAndPromiseForOperation, 1> mPromisesForOperation;
   /* Used to queue us to add the mixer callback on first run. */
   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 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
@@ -408,17 +408,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;
   }
 
@@ -663,17 +663,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]) {
@@ -786,17 +786,17 @@ MediaStreamGraphImpl::PlayAudio(MediaStr
       }
       t = end;
     }
     audioOutput.mLastTickWritten = offset;
 
     // Need unique id for stream & track - and we want it to match the inserter
     output.WriteTo(LATENCY_STREAM_ID(aStream, track->GetID()),
                                      mMixer,
-                                     AudioChannelCount(),
+                                     AudioOutputChannelCount(),
                                      mSampleRate);
   }
   return ticksWritten;
 }
 
 void
 MediaStreamGraphImpl::OpenAudioInputImpl(CubebUtils::AudioDeviceID aID,
                                          AudioDataListener *aListener)
@@ -818,18 +818,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");
     }
   }
 }
 
@@ -3632,17 +3631,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);
+      // Always start with zero input channels.
+      AudioCallbackDriver* driver = new AudioCallbackDriver(this, 0);
       mDriver = driver;
     } else {
       mDriver = new SystemClockDriver(this);
     }
 
 #ifdef TRACING
     // This is a noop if the logger has not been enabled.
     gMSGTraceLogger.Start();
@@ -4161,17 +4161,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.