Bug 1404977 - Part 1 - Add a pref to force the output device by name, for testing. r?pehrsons draft
authorPaul Adenot <paul@paul.cx>
Tue, 17 Apr 2018 16:45:33 +0200
changeset 798178 059af9627c21c9183eeac842694f83c6eedc05ec
parent 798177 ba4ce98ebac5545372dd9ef98e25d6cb763d0566
child 798179 cc2dc1608883e03bbfaa650bc4549245296ac2db
push id110687
push userachronop@gmail.com
push dateTue, 22 May 2018 14:13:17 +0000
reviewerspehrsons
bugs1404977
milestone62.0a1
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: CBAmQKDq36z
dom/media/CubebUtils.cpp
dom/media/CubebUtils.h
dom/media/GraphDriver.cpp
dom/media/tests/mochitest/test_getUserMedia_basicAudio_loopback.html
--- 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 (const 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();
@@ -678,17 +693,17 @@ AudioCallbackDriver::Init()
          ) &&
         // 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.
         cubeb_stream_init(cubebContext, &stream,
                           "AudioCallbackDriver",
                           input_id,
                           mGraphImpl->mInputWanted ? &input : nullptr,
-                          output_id,
+                          forcedOutputDeviceId ? forcedOutputDeviceId : output_id,
                           mGraphImpl->mOutputWanted ? &output : nullptr, latency_frames,
                           DataCallback_s, StateCallback_s, this) == CUBEB_OK) {
       mAudioStream.own(stream);
       DebugOnly<int> rv = cubeb_stream_set_volume(mAudioStream, CubebUtils::GetVolumeScale());
       NS_WARNING_ASSERTION(
         rv == CUBEB_OK,
         "Could not set the audio stream volume in GraphDriver.cpp");
       CubebUtils::ReportCubebBackendUsed();
--- a/dom/media/tests/mochitest/test_getUserMedia_basicAudio_loopback.html
+++ b/dom/media/tests/mochitest/test_getUserMedia_basicAudio_loopback.html
@@ -81,14 +81,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>