Bug 1277037 - Make AudioCallbackDriver::StartStream fallible. r?jesup draft
authorRalph Giles <giles@mozilla.com>
Wed, 11 Jan 2017 11:51:23 -0800
changeset 459350 63b449637984e347e4de350f3702840d12d1547d
parent 459259 63ad56438630b389efdb01632e153143cf3008ec
child 541868 e9a2b9471a62abcccb552708867f81bb620a7116
push id41193
push userbmo:giles@thaumas.net
push dateWed, 11 Jan 2017 19:55:00 +0000
reviewersjesup
bugs1277037
milestone53.0a1
Bug 1277037 - Make AudioCallbackDriver::StartStream fallible. r?jesup Avoid crashing in the case that cubeb stream start fails and report an error instead. MozReview-Commit-ID: 75M392POyHo
dom/media/GraphDriver.cpp
dom/media/GraphDriver.h
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -510,17 +510,19 @@ AsyncCubebTask::EnsureThread()
 NS_IMETHODIMP
 AsyncCubebTask::Run()
 {
   MOZ_ASSERT(mDriver);
 
   switch(mOperation) {
     case AsyncCubebOperation::INIT: {
       LIFECYCLE_LOG("AsyncCubebOperation::INIT driver=%p\n", mDriver.get());
-      mDriver->Init();
+      if (!mDriver->Init()) {
+        return NS_ERROR_FAILURE;
+      }
       mDriver->CompleteAudioContextOperations(mOperation);
       break;
     }
     case AsyncCubebOperation::SHUTDOWN: {
       LIFECYCLE_LOG("AsyncCubebOperation::SHUTDOWN driver=%p\n", mDriver.get());
       mDriver->Stop();
 
       mDriver->CompleteAudioContextOperations(mOperation);
@@ -589,26 +591,26 @@ bool IsMacbookOrMacbookAir()
       }
     }
     return false;
   }
 #endif
   return false;
 }
 
-void
+bool
 AudioCallbackDriver::Init()
 {
   cubeb* cubebContext = CubebUtils::GetCubebContext();
   if (!cubebContext) {
     NS_WARNING("Could not get cubeb context.");
     if (!mFromFallback) {
       CubebUtils::ReportCubebStreamInitFailure(true);
     }
-    return;
+    return false;
   }
 
   cubeb_stream_params output;
   cubeb_stream_params input;
   uint32_t latency_frames;
   bool firstStream = CubebUtils::GetFirstStream();
 
   MOZ_ASSERT(!NS_IsMainThread(),
@@ -619,17 +621,17 @@ AudioCallbackDriver::Init()
 #if defined(__ANDROID__)
 #if defined(MOZ_B2G)
   output.stream_type = CubebUtils::ConvertChannelToCubebType(mAudioChannel);
 #else
   output.stream_type = CUBEB_STREAM_TYPE_MUSIC;
 #endif
   if (output.stream_type == CUBEB_STREAM_TYPE_MAX) {
     NS_WARNING("Bad stream type");
-    return;
+    return false;
   }
 #else
   (void)mAudioChannel;
 #endif
 
   output.channels = mGraphImpl->AudioChannelCount();
   if (AUDIO_OUTPUT_FORMAT == AUDIO_FORMAT_S16) {
     output.format = CUBEB_SAMPLE_S16NE;
@@ -638,17 +640,17 @@ AudioCallbackDriver::Init()
   }
 
   Maybe<uint32_t> latencyPref = CubebUtils::GetCubebMSGLatencyInFrames();
   if (latencyPref) {
     latency_frames = latencyPref.value();
   } else {
     if (cubeb_get_min_latency(cubebContext, output, &latency_frames) != CUBEB_OK) {
       NS_WARNING("Could not get minimal latency from cubeb.");
-      return;
+      return false;
     }
   }
 
   // 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);
   }
@@ -707,29 +709,33 @@ AudioCallbackDriver::Init()
       // Fall back to a driver using a normal thread.
       MonitorAutoLock lock(GraphImpl()->GetMonitor());
       SystemClockDriver* nextDriver = new SystemClockDriver(GraphImpl());
       SetNextDriver(nextDriver);
       nextDriver->MarkAsFallback();
       nextDriver->SetGraphTime(this, mIterationStart, mIterationEnd);
       mGraphImpl->SetCurrentDriver(nextDriver);
       nextDriver->Start();
-      return;
+      return true;
     }
   }
   bool aec;
   Unused << mGraphImpl->AudioTrackPresent(aec);
   SetMicrophoneActive(aec);
 
   cubeb_stream_register_device_changed_callback(mAudioStream,
                                                 AudioCallbackDriver::DeviceChangedCallback_s);
 
-  StartStream();
+  if (!StartStream()) {
+    STREAM_LOG(LogLevel::Warning, ("AudioCallbackDriver couldn't start stream."));
+    return false;
+  }
 
   STREAM_LOG(LogLevel::Debug, ("AudioCallbackDriver started."));
+  return true;
 }
 
 
 void
 AudioCallbackDriver::Destroy()
 {
   STREAM_LOG(LogLevel::Debug, ("AudioCallbackDriver destroyed."));
   mAudioInput = nullptr;
@@ -766,28 +772,30 @@ AudioCallbackDriver::Start()
 
   LIFECYCLE_LOG("Starting new audio driver off main thread, "
                 "to ensure it runs after previous shutdown.");
   RefPtr<AsyncCubebTask> initEvent =
     new AsyncCubebTask(AsAudioCallbackDriver(), AsyncCubebOperation::INIT);
   initEvent->Dispatch();
 }
 
-void
+bool
 AudioCallbackDriver::StartStream()
 {
   if (cubeb_stream_start(mAudioStream) != CUBEB_OK) {
-    MOZ_CRASH("Could not start cubeb stream for MSG.");
+    NS_WARNING("Could not start cubeb stream for MSG.");
+    return false;
   }
 
   {
     MonitorAutoLock mon(mGraphImpl->GetMonitor());
     mStarted = true;
     mWaitState = WAITSTATE_RUNNING;
   }
+  return true;
 }
 
 void
 AudioCallbackDriver::Stop()
 {
   if (cubeb_stream_stop(mAudioStream) != CUBEB_OK) {
     NS_WARNING("Could not stop cubeb stream for MSG.");
   }
--- a/dom/media/GraphDriver.h
+++ b/dom/media/GraphDriver.h
@@ -462,19 +462,19 @@ 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);
   /**
    * This is called when the output device used by the cubeb stream changes. */
   void DeviceChangedCallback();
   /* Start the cubeb stream */
-  void StartStream();
+  bool StartStream();
   friend class AsyncCubebTask;
-  void Init();
+  bool Init();
   /* MediaStreamGraphs are always down/up mixed to stereo for now. */
   static const uint32_t ChannelCount = 2;
   /* The size of this buffer comes from the fact that some audio backends can
    * call back with a number of frames lower than one block (128 frames), so we
    * need to keep at most two block in the SpillBuffer, because we always round
    * up to block boundaries during an iteration.
    * This is only ever accessed on the audio callback thread. */
   SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2, ChannelCount> mScratchBuffer;