Bug 1427011 - Disable default device switching in libcubeb's WASAPI backend. r?padenot draft
authorMatthew Gregan <kinetik@flim.org>
Sat, 28 Apr 2018 13:19:17 +1200
changeset 789311 95c56d7c7975a477df71198bf91f740449a37683
parent 789072 74a88bd1be7154d5a375c26b7c27370ad89c7d6d
push id108249
push usermgregan@mozilla.com
push dateSat, 28 Apr 2018 01:20:04 +0000
reviewerspadenot
bugs1427011
milestone61.0a1
Bug 1427011 - Disable default device switching in libcubeb's WASAPI backend. r?padenot MozReview-Commit-ID: 8aqb1yutRgD
dom/media/AudioStream.cpp
dom/media/CubebUtils.cpp
dom/media/CubebUtils.h
dom/media/GraphDriver.cpp
media/libcubeb/include/cubeb.h
media/libcubeb/src/cubeb_wasapi.cpp
media/libcubeb/update.sh
--- a/dom/media/AudioStream.cpp
+++ b/dom/media/AudioStream.cpp
@@ -347,17 +347,17 @@ AudioStream::Init(uint32_t aNumChannels,
 
   mDumpFile = OpenDumpFile(aNumChannels, aRate);
 
   cubeb_stream_params params;
   params.rate = aRate;
   params.channels = mOutChannels;
   params.layout = static_cast<uint32_t>(aChannelMap);
   params.format = ToCubebFormat<AUDIO_OUTPUT_FORMAT>::value;
-  params.prefs = CUBEB_STREAM_PREF_NONE;
+  params.prefs = CubebUtils::GetDefaultStreamPrefs();
 
   mAudioClock.Init(aRate);
 
   cubeb* cubebContext = CubebUtils::GetCubebContext();
   if (!cubebContext) {
     LOGE("Can't get cubeb context!");
     CubebUtils::ReportCubebStreamInitFailure(true);
     return NS_ERROR_DOM_MEDIA_CUBEB_INITIALIZATION_ERR;
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -37,16 +37,18 @@
 #define PREF_CUBEB_LATENCY_PLAYBACK "media.cubeb_latency_playback_ms"
 #define PREF_CUBEB_LATENCY_MSG "media.cubeb_latency_msg_frames"
 // Allows to get something non-default for the preferred sample-rate, to allow
 // troubleshooting in the field and testing.
 #define PREF_CUBEB_FORCE_SAMPLE_RATE "media.cubeb.force_sample_rate"
 #define PREF_CUBEB_LOGGING_LEVEL "media.cubeb.logging_level"
 // Hidden pref used by tests to force failure to obtain cubeb context
 #define PREF_CUBEB_FORCE_NULL_CONTEXT "media.cubeb.force_null_context"
+// Hidden pref to disable BMO 1427011 experiment; can be removed once proven.
+#define PREF_CUBEB_DISABLE_DEVICE_SWITCHING "media.cubeb.disable_device_switching"
 #define PREF_CUBEB_SANDBOX "media.cubeb.sandbox"
 #define PREF_AUDIOIPC_POOL_SIZE "media.audioipc.pool_size"
 #define PREF_AUDIOIPC_STACK_SIZE "media.audioipc.stack_size"
 
 #if (defined(XP_LINUX) && !defined(MOZ_WIDGET_ANDROID)) || defined(XP_MACOSX)
 #define MOZ_CUBEB_REMOTING
 #endif
 
@@ -125,16 +127,17 @@ uint32_t sCubebMSGLatencyInFrames = 512;
 // If sCubebForcedSampleRate is zero, PreferredSampleRate will return the
 // preferred sample-rate for the audio backend in use. Otherwise, it will be
 // used as the preferred sample-rate.
 uint32_t sCubebForcedSampleRate = 0;
 bool sCubebPlaybackLatencyPrefSet = false;
 bool sCubebMSGLatencyPrefSet = false;
 bool sAudioStreamInitEverSucceeded = false;
 bool sCubebForceNullContext = false;
+bool sCubebDisableDeviceSwitching = true;
 #ifdef MOZ_CUBEB_REMOTING
 bool sCubebSandbox = false;
 size_t sAudioIPCPoolSize;
 size_t sAudioIPCStackSize;
 #endif
 StaticAutoPtr<char> sBrandName;
 StaticAutoPtr<char> sCubebBackendName;
 
@@ -238,17 +241,22 @@ void PrefChanged(const char* aPref, void
       PodCopy(sCubebBackendName.get(), value.get(), value.Length());
       sCubebBackendName[value.Length()] = 0;
     }
   } else if (strcmp(aPref, PREF_CUBEB_FORCE_NULL_CONTEXT) == 0) {
     StaticMutexAutoLock lock(sMutex);
     sCubebForceNullContext = Preferences::GetBool(aPref, false);
     MOZ_LOG(gCubebLog, LogLevel::Verbose,
             ("%s: %s", PREF_CUBEB_FORCE_NULL_CONTEXT, sCubebForceNullContext ? "true" : "false"));
-  }
+  } else if (strcmp(aPref, PREF_CUBEB_DISABLE_DEVICE_SWITCHING) == 0) {
+    StaticMutexAutoLock lock(sMutex);
+    sCubebDisableDeviceSwitching = Preferences::GetBool(aPref, true);
+    MOZ_LOG(gCubebLog, LogLevel::Verbose,
+            ("%s: %s", PREF_CUBEB_DISABLE_DEVICE_SWITCHING, sCubebDisableDeviceSwitching ? "true" : "false"));
+ }
 #ifdef MOZ_CUBEB_REMOTING
   else if (strcmp(aPref, PREF_CUBEB_SANDBOX) == 0) {
     StaticMutexAutoLock lock(sMutex);
     sCubebSandbox = Preferences::GetBool(aPref);
     MOZ_LOG(gCubebLog, LogLevel::Verbose, ("%s: %s", PREF_CUBEB_SANDBOX, sCubebSandbox ? "true" : "false"));
 
     if (sCubebSandbox && !sServerHandle && XRE_IsParentProcess()) {
       MOZ_LOG(gCubebLog, LogLevel::Debug, ("Starting cubeb server..."));
@@ -689,16 +697,29 @@ void GetDeviceCollection(nsTArray<RefPtr
                               device.latency_lo);
         aDeviceInfos.AppendElement(info);
       }
     }
     cubeb_device_collection_destroy(context, &collection);
   }
 }
 
