Bug 1404977 - Part 1 - Add a pref to force the output device by name, for testing. r?pehrsons
This is a bit unrelated, but I'm touching a bunch of stuff in the area and it
was in the same queue.
MozReview-Commit-ID: 6fgDqbf2Lnk
--- a/dom/media/CubebUtils.cpp
+++ b/dom/media/CubebUtils.cpp
@@ -29,16 +29,17 @@
#include "GeneratedJNIWrappers.h"
#endif
#define AUDIOIPC_POOL_SIZE_DEFAULT 1
#define AUDIOIPC_STACK_SIZE_DEFAULT (64*1024)
#define PREF_VOLUME_SCALE "media.volume_scale"
#define PREF_CUBEB_BACKEND "media.cubeb.backend"
+#define PREF_CUBEB_OUTPUT_DEVICE "media.cubeb.output_device"
#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"
@@ -135,16 +136,17 @@ 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;
+StaticAutoPtr<char> sCubebOutputDeviceName;
const char kBrandBundleURL[] = "chrome://branding/locale/brand.properties";
const char* AUDIOSTREAM_BACKEND_ID_STR[] = {
"jack",
"pulse",
"alsa",
"audiounit",
@@ -181,16 +183,29 @@ uint32_t sPreferredSampleRate;
static const uint32_t CUBEB_NORMAL_LATENCY_MS = 100;
// Consevative default that can work on all platforms.
static const uint32_t CUBEB_NORMAL_LATENCY_FRAMES = 1024;
namespace CubebUtils {
cubeb* GetCubebContextUnlocked();
+void GetPrefAndSetString(const char* aPref, StaticAutoPtr<char>& aStorage)
+{
+ nsAutoCString value;
+ Preferences::GetCString(aPref, value);
+ if (value.IsEmpty()) {
+ aStorage = nullptr;
+ } else {
+ aStorage = new char[value.Length() + 1];
+ PodCopy(aStorage.get(), value.get(), value.Length());
+ aStorage[value.Length()] = 0;
+ }
+}
+
void PrefChanged(const char* aPref, void* aClosure)
{
if (strcmp(aPref, PREF_VOLUME_SCALE) == 0) {
nsAutoCString value;
Preferences::GetCString(aPref, value);
StaticMutexAutoLock lock(sMutex);
if (value.IsEmpty()) {
sVolumeScale = 1.0;
@@ -227,25 +242,21 @@ void PrefChanged(const char* aPref, void
} else if (value.EqualsLiteral("normal")) {
cubeb_set_log_callback(CUBEB_LOG_NORMAL, CubebLogCallback);
cubebLog->SetLevel(LogLevel::Error);
} else if (value.IsEmpty()) {
cubeb_set_log_callback(CUBEB_LOG_DISABLED, nullptr);
cubebLog->SetLevel(LogLevel::Disabled);
}
} else if (strcmp(aPref, PREF_CUBEB_BACKEND) == 0) {
- nsAutoCString value;
- Preferences::GetCString(aPref, value);
- if (value.IsEmpty()) {
- sCubebBackendName = nullptr;
- } else {
- sCubebBackendName = new char[value.Length() + 1];
- PodCopy(sCubebBackendName.get(), value.get(), value.Length());
- sCubebBackendName[value.Length()] = 0;
- }
+ StaticMutexAutoLock lock(sMutex);
+ GetPrefAndSetString(aPref, sCubebBackendName);
+ } else if (strcmp(aPref, PREF_CUBEB_OUTPUT_DEVICE) == 0) {
+ StaticMutexAutoLock lock(sMutex);
+ GetPrefAndSetString(aPref, sCubebOutputDeviceName);
} 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);
@@ -520,16 +531,17 @@ uint32_t GetCubebMSGLatencyInFrames(cube
}
return latency_frames;
#endif
}
void InitLibrary()
{
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_VOLUME_SCALE);
+ Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_OUTPUT_DEVICE);
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_LATENCY_MSG);
Preferences::RegisterCallback(PrefChanged, PREF_CUBEB_FORCE_SAMPLE_RATE);
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_BACKEND);
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_FORCE_NULL_CONTEXT);
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_CUBEB_SANDBOX);
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_AUDIOIPC_POOL_SIZE);
Preferences::RegisterCallbackAndCall(PrefChanged, PREF_AUDIOIPC_STACK_SIZE);
@@ -552,16 +564,17 @@ void InitLibrary()
InitAudioIPCConnection();
}
#endif
}
void ShutdownLibrary()
{
Preferences::UnregisterCallback(PrefChanged, PREF_VOLUME_SCALE);
+ Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_OUTPUT_DEVICE);
Preferences::UnregisterCallback(PrefChanged, PREF_AUDIOIPC_STACK_SIZE);
Preferences::UnregisterCallback(PrefChanged, PREF_AUDIOIPC_POOL_SIZE);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_SANDBOX);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_BACKEND);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_PLAYBACK);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_FORCE_SAMPLE_RATE);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LATENCY_MSG);
Preferences::UnregisterCallback(PrefChanged, PREF_CUBEB_LOGGING_LEVEL);
@@ -604,16 +617,22 @@ void GetCurrentBackend(nsAString& aBacke
if (backend) {
aBackend.AssignASCII(backend);
return;
}
}
aBackend.AssignLiteral("unknown");
}
+char* GetForcedOutputDevice()
+{
+ StaticMutexAutoLock lock(sMutex);
+ return sCubebOutputDeviceName;
+}
+
uint16_t ConvertCubebType(cubeb_device_type aType)
{
uint16_t map[] = {
nsIAudioDeviceInfo::TYPE_UNKNOWN, // CUBEB_DEVICE_TYPE_UNKNOWN
nsIAudioDeviceInfo::TYPE_INPUT, // CUBEB_DEVICE_TYPE_INPUT,
nsIAudioDeviceInfo::TYPE_OUTPUT // CUBEB_DEVICE_TYPE_OUTPUT
};
return map[aType];
--- a/dom/media/CubebUtils.h
+++ b/dom/media/CubebUtils.h
@@ -42,17 +42,17 @@ void ReportCubebStreamInitFailure(bool a
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();
-
+char* GetForcedOutputDevice();
#ifdef MOZ_WIDGET_ANDROID
uint32_t AndroidGetAudioOutputSampleRate();
uint32_t AndroidGetAudioOutputFramesPerBuffer();
#endif
} // namespace CubebUtils
} // namespace mozilla
#endif // CubebUtils_h_
--- a/dom/media/GraphDriver.cpp
+++ b/dom/media/GraphDriver.cpp
@@ -599,16 +599,31 @@ AudioCallbackDriver::Init()
if (!mFromFallback) {
CubebUtils::ReportCubebStreamInitFailure(true);
}
MonitorAutoLock lock(GraphImpl()->GetMonitor());
FallbackToSystemClockDriver();
return true;
}
+ cubeb_devid forcedOutputDeviceId = nullptr;
+ char* forcedOutputDeviceName = CubebUtils::GetForcedOutputDevice();
+ if (forcedOutputDeviceName) {
+ nsTArray<RefPtr<AudioDeviceInfo>> deviceInfos;
+ GetDeviceCollection(deviceInfos, CubebUtils::Output);
+ for (auto device : deviceInfos) {
+ const nsString& friendlyName = device->FriendlyName();
+ if (friendlyName.Equals(NS_ConvertUTF8toUTF16(forcedOutputDeviceName))) {
+ if (device->GetDeviceID().isSome()) {
+ forcedOutputDeviceId = device->GetDeviceID().ref();
+ }
+ }
+ }
+ }
+
cubeb_stream_params output;
cubeb_stream_params input;
bool firstStream = CubebUtils::GetFirstStream();
MOZ_ASSERT(!NS_IsMainThread(),
"This is blocking and should never run on the main thread.");
mSampleRate = output.rate = mGraphImpl->GraphRate();
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio_loopback.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio_loopback.html
@@ -16,17 +16,19 @@
* Run a test to verify the use of LoopbackTone as audio input.
*/
scriptsReady.then(() => runTestWhenReady(async () => {
let audioDevice = SpecialPowers.getCharPref("media.audio_loopback_dev", "");
if (!audioDevice) {
todo(false, "No loopback device set by framework. Try --use-test-media-devices");
return Promise.resolve();
}
-
+ await SpecialPowers.pushPrefEnv({"set": [
+ ["media.cubeb.output_device", audioDevice]
+ ]});
// At this point DefaultLoopbackTone has been instantiated
// automatically on frequency TEST_AUDIO_FREQ (440 Hz). Verify
// that a tone is detected on that frequency.
info("Capturing at default frequency");
let stream = await getUserMedia({audio: true});
let audioContext = new AudioContext();
let analyser = new AudioStreamAnalyser(audioContext, stream);
@@ -81,14 +83,13 @@
const freg_50Hz = array[analyser.binIndexForFrequency(50)];
const freq = array[analyser.binIndexForFrequency(TEST_AUDIO_FREQ)];
const freq_2000Hz = array[analyser.binIndexForFrequency(2000)];
info("Analysing audio frequency - low:target:high = "
+ freg_50Hz + ':' + freq + ':' + freq_2000Hz);
return freg_50Hz < 50 && freq < 50 && freq_2000Hz < 50;
})
- }))
- .then(() => finish())
+ }).then(() => finish()))
</script>
</pre>
</body>
</html>