From 976a75ed24f11e3211a98120b1ac57126e625d6c Mon Sep 17 00:00:00 2001
From: Roxie Linden <roxie@lindenlab.com>
Date: Mon, 18 Sep 2023 11:25:47 -0700
Subject: Fix voice device settings

---
 indra/llwebrtc/llwebrtc.cpp                  | 94 +++++++++++++++++++++-------
 indra/llwebrtc/llwebrtc_impl.h               |  3 +
 indra/newview/llpanelvoicedevicesettings.cpp | 79 ++++++++++++-----------
 3 files changed, 120 insertions(+), 56 deletions(-)

diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp
index edf941436e..96be2c1f0b 100644
--- a/indra/llwebrtc/llwebrtc.cpp
+++ b/indra/llwebrtc/llwebrtc.cpp
@@ -55,7 +55,7 @@ void LLWebRTCImpl::init()
     mSignalingThread->SetName("WebRTCSignalingThread", nullptr);
     mSignalingThread->Start();
 
-    mSignalingThread->PostTask(
+    mWorkerThread->PostTask(
         [this]()
         {
             mDeviceModule = webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio,
@@ -68,7 +68,7 @@ void LLWebRTCImpl::init()
 
 void LLWebRTCImpl::refreshDevices()
 {
-    mSignalingThread->PostTask([this]() { updateDevices(); });
+    mWorkerThread->PostTask([this]() { updateDevices(); });
 }
 
 void LLWebRTCImpl::setDevicesObserver(LLWebRTCDevicesObserver *observer) { mVoiceDevicesObserverList.emplace_back(observer); }
@@ -85,41 +85,48 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer)
 
 void LLWebRTCImpl::setCaptureDevice(const std::string &id)
 {
-    mSignalingThread->PostTask(
+    mWorkerThread->PostTask(
         [this, id]()
         {
+            mDeviceModule->StopRecording();
             int16_t captureDeviceCount = mDeviceModule->RecordingDevices();
             for (int16_t index = 0; index < captureDeviceCount; index++)
             {
                 char name[webrtc::kAdmMaxDeviceNameSize];
                 char guid[webrtc::kAdmMaxGuidSize];
                 mDeviceModule->RecordingDeviceName(index, name, guid);
-                if (id == guid || id == name)
+                if (id == guid || id == "Default")
                 {
+                    RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << index;
                     mDeviceModule->SetRecordingDevice(index);
                     break;
                 }
             }
+            mDeviceModule->InitRecording();
+            mDeviceModule->StartRecording();
         });
 }
 
 void LLWebRTCImpl::setRenderDevice(const std::string &id)
 {
-    mSignalingThread->PostTask(
+    mWorkerThread->PostTask(
         [this, id]()
         {
+            mDeviceModule->StopPlayout();
             int16_t renderDeviceCount = mDeviceModule->RecordingDevices();
             for (int16_t index = 0; index < renderDeviceCount; index++)
             {
                 char name[webrtc::kAdmMaxDeviceNameSize];
                 char guid[webrtc::kAdmMaxGuidSize];
                 mDeviceModule->PlayoutDeviceName(index, name, guid);
-                if (id == guid || id == name)
+                if (id == guid || id == "Default")
                 {
                     mDeviceModule->SetPlayoutDevice(index);
                     break;
                 }
             }
+            mDeviceModule->InitPlayout();
+            mDeviceModule->StartPlayout();
         });
 }
 
@@ -156,7 +163,7 @@ void LLWebRTCImpl::updateDevices()
 
 void LLWebRTCImpl::setTuningMode(bool enable)
 {
-    mSignalingThread->PostTask(
+    mWorkerThread->PostTask(
         [this, enable]()
         {
             if (enable)
@@ -181,7 +188,7 @@ void LLWebRTCImpl::OnCaptureData(const void    *audio_samples,
                                  const size_t   num_channels,
                                  const uint32_t samples_per_sec)
 {
-    if (bytes_per_sample != 4)
+    if (bytes_per_sample != 2)
     {
         return;
     }
@@ -220,28 +227,31 @@ void LLWebRTCImpl::unsetSignalingObserver(LLWebRTCSignalingObserver *observer)
     }
 }
 
+
 bool LLWebRTCImpl::initializeConnection()
 {
     RTC_DCHECK(!mPeerConnection);
-    RTC_DCHECK(!mPeerConnectionFactory);
+    RTC_DCHECK(mPeerConnectionFactory);
     mAnswerReceived        = false;
+
+    mSignalingThread->PostTask([this]() { initializeConnectionThreaded(); });
+    return true;
+}
+
+
+
+bool LLWebRTCImpl::initializeConnectionThreaded()
+{
     mPeerConnectionFactory = webrtc::CreatePeerConnectionFactory(mNetworkThread.get(),
                                                                  mWorkerThread.get(),
                                                                  mSignalingThread.get(),
-                                                                 nullptr /* default_adm */,
+                                                                 mDeviceModule,
                                                                  webrtc::CreateBuiltinAudioEncoderFactory(),
                                                                  webrtc::CreateBuiltinAudioDecoderFactory(),
                                                                  nullptr /* video_encoder_factory */,
                                                                  nullptr /* video_decoder_factory */,
                                                                  nullptr /* audio_mixer */,
                                                                  nullptr /* audio_processing */);
-
-    if (!mPeerConnectionFactory)
-    {
-        shutdownConnection();
-        return false;
-    }
-
     webrtc::PeerConnectionInterface::RTCConfiguration config;
     config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
     webrtc::PeerConnectionInterface::IceServer server;
@@ -272,16 +282,34 @@ bool LLWebRTCImpl::initializeConnection()
 
     cricket::AudioOptions audioOptions;
     audioOptions.auto_gain_control = true;
-    audioOptions.echo_cancellation = true;
+    audioOptions.echo_cancellation = false;  // incompatible with opus stereo
     audioOptions.noise_suppression = true;
 
     rtc::scoped_refptr<webrtc::MediaStreamInterface> stream = mPeerConnectionFactory->CreateLocalMediaStream("SLStream");
     rtc::scoped_refptr<webrtc::AudioTrackInterface>  audio_track(
-        mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(cricket::AudioOptions()).get()));
+        mPeerConnectionFactory->CreateAudioTrack("SLAudio", mPeerConnectionFactory->CreateAudioSource(audioOptions).get()));
     audio_track->set_enabled(true);
     stream->AddTrack(audio_track);
 
     mPeerConnection->AddTrack(audio_track, {"SLStream"});
+
+    auto senders = mPeerConnection->GetSenders();
+
+    for (auto &sender : senders)
+    {
+        webrtc::RtpParameters      params;
+        webrtc::RtpCodecParameters codecparam;
+        codecparam.name                       = "opus";
+        codecparam.kind                       = cricket::MEDIA_TYPE_AUDIO;
+        codecparam.clock_rate                 = 48000;
+        codecparam.num_channels               = 1;
+        codecparam.parameters["stereo"]       = "0";
+        codecparam.parameters["sprop-stereo"] = "0";
+
+        params.codecs.push_back(codecparam);
+        sender->SetParameters(params);
+    }
+
     mPeerConnection->SetLocalDescription(rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface>(this));
 
     RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state();
@@ -297,6 +325,12 @@ void LLWebRTCImpl::shutdownConnection()
 
 void LLWebRTCImpl::AnswerAvailable(const std::string &sdp)
 {
+    std::istringstream sdp_stream(sdp);
+    std::string        sdp_line;
+    while (std::getline(sdp_stream, sdp_line))
+    {
+        RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp_line;
+    }
     mSignalingThread->PostTask(
         [this, sdp]()
         {
@@ -515,10 +549,28 @@ void LLWebRTCImpl::OnSetLocalDescriptionComplete(webrtc::RTCError error)
     auto        desc = mPeerConnection->pending_local_description();
     std::string sdp;
     desc->ToString(&sdp);
-    RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp;
+    // mangle the sdp as this is the only way currently to bump up
+    // the send audio rate to 48k
+    std::istringstream sdp_stream(sdp);
+    std::ostringstream sdp_mangled_stream;
+    std::string        sdp_line;
+    while (std::getline(sdp_stream, sdp_line)) {
+        int bandwidth = 0;
+        int payload_id = 0;
+        RTC_LOG(LS_INFO) << __FUNCTION__ << " Local SDP: " << sdp_line;
+        // force mono
+        if (std::sscanf(sdp_line.c_str(), "a=rtpmap:%i opus/%i/2", &payload_id, &bandwidth) == 2)
+        {
+            sdp_mangled_stream << sdp_line << "\n";
+        }
+        else
+        {
+            sdp_mangled_stream << sdp_line << "\n";
+        }
+    }
     for (auto &observer : mSignalingObserverList)
     {
-        observer->OnOfferAvailable(sdp);
+        observer->OnOfferAvailable(sdp_mangled_stream.str());
     }
 }
 
diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h
index 5c6cfcdbc6..3f9b06cae7 100644
--- a/indra/llwebrtc/llwebrtc_impl.h
+++ b/indra/llwebrtc/llwebrtc_impl.h
@@ -160,6 +160,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface,
     void OnSetLocalDescriptionComplete(webrtc::RTCError error) override;
 
   protected:
+
+    bool                                                       initializeConnectionThreaded();
+
     std::unique_ptr<rtc::Thread>                               mNetworkThread;
     std::unique_ptr<rtc::Thread>                               mWorkerThread;
     std::unique_ptr<rtc::Thread>                               mSignalingThread;
diff --git a/indra/newview/llpanelvoicedevicesettings.cpp b/indra/newview/llpanelvoicedevicesettings.cpp
index 28631e2b7b..48c90d6856 100644
--- a/indra/newview/llpanelvoicedevicesettings.cpp
+++ b/indra/newview/llpanelvoicedevicesettings.cpp
@@ -236,44 +236,47 @@ void LLPanelVoiceDeviceSettings::refresh()
 		
 		if(mCtrlInputDevices)
 		{
-			mCtrlInputDevices->removeall();
-			mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
-
-			for(device=LLVoiceClient::getInstance()->getCaptureDevices().begin(); 
-				device != LLVoiceClient::getInstance()->getCaptureDevices().end();
-				device++)
-			{
-				mCtrlInputDevices->add(getLocalizedDeviceName(device->display_name), device->full_name, ADD_BOTTOM);
-			}
-
-			// Fix invalid input audio device preference.
-			if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE))
-			{
-				mCtrlInputDevices->setValue(DEFAULT_DEVICE);
-				gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE);
-				mInputDevice = DEFAULT_DEVICE;
-			}
+            LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getCaptureDevices();
+            if (devices.size() > 0) // if zero, we've not received our devices yet
+            {
+                mCtrlInputDevices->removeall();
+                mCtrlInputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
+                for (auto& device : devices)
+                {
+                    mCtrlInputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM);
+                }
+
+                // Fix invalid input audio device preference.
+                if (!mCtrlInputDevices->setSelectedByValue(mInputDevice, TRUE))
+                {
+                    mCtrlInputDevices->setValue(DEFAULT_DEVICE);
+                    gSavedSettings.setString("VoiceInputAudioDevice", DEFAULT_DEVICE);
+                    mInputDevice = DEFAULT_DEVICE;
+                }
+            }
 		}
 		
 		if(mCtrlOutputDevices)
 		{
-			mCtrlOutputDevices->removeall();
-			mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
-
-			for(device = LLVoiceClient::getInstance()->getRenderDevices().begin(); 
-				device !=  LLVoiceClient::getInstance()->getRenderDevices().end();
-                device++)
-			{
-                mCtrlOutputDevices->add(getLocalizedDeviceName(device->display_name), device->full_name, ADD_BOTTOM);
-			}
-
-			// Fix invalid output audio device preference.
-			if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE))
-			{
-				mCtrlOutputDevices->setValue(DEFAULT_DEVICE);
-				gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE);
-				mOutputDevice = DEFAULT_DEVICE;
-			}
+            LLVoiceDeviceList devices = LLVoiceClient::getInstance()->getRenderDevices();
+            if (devices.size() > 0)  // if zero, we've not received our devices yet
+            {
+                mCtrlOutputDevices->removeall();
+                mCtrlOutputDevices->add(getLocalizedDeviceName(DEFAULT_DEVICE), DEFAULT_DEVICE, ADD_BOTTOM);
+
+                for (auto& device : devices)
+                {
+                    mCtrlOutputDevices->add(getLocalizedDeviceName(device.display_name), device.full_name, ADD_BOTTOM);
+                }
+
+                // Fix invalid output audio device preference.
+                if (!mCtrlOutputDevices->setSelectedByValue(mOutputDevice, TRUE))
+                {
+                    mCtrlOutputDevices->setValue(DEFAULT_DEVICE);
+                    gSavedSettings.setString("VoiceOutputAudioDevice", DEFAULT_DEVICE);
+                    mOutputDevice = DEFAULT_DEVICE;
+                }
+            }
 		}
 	}	
 }
@@ -316,8 +319,11 @@ void LLPanelVoiceDeviceSettings::onCommitInputDevice()
 	if(LLVoiceClient::getInstance())
 	{
 		mInputDevice = mCtrlInputDevices->getValue().asString();
-		LLVoiceClient::getInstance()->setRenderDevice(mInputDevice);
+		LLVoiceClient::getInstance()->setCaptureDevice(mInputDevice);
 	}
+	// the preferences floater stuff is a mess, hence apply will never
+	// be called when 'ok' is pressed, so just force it for now.
+    apply();
 }
 
 void LLPanelVoiceDeviceSettings::onCommitOutputDevice()
@@ -328,6 +334,9 @@ void LLPanelVoiceDeviceSettings::onCommitOutputDevice()
 		mOutputDevice = mCtrlOutputDevices->getValue().asString(); 
 		LLVoiceClient::getInstance()->setRenderDevice(mOutputDevice);
 	}
+    // the preferences floater stuff is a mess, hence apply will never
+    // be called when 'ok' is pressed, so just force it for now.
+    apply();
 }
 
 void LLPanelVoiceDeviceSettings::onOutputDevicesClicked()
-- 
cgit v1.2.3