+cubeb_stream_prefs GetDefaultStreamPrefs()
+{
+#ifdef XP_WIN
+  // Investigation for bug 1427011 - if we're in E10S mode, rely on the
+  // AudioNotification IPC to detect device changes.
+  if (sCubebDisableDeviceSwitching &&
+      (XRE_IsE10sParentProcess() || XRE_IsContentProcess())) {
+    return CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING;
+  }
+#endif
+  return CUBEB_STREAM_PREF_NONE;
+}
+
 #ifdef MOZ_WIDGET_ANDROID
 uint32_t AndroidGetAudioOutputSampleRate()
 {
   int32_t sample_rate = java::GeckoAppShell::GetAudioOutputSampleRate();
   MOZ_ASSERT(sample_rate > 0);
   return sample_rate;
 }
 uint32_t AndroidGetAudioOutputFramesPerBuffer()
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -41,16 +41,17 @@ cubeb* GetCubebContext();
 void ReportCubebStreamInitFailure(bool aIsFirstStream);
 void ReportCubebBackendUsed();
 uint32_t GetCubebPlaybackLatencyInMilliseconds();
 uint32_t GetCubebMSGLatencyInFrames(cubeb_stream_params * params);
 bool CubebLatencyPrefSet();
 void GetCurrentBackend(nsAString& aBackend);
 void GetDeviceCollection(nsTArray<RefPtr<AudioDeviceInfo>>& aDeviceInfos,
                          Side aSide);
+cubeb_stream_prefs GetDefaultStreamPrefs();
 
 #ifdef MOZ_WIDGET_ANDROID
 uint32_t AndroidGetAudioOutputSampleRate();
 uint32_t AndroidGetAudioOutputFramesPerBuffer();
 #endif
 } // namespace CubebUtils
 } // namespace mozilla
 
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -622,17 +622,17 @@ AudioCallbackDriver::Init()
     return true;
   }
 
   mBuffer = AudioCallbackBufferWrapper<AudioDataValue>(mOutputChannels);
   mScratchBuffer = SpillBuffer<AudioDataValue, WEBAUDIO_BLOCK_SIZE * 2>(mOutputChannels);
 
   output.channels = mOutputChannels;
   output.layout = CUBEB_LAYOUT_UNDEFINED;
-  output.prefs = CUBEB_STREAM_PREF_NONE;
+  output.prefs = CubebUtils::GetDefaultStreamPrefs();
 
   uint32_t latency_frames = CubebUtils::GetCubebMSGLatencyInFrames(&output);
 
   // 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);
   }
