Bug 1245216: Fix getUserMedia input in full_duplex mode coming from the wrong place r?padenot draft
authorRandell Jesup <rjesup@jesup.org>
Thu, 04 Feb 2016 12:40:06 -0500
changeset 328917 0ef2d242d4aa82ca79286175056c1d69898eb713
parent 328916 a5a434968966147b40741b170f5aab80ce8e78b6
child 328918 b2a56a553cf70301d21127c8b9a83d939a739fa7
push id10433
push userrjesup@wgate.com
push dateThu, 04 Feb 2016 17:45:17 +0000
reviewerspadenot
bugs1245216
milestone47.0a1
Bug 1245216: Fix getUserMedia input in full_duplex mode coming from the wrong place r?padenot Also cleanup of an leftover overrridden interface, and re-add a line lost in merges
dom/media/GraphDriver.cpp
dom/media/GraphDriver.h
dom/media/MediaStreamGraph.cpp
dom/media/webrtc/MediaEngineWebRTCAudio.cpp
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -47,17 +47,16 @@ struct AutoProfilerUnregisterThread
   }
 };
 
 GraphDriver::GraphDriver(MediaStreamGraphImpl* aGraphImpl)
   : mIterationStart(0),
     mIterationEnd(0),
     mGraphImpl(aGraphImpl),
     mWaitState(WAITSTATE_RUNNING),
-    mAudioInput(nullptr),
     mCurrentTimeStamp(TimeStamp::Now()),
     mPreviousDriver(nullptr),
     mNextDriver(nullptr)
 { }
 
 void GraphDriver::SetGraphTime(GraphDriver* aPreviousDriver,
                                GraphTime aLastSwitchNextIterationStart,
                                GraphTime aLastSwitchNextIterationEnd)
@@ -541,16 +540,17 @@ StreamAndPromiseForOperation::StreamAndP
   , mOperation(aOperation)
 {
   // MOZ_ASSERT(aPromise);
 }
 
 AudioCallbackDriver::AudioCallbackDriver(MediaStreamGraphImpl* aGraphImpl)
   : GraphDriver(aGraphImpl)
   , mSampleRate(0)
+  , mInputChannels(1)
   , mIterationDurationMS(MEDIA_GRAPH_TARGET_PERIOD_MS)
   , mStarted(false)
   , mAudioInput(nullptr)
   , mAudioChannel(aGraphImpl->AudioChannel())
   , mAddedMixer(false)
   , mInCallback(false)
   , mMicrophoneActive(false)
 #ifdef XP_MACOSX
@@ -599,17 +599,17 @@ AudioCallbackDriver::Init()
   }
 
   if (cubeb_get_min_latency(CubebUtils::GetCubebContext(), output, &latency) != CUBEB_OK) {
     NS_WARNING("Could not get minimal latency from cubeb.");
     return;
   }
 
   input = output;
