Bug 1427011 - Disable default device switching in libcubeb's WASAPI backend. r?padenot
MozReview-Commit-ID: 8aqb1yutRgD
--- 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
+