--- a/media/libcubeb/include/cubeb.h
+++ b/media/libcubeb/include/cubeb.h
@@ -216,20 +216,23 @@ enum {
                          CHANNEL_FRONT_CENTER | CHANNEL_LOW_FREQUENCY |
                          CHANNEL_BACK_LEFT | CHANNEL_BACK_RIGHT |
                          CHANNEL_SIDE_LEFT | CHANNEL_SIDE_RIGHT,
 };
 
 /** Miscellaneous stream preferences. */
 typedef enum {
   CUBEB_STREAM_PREF_NONE     = 0x00, /**< No stream preferences are requested. */
-  CUBEB_STREAM_PREF_LOOPBACK = 0x01 /**< Request a loopback stream. Should be
-                                         specified on the input params and an
-                                         output device to loopback from should
-                                         be passed in place of an input device. */
+  CUBEB_STREAM_PREF_LOOPBACK = 0x01, /**< Request a loopback stream. Should be
+                                          specified on the input params and an
+                                          output device to loopback from should
+                                          be passed in place of an input device. */
+  CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING = 0x02, /**< Disable switching
+                                                          default device on OS
+                                                          changes. */
 } cubeb_stream_prefs;
 
 /** Stream format initialization parameters. */
 typedef struct {
   cubeb_sample_format format;   /**< Requested sample format.  One of
                                      #cubeb_sample_format. */
   uint32_t rate;                /**< Requested sample rate.  Valid range is [1000, 192000]. */
   uint32_t channels;            /**< Requested channel count.  Valid range is [1, 8]. */
--- a/media/libcubeb/src/cubeb_wasapi.cpp
+++ b/media/libcubeb/src/cubeb_wasapi.cpp
@@ -1829,21 +1829,26 @@ wasapi_stream_init(cubeb * context, cube
        assert that the lock is held in the function. */
     auto_lock lock(stm->stream_reset_lock);
     rv = setup_wasapi_stream(stm.get());
   }
   if (rv != CUBEB_OK) {
     return rv;
   }
 
-  HRESULT hr = register_notification_client(stm.get());
-  if (FAILED(hr)) {
-    /* this is not fatal, we can still play audio, but we won't be able
-       to keep using the default audio endpoint if it changes. */
-    LOG("failed to register notification client, %lx", hr);
+  if (!((input_stream_params ?
+         (input_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0) ||
+        (output_stream_params ?
+         (output_stream_params->prefs & CUBEB_STREAM_PREF_DISABLE_DEVICE_SWITCHING) : 0))) {
+    HRESULT hr = register_notification_client(stm.get());
+    if (FAILED(hr)) {
+      /* this is not fatal, we can still play audio, but we won't be able
+         to keep using the default audio endpoint if it changes. */
+      LOG("failed to register notification client, %lx", hr);
+    }
   }
 
   *stream = stm.release();
 
   LOG("Stream init succesfull (%p)", *stream);
   return CUBEB_OK;
 }
 
@@ -1879,17 +1884,19 @@ void wasapi_stream_destroy(cubeb_stream 
   // Only free stm->emergency_bailout if we could join the thread.
   // If we could not join the thread, stm->emergency_bailout is true
   // and is still alive until the thread wakes up and exits cleanly.
   if (stop_and_join_render_thread(stm)) {
     delete stm->emergency_bailout.load();
     stm->emergency_bailout = nullptr;
   }
 
-  unregister_notification_client(stm);
+  if (stm->notification_client) {
+    unregister_notification_client(stm);
+  }
 
   CloseHandle(stm->reconfigure_event);
   CloseHandle(stm->refill_event);
   CloseHandle(stm->input_available_event);
 
   // The variables intialized in wasapi_stream_init,
   // must be destroyed in wasapi_stream_destroy.
   stm->linear_input_buffer.reset();
--- a/media/libcubeb/update.sh
+++ b/media/libcubeb/update.sh
@@ -74,8 +74,12 @@ else
   echo "Remember to update README_MOZILLA with the version details."
 fi
 
 echo "Applying disable-assert.patch on top of $rev"
 patch -p3 < disable-assert.patch
 
 echo "Applying prefer-pulse-rust.patch on top of $rev"
 patch -p3 < prefer-pulse-rust.patch
+
+echo "Applying disable-device-switching.patch on top of $rev"
+patch -p3 < disable-device-switching.patch
+