-  input.channels = 1; // change to support optional stereo capture
+  input.channels = mInputChannels; // change to support optional stereo capture
 
   cubeb_stream* stream;
   // 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.
   if (cubeb_stream_init(CubebUtils::GetCubebContext(), &stream,
                         "AudioCallbackDriver",
                         mGraphImpl->mInputDeviceID,
@@ -924,17 +924,17 @@ AudioCallbackDriver::DataCallback(const 
   mGraphImpl->NotifyOutputData(aOutputBuffer, static_cast<size_t>(aFrames),
                                mSampleRate, ChannelCount);
 
   // Process mic data if any/needed -- after inserting far-end data for AEC!
   if (aInputBuffer) {
     if (mAudioInput) { // for this specific input-only or full-duplex stream
       mAudioInput->NotifyInputData(mGraphImpl, aInputBuffer,
                                    static_cast<size_t>(aFrames),
-                                   mSampleRate, ChannelCount);
+                                   mSampleRate, mInputChannels);
     }
   }
 
   bool switching = false;
   {
     MonitorAutoLock mon(mGraphImpl->GetMonitor());
     switching = !!NextDriver();
   }
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -192,25 +192,16 @@ public:
   void EnsureNextIterationLocked();
 
   MediaStreamGraphImpl* GraphImpl() {
     return mGraphImpl;
   }
 
   virtual bool OnThread() = 0;
 
-  // These are invoked on the MSG thread (or MainThread in shutdown)
-  virtual void SetInputListener(AudioDataListener *aListener) {
-    mAudioInput = aListener;
-  }
-  // XXX do we need the param?  probably no
-  virtual void RemoveInputListener(AudioDataListener *aListener) {
-    mAudioInput = nullptr;
-  }
-
 protected:
   GraphTime StateComputedTime() const;
 
   // Time of the start of this graph iteration. This must be accessed while
   // having the monitor.
   GraphTime mIterationStart;
   // Time of the end of this graph iteration. This must be accessed while having
   // the monitor.
@@ -231,19 +222,16 @@ protected:
     WAITSTATE_WAITING_INDEFINITELY,
     // Something has signaled RunThread() to wake up immediately,
     // but it hasn't done so yet
     WAITSTATE_WAKING_UP
   };
   // This must be access with the monitor.
   WaitState mWaitState;
 
-  // Callback for mic data, if any
-  AudioDataListener *mAudioInput;
-
   // This is used on the main thread (during initialization), and the graph
   // thread. No monitor needed because we know the graph thread does not run
   // during the initialization.
   TimeStamp mCurrentTimeStamp;
   // This is non-null only when this driver has recently switched from an other
   // driver, and has not cleaned it up yet (for example because the audio stream
   // is currently calling the callback during initialization).
   //
@@ -416,16 +404,27 @@ public:
   /* 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(OnThread());
+    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;
   }
 
   /* Enqueue a promise that is going to be resolved when a specific operation
    * occurs on the cubeb stream. */
   void EnqueueStreamAndPromiseForOperation(MediaStream* aStream,
                                          void* aPromise,
@@ -481,16 +480,19 @@ private:
    * callback thread. */
   AudioCallbackBufferWrapper<AudioDataValue, ChannelCount> 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;
   /* 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.
--- a/dom/media/MediaStreamGraph.cpp
+++ b/dom/media/MediaStreamGraph.cpp
@@ -939,16 +939,17 @@ MediaStreamGraphImpl::OpenAudioInputImpl
   // we close cubeb.
   mInputDeviceID = aID;
   mAudioInputs.AppendElement(aListener); // always monitor speaker data
 
   // Switch Drivers since we're adding input (to input-only or full-duplex)
   MonitorAutoLock mon(mMonitor);
   if (mLifecycleState == LIFECYCLE_RUNNING) {
     AudioCallbackDriver* driver = new AudioCallbackDriver(this);
+    driver->SetInputListener(aListener);
     CurrentDriver()->SwitchAtNextIteration(driver);
   }
 }
 
 nsresult
 MediaStreamGraphImpl::OpenAudioInput(CubebUtils::AudioDeviceID aID,
                                      AudioDataListener *aListener)
 {
@@ -978,17 +979,20 @@ MediaStreamGraphImpl::OpenAudioInput(Cub
   return NS_OK;
 }
 
 void
 MediaStreamGraphImpl::CloseAudioInputImpl(AudioDataListener *aListener)
 {
   mInputDeviceID = nullptr;
   mInputWanted = false;
-  CurrentDriver()->RemoveInputListener(aListener);
+  AudioCallbackDriver *driver = CurrentDriver()->AsAudioCallbackDriver();
+  if (driver) {
+    driver->RemoveInputListener(aListener);
+  }
   mAudioInputs.RemoveElement(aListener);
 
   // Switch Drivers since we're adding or removing an input (to nothing/system or output only)
   bool audioTrackPresent = false;
   for (uint32_t i = 0; i < mStreams.Length(); ++i) {
     MediaStream* stream = mStreams[i];
     // If this is a AudioNodeStream, force a AudioCallbackDriver.
     if (stream->AsAudioNodeStream()) {
--- a/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
+++ b/dom/media/webrtc/MediaEngineWebRTCAudio.cpp
@@ -369,25 +369,27 @@ MediaEngineWebRTCMicrophoneSource::Start
   // Register output observer
   // XXX
   MOZ_ASSERT(gFarendObserver);
   gFarendObserver->Clear();
 
   if (mVoEBase->StartReceive(mChannel)) {
     return NS_ERROR_FAILURE;
   }
+
+  // Must be *before* StartSend() so it will notice we selected external input (full_duplex)
+  mAudioInput->StartRecording(aStream->Graph(), mListener);
+
   if (mVoEBase->StartSend(mChannel)) {
     return NS_ERROR_FAILURE;
   }
 
   // Attach external media processor, so this::Process will be called.
   mVoERender->RegisterExternalMediaProcessing(mChannel, webrtc::kRecordingPerChannel, *this);
 
-  mAudioInput->StartRecording(aStream->Graph(), mListener);
-
   return NS_OK;
 }
 
 nsresult
 MediaEngineWebRTCMicrophoneSource::Stop(SourceMediaStream *aSource, TrackID aID)
 {
   AssertIsOnOwningThread();
   {