[WebRTC] Rework device handling sequence so that we can handle unplugging/re-plugging devices (#4593)
* [WebRTC] Rework device handling sequence so that we can handle unplugging/re-plugging devices
The device handling was not processing device updates in the proper sequence as
things like AEC use both input and output devices. Devices like headsets are both
so unplugging them resulted in various mute conditions and sometimes even a crash.
Now, we update both capture and render devices at once in the proper sequence.
Test Guidance:
* Bring two users in the same place in webrtc regions.
* The 'listening' one should have a headset or something set oas 'Default'
* Press 'talk' on one, and verify the other can hear.
* Unplug the headset from the listening one.
* Validate that audio changes from the headset to the speakers.
* Plug the headset back in.
* Validate that audio changes from speakers to headset.
* Do the same type of test with the headset viewer talking.
* The microphone used should switch from the headset to the computer (it should have one)
Do other various device tests, such as setting devices explicitly, messing with the device selector, etc.
* Fix race condition when multiple change device requests might come in at once
* Update to m137
The primary feature of this commit is to update libwebrtc from m114
to m137. This is needed to make webrtc buildable, as m114 is not buildable
by the current toolset.
m137 had some changes to the API, which required renaming or changing namespace
of some of the calls.
Additionally, this PR moves from a callback mechanism for gathering the energy
levels for tuning to a wrapper AudioDeviceModule, which gives us more control
over the audio stream.
Finally, the new m137-based webrtc has been updated to allow for 192khz audio
streams.
* Properly pass the observer setting into the inner audio device module
* Update to m137 and get rid of some noise
This change updates to m137 from m114, which required a few API changes.
Additionally, this fixes the hiss that happens shortly after someone unmutes: https://github.com/secondlife/server/issues/2094
There was also an issue with a slight amount of repeated after unmuting if there was audio right before unmuting. This is because
the audio processing and buffering still had audio from the previous speaking session. Now, we inject nearly a half second of
silence into the audio buffers/processor after unmuting to flush things.
* Install nsis on windows
* Use the newer digital AGC pipeline
m137 improved the AGC pipeline and the existing analog style is going away
so move to the new digital pipeline.
Also, some tweaking for audio levels so that we don't see inworld bars when tuning,
so one's own bars seem a reasonable size, etc.
* Install NSIS during windows sisgning and package build step
* Try pinning the packaging to windows 2022 to deal with missing nsis
* Adjust gain calculation and audio level calculations for tuning and peer connections
* Update with mac universal webrtc build
* Tuning of voice indicators for both tuning mode and inworld for self.
* Redo device deployment to handle cases where multiple deploy requests pile up
Also, mute when leaving webrtc-enabled regions or parcels,
and unmute when voice comes back.
* pre commit issue
master
parent
59e99b2aca
commit
d070b996bd
|
|
@ -93,7 +93,6 @@ jobs:
|
||||||
uses: actions/setup-python@v6
|
uses: actions/setup-python@v6
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
- name: Checkout build variables
|
- name: Checkout build variables
|
||||||
uses: actions/checkout@v5
|
uses: actions/checkout@v5
|
||||||
with:
|
with:
|
||||||
|
|
|
||||||
|
|
@ -2996,11 +2996,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>3570b6442d472cd97bad8622c2ec2571d72218a0</string>
|
<string>43c5f93517794aeade550e4266b959d1f0cfcb7f</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>sha1</string>
|
<string>sha1</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.72-test/webrtc-m114.5735.08.72-test.10444682919-darwin64-10444682919.tar.zst</string>
|
<string>https://github.com/secondlife/3p-webrtc-build/releases/download/m137.7151.04.20-universal/webrtc-m137.7151.04.20-universal.17630578914-darwin64-17630578914.tar.zst</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>darwin64</string>
|
<string>darwin64</string>
|
||||||
|
|
@ -3010,11 +3010,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>eadf6aa99313940ded11801d42c11375669f1628</string>
|
<string>efc5b176d878cfc16b8f82445d82ddb96815b6ab</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>sha1</string>
|
<string>sha1</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.72-test/webrtc-m114.5735.08.72-test.10444682919-linux64-10444682919.tar.zst</string>
|
<string>https://github.com/secondlife/3p-webrtc-build/releases/download/m137.7151.04.20-universal/webrtc-m137.7151.04.20-universal.17630578914-linux64-17630578914.tar.zst</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>linux64</string>
|
<string>linux64</string>
|
||||||
|
|
@ -3024,11 +3024,11 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>archive</key>
|
<key>archive</key>
|
||||||
<map>
|
<map>
|
||||||
<key>hash</key>
|
<key>hash</key>
|
||||||
<string>0081fd35290adbc8e66dd366535fb6cd8a966f1e</string>
|
<string>1e36f100de32c7c71325497a672fb1659b3f206d</string>
|
||||||
<key>hash_algorithm</key>
|
<key>hash_algorithm</key>
|
||||||
<string>sha1</string>
|
<string>sha1</string>
|
||||||
<key>url</key>
|
<key>url</key>
|
||||||
<string>https://github.com/secondlife/3p-webrtc-build/releases/download/m114.5735.08.72-test/webrtc-m114.5735.08.72-test.10444682919-windows64-10444682919.tar.zst</string>
|
<string>https://github.com/secondlife/3p-webrtc-build/releases/download/m137.7151.04.20-universal/webrtc-m137.7151.04.20-universal.17630578914-windows64-17630578914.tar.zst</string>
|
||||||
</map>
|
</map>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>windows64</string>
|
<string>windows64</string>
|
||||||
|
|
@ -3041,7 +3041,7 @@ Copyright (c) 2012, 2014, 2015, 2016 nghttp2 contributors</string>
|
||||||
<key>copyright</key>
|
<key>copyright</key>
|
||||||
<string>Copyright (c) 2011, The WebRTC project authors. All rights reserved.</string>
|
<string>Copyright (c) 2011, The WebRTC project authors. All rights reserved.</string>
|
||||||
<key>version</key>
|
<key>version</key>
|
||||||
<string>m114.5735.08.72-test.10444682919</string>
|
<string>m137.7151.04.20-universal.17630578914</string>
|
||||||
<key>name</key>
|
<key>name</key>
|
||||||
<string>webrtc</string>
|
<string>webrtc</string>
|
||||||
<key>vcs_branch</key>
|
<key>vcs_branch</key>
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ if (WINDOWS)
|
||||||
iphlpapi
|
iphlpapi
|
||||||
libcmt)
|
libcmt)
|
||||||
# as the webrtc libraries are release, build this binary as release as well.
|
# as the webrtc libraries are release, build this binary as release as well.
|
||||||
target_compile_options(llwebrtc PRIVATE "/MT")
|
target_compile_options(llwebrtc PRIVATE "/MT" "/Zc:wchar_t")
|
||||||
if (USE_BUGSPLAT)
|
if (USE_BUGSPLAT)
|
||||||
set_target_properties(llwebrtc PROPERTIES PDB_OUTPUT_DIRECTORY "${SYMBOLS_STAGING_DIR}")
|
set_target_properties(llwebrtc PROPERTIES PDB_OUTPUT_DIRECTORY "${SYMBOLS_STAGING_DIR}")
|
||||||
endif (USE_BUGSPLAT)
|
endif (USE_BUGSPLAT)
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
|
@ -159,7 +159,10 @@ class LLWebRTCDeviceInterface
|
||||||
virtual void setTuningMode(bool enable) = 0;
|
virtual void setTuningMode(bool enable) = 0;
|
||||||
virtual float getTuningAudioLevel() = 0; // for use during tuning
|
virtual float getTuningAudioLevel() = 0; // for use during tuning
|
||||||
virtual float getPeerConnectionAudioLevel() = 0; // for use when not tuning
|
virtual float getPeerConnectionAudioLevel() = 0; // for use when not tuning
|
||||||
virtual void setPeerConnectionGain(float gain) = 0;
|
virtual void setMicGain(float gain) = 0;
|
||||||
|
virtual void setTuningMicGain(float gain) = 0;
|
||||||
|
|
||||||
|
virtual void setMute(bool mute, int delay_ms = 0) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
// LLWebRTCAudioInterface provides the viewer with a way
|
// LLWebRTCAudioInterface provides the viewer with a way
|
||||||
|
|
|
||||||
|
|
@ -54,12 +54,12 @@
|
||||||
#include "rtc_base/ref_counted_object.h"
|
#include "rtc_base/ref_counted_object.h"
|
||||||
#include "rtc_base/ssl_adapter.h"
|
#include "rtc_base/ssl_adapter.h"
|
||||||
#include "rtc_base/thread.h"
|
#include "rtc_base/thread.h"
|
||||||
|
#include "rtc_base/logging.h"
|
||||||
#include "api/peer_connection_interface.h"
|
#include "api/peer_connection_interface.h"
|
||||||
#include "api/media_stream_interface.h"
|
#include "api/media_stream_interface.h"
|
||||||
#include "api/create_peerconnection_factory.h"
|
#include "api/create_peerconnection_factory.h"
|
||||||
#include "modules/audio_device/include/audio_device.h"
|
#include "modules/audio_device/include/audio_device.h"
|
||||||
#include "modules/audio_device/include/audio_device_data_observer.h"
|
#include "modules/audio_device/include/audio_device_data_observer.h"
|
||||||
#include "rtc_base/task_queue.h"
|
|
||||||
#include "api/task_queue/task_queue_factory.h"
|
#include "api/task_queue/task_queue_factory.h"
|
||||||
#include "api/task_queue/default_task_queue_factory.h"
|
#include "api/task_queue/default_task_queue_factory.h"
|
||||||
#include "modules/audio_device/include/audio_device_defines.h"
|
#include "modules/audio_device/include/audio_device_defines.h"
|
||||||
|
|
@ -69,35 +69,30 @@ namespace llwebrtc
|
||||||
|
|
||||||
class LLWebRTCPeerConnectionImpl;
|
class LLWebRTCPeerConnectionImpl;
|
||||||
|
|
||||||
class LLWebRTCLogSink : public rtc::LogSink {
|
class LLWebRTCLogSink : public webrtc::LogSink
|
||||||
|
{
|
||||||
public:
|
public:
|
||||||
LLWebRTCLogSink(LLWebRTCLogCallback* callback) :
|
LLWebRTCLogSink(LLWebRTCLogCallback* callback) : mCallback(callback) {}
|
||||||
mCallback(callback)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
// Destructor: close the log file
|
// Destructor: close the log file
|
||||||
~LLWebRTCLogSink() override
|
~LLWebRTCLogSink() override {}
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void OnLogMessage(const std::string& msg,
|
void OnLogMessage(const std::string& msg, webrtc::LoggingSeverity severity) override
|
||||||
rtc::LoggingSeverity severity) override
|
|
||||||
{
|
{
|
||||||
if (mCallback)
|
if (mCallback)
|
||||||
{
|
{
|
||||||
switch(severity)
|
switch (severity)
|
||||||
{
|
{
|
||||||
case rtc::LS_VERBOSE:
|
case webrtc::LS_VERBOSE:
|
||||||
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
||||||
break;
|
break;
|
||||||
case rtc::LS_INFO:
|
case webrtc::LS_INFO:
|
||||||
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
||||||
break;
|
break;
|
||||||
case rtc::LS_WARNING:
|
case webrtc::LS_WARNING:
|
||||||
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
||||||
break;
|
break;
|
||||||
case rtc::LS_ERROR:
|
case webrtc::LS_ERROR:
|
||||||
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
mCallback->LogMessage(LLWebRTCLogCallback::LOG_LEVEL_VERBOSE, msg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|
@ -118,73 +113,307 @@ private:
|
||||||
LLWebRTCLogCallback* mCallback;
|
LLWebRTCLogCallback* mCallback;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Implements a class allowing capture of audio data
|
// -----------------------------------------------------------------------------
|
||||||
// to determine audio level of the microphone.
|
// A proxy transport that forwards capture data to two AudioTransport sinks:
|
||||||
class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver
|
// - the "engine" (libwebrtc's VoiceEngine)
|
||||||
|
// - the "user" (your app's listener)
|
||||||
|
//
|
||||||
|
// Playout (NeedMorePlayData) goes only to the engine by default to avoid
|
||||||
|
// double-writing into the output buffer. See notes below if you want a tap.
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
class LLWebRTCAudioTransport : public webrtc::AudioTransport
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LLAudioDeviceObserver();
|
LLWebRTCAudioTransport();
|
||||||
|
|
||||||
// Retrieve the RMS audio loudness
|
void SetEngineTransport(webrtc::AudioTransport* t);
|
||||||
float getMicrophoneEnergy();
|
|
||||||
|
|
||||||
// Data retrieved from the caputure device is
|
// -------- Capture path: fan out to both sinks --------
|
||||||
// passed in here for processing.
|
int32_t RecordedDataIsAvailable(const void* audio_data,
|
||||||
void OnCaptureData(const void *audio_samples,
|
size_t number_of_samples,
|
||||||
const size_t num_samples,
|
size_t bytes_per_sample,
|
||||||
const size_t bytes_per_sample,
|
size_t number_of_channels,
|
||||||
const size_t num_channels,
|
uint32_t samples_per_sec,
|
||||||
const uint32_t samples_per_sec) override;
|
uint32_t total_delay_ms,
|
||||||
|
int32_t clock_drift,
|
||||||
|
uint32_t current_mic_level,
|
||||||
|
bool key_pressed,
|
||||||
|
uint32_t& new_mic_level) override;
|
||||||
|
|
||||||
// This is for data destined for the render device.
|
// -------- Playout path: delegate to engine only --------
|
||||||
// not currently used.
|
int32_t NeedMorePlayData(size_t number_of_samples,
|
||||||
void OnRenderData(const void *audio_samples,
|
size_t bytes_per_sample,
|
||||||
const size_t num_samples,
|
size_t number_of_channels,
|
||||||
const size_t bytes_per_sample,
|
uint32_t samples_per_sec,
|
||||||
const size_t num_channels,
|
void* audio_data,
|
||||||
const uint32_t samples_per_sec) override;
|
size_t& number_of_samples_out,
|
||||||
|
int64_t* elapsed_time_ms,
|
||||||
|
int64_t* ntp_time_ms) override;
|
||||||
|
|
||||||
protected:
|
// Method to pull mixed render audio data from all active VoE channels.
|
||||||
|
// The data will not be passed as reference for audio processing internally.
|
||||||
|
void PullRenderData(int bits_per_sample,
|
||||||
|
int sample_rate,
|
||||||
|
size_t number_of_channels,
|
||||||
|
size_t number_of_frames,
|
||||||
|
void* audio_data,
|
||||||
|
int64_t* elapsed_time_ms,
|
||||||
|
int64_t* ntp_time_ms) override;
|
||||||
|
|
||||||
|
float GetMicrophoneEnergy() { return mMicrophoneEnergy.load(std::memory_order_relaxed); }
|
||||||
|
void SetGain(float gain) { mGain.store(gain, std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::atomic<webrtc::AudioTransport*> engine_{ nullptr };
|
||||||
static const int NUM_PACKETS_TO_FILTER = 30; // 300 ms of smoothing (30 frames)
|
static const int NUM_PACKETS_TO_FILTER = 30; // 300 ms of smoothing (30 frames)
|
||||||
float mSumVector[NUM_PACKETS_TO_FILTER];
|
float mSumVector[NUM_PACKETS_TO_FILTER];
|
||||||
float mMicrophoneEnergy;
|
std::atomic<float> mMicrophoneEnergy;
|
||||||
|
std::atomic<float> mGain{ 0.0f };
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
// LLWebRTCAudioDeviceModule
|
||||||
|
// - Wraps a real ADM to provide microphone energy for tuning
|
||||||
|
// -----------------------------------------------------------------------------
|
||||||
|
class LLWebRTCAudioDeviceModule : public webrtc::AudioDeviceModule
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit LLWebRTCAudioDeviceModule(webrtc::scoped_refptr<webrtc::AudioDeviceModule> inner) : inner_(std::move(inner)), tuning_(false)
|
||||||
|
{
|
||||||
|
RTC_CHECK(inner_);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ----- AudioDeviceModule interface: we mostly forward to |inner_| -----
|
||||||
|
int32_t ActiveAudioLayer(AudioLayer* audioLayer) const override { return inner_->ActiveAudioLayer(audioLayer); }
|
||||||
|
|
||||||
|
int32_t RegisterAudioCallback(webrtc::AudioTransport* engine_transport) override
|
||||||
|
{
|
||||||
|
// The engine registers its transport here. We put our audio transport between engine and ADM.
|
||||||
|
audio_transport_.SetEngineTransport(engine_transport);
|
||||||
|
// Register our proxy with the real ADM.
|
||||||
|
return inner_->RegisterAudioCallback(&audio_transport_);
|
||||||
|
}
|
||||||
|
|
||||||
|
int32_t Init() override { return inner_->Init(); }
|
||||||
|
int32_t Terminate() override { return inner_->Terminate(); }
|
||||||
|
bool Initialized() const override { return inner_->Initialized(); }
|
||||||
|
|
||||||
|
// --- Device enumeration/selection (forward) ---
|
||||||
|
int16_t PlayoutDevices() override { return inner_->PlayoutDevices(); }
|
||||||
|
int16_t RecordingDevices() override { return inner_->RecordingDevices(); }
|
||||||
|
int32_t PlayoutDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], char guid[webrtc::kAdmMaxGuidSize]) override
|
||||||
|
{
|
||||||
|
return inner_->PlayoutDeviceName(index, name, guid);
|
||||||
|
}
|
||||||
|
int32_t RecordingDeviceName(uint16_t index, char name[webrtc::kAdmMaxDeviceNameSize], char guid[webrtc::kAdmMaxGuidSize]) override
|
||||||
|
{
|
||||||
|
return inner_->RecordingDeviceName(index, name, guid);
|
||||||
|
}
|
||||||
|
int32_t SetPlayoutDevice(uint16_t index) override { return inner_->SetPlayoutDevice(index); }
|
||||||
|
int32_t SetRecordingDevice(uint16_t index) override { return inner_->SetRecordingDevice(index); }
|
||||||
|
|
||||||
|
// Windows default/communications selectors, if your branch exposes them:
|
||||||
|
int32_t SetPlayoutDevice(WindowsDeviceType type) override { return inner_->SetPlayoutDevice(type); }
|
||||||
|
int32_t SetRecordingDevice(WindowsDeviceType type) override { return inner_->SetRecordingDevice(type); }
|
||||||
|
|
||||||
|
// --- Init/start/stop (forward) ---
|
||||||
|
int32_t InitPlayout() override { return inner_->InitPlayout(); }
|
||||||
|
bool PlayoutIsInitialized() const override { return inner_->PlayoutIsInitialized(); }
|
||||||
|
int32_t StartPlayout() override {
|
||||||
|
if (tuning_) return 0; // For tuning, don't allow playout
|
||||||
|
return inner_->StartPlayout();
|
||||||
|
}
|
||||||
|
int32_t StopPlayout() override { return inner_->StopPlayout(); }
|
||||||
|
bool Playing() const override { return inner_->Playing(); }
|
||||||
|
|
||||||
|
int32_t InitRecording() override { return inner_->InitRecording(); }
|
||||||
|
bool RecordingIsInitialized() const override { return inner_->RecordingIsInitialized(); }
|
||||||
|
int32_t StartRecording() override {
|
||||||
|
// ignore start recording as webrtc.lib will
|
||||||
|
// send one when streams first connect, resulting
|
||||||
|
// in an inadvertant 'recording' when mute is on.
|
||||||
|
// We take full control of StartRecording via
|
||||||
|
// ForceStartRecording below.
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int32_t StopRecording() override {
|
||||||
|
if (tuning_) return 0; // if we're tuning, disregard the StopRecording we get from disabling the streams
|
||||||
|
return inner_->StopRecording();
|
||||||
|
}
|
||||||
|
int32_t ForceStartRecording() { return inner_->StartRecording(); }
|
||||||
|
int32_t ForceStopRecording() { return inner_->StopRecording(); }
|
||||||
|
bool Recording() const override { return inner_->Recording(); }
|
||||||
|
|
||||||
|
// --- Stereo opts (forward if available on your branch) ---
|
||||||
|
int32_t SetStereoPlayout(bool enable) override { return inner_->SetStereoPlayout(enable); }
|
||||||
|
int32_t SetStereoRecording(bool enable) override { return inner_->SetStereoRecording(enable); }
|
||||||
|
int32_t PlayoutIsAvailable(bool* available) override { return inner_->PlayoutIsAvailable(available); }
|
||||||
|
int32_t RecordingIsAvailable(bool* available) override { return inner_->RecordingIsAvailable(available); }
|
||||||
|
|
||||||
|
// --- AGC/Volume/Mute/etc. (forward) ---
|
||||||
|
int32_t SetMicrophoneVolume(uint32_t volume) override { return inner_->SetMicrophoneVolume(volume); }
|
||||||
|
int32_t MicrophoneVolume(uint32_t* volume) const override { return inner_->MicrophoneVolume(volume); }
|
||||||
|
|
||||||
|
// --- Speaker/Microphone init (forward) ---
|
||||||
|
int32_t InitSpeaker() override { return inner_->InitSpeaker(); }
|
||||||
|
bool SpeakerIsInitialized() const override { return inner_->SpeakerIsInitialized(); }
|
||||||
|
int32_t InitMicrophone() override { return inner_->InitMicrophone(); }
|
||||||
|
bool MicrophoneIsInitialized() const override { return inner_->MicrophoneIsInitialized(); }
|
||||||
|
|
||||||
|
// --- Speaker Volume (forward) ---
|
||||||
|
int32_t SpeakerVolumeIsAvailable(bool* available) override { return inner_->SpeakerVolumeIsAvailable(available); }
|
||||||
|
int32_t SetSpeakerVolume(uint32_t volume) override { return inner_->SetSpeakerVolume(volume); }
|
||||||
|
int32_t SpeakerVolume(uint32_t* volume) const override { return inner_->SpeakerVolume(volume); }
|
||||||
|
int32_t MaxSpeakerVolume(uint32_t* maxVolume) const override { return inner_->MaxSpeakerVolume(maxVolume); }
|
||||||
|
int32_t MinSpeakerVolume(uint32_t* minVolume) const override { return inner_->MinSpeakerVolume(minVolume); }
|
||||||
|
|
||||||
|
// --- Microphone Volume (forward) ---
|
||||||
|
int32_t MicrophoneVolumeIsAvailable(bool* available) override { return inner_->MicrophoneVolumeIsAvailable(available); }
|
||||||
|
int32_t MaxMicrophoneVolume(uint32_t* maxVolume) const override { return inner_->MaxMicrophoneVolume(maxVolume); }
|
||||||
|
int32_t MinMicrophoneVolume(uint32_t* minVolume) const override { return inner_->MinMicrophoneVolume(minVolume); }
|
||||||
|
|
||||||
|
// --- Speaker Mute (forward) ---
|
||||||
|
int32_t SpeakerMuteIsAvailable(bool* available) override { return inner_->SpeakerMuteIsAvailable(available); }
|
||||||
|
int32_t SetSpeakerMute(bool enable) override { return inner_->SetSpeakerMute(enable); }
|
||||||
|
int32_t SpeakerMute(bool* enabled) const override { return inner_->SpeakerMute(enabled); }
|
||||||
|
|
||||||
|
// --- Microphone Mute (forward) ---
|
||||||
|
int32_t MicrophoneMuteIsAvailable(bool* available) override { return inner_->MicrophoneMuteIsAvailable(available); }
|
||||||
|
int32_t SetMicrophoneMute(bool enable) override { return inner_->SetMicrophoneMute(enable); }
|
||||||
|
int32_t MicrophoneMute(bool* enabled) const override { return inner_->MicrophoneMute(enabled); }
|
||||||
|
|
||||||
|
// --- Stereo Support (forward) ---
|
||||||
|
int32_t StereoPlayoutIsAvailable(bool* available) const override { return inner_->StereoPlayoutIsAvailable(available); }
|
||||||
|
int32_t StereoPlayout(bool* enabled) const override { return inner_->StereoPlayout(enabled); }
|
||||||
|
int32_t StereoRecordingIsAvailable(bool* available) const override { return inner_->StereoRecordingIsAvailable(available); }
|
||||||
|
int32_t StereoRecording(bool* enabled) const override { return inner_->StereoRecording(enabled); }
|
||||||
|
|
||||||
|
// --- Delay/Timing (forward) ---
|
||||||
|
int32_t PlayoutDelay(uint16_t* delayMS) const override { return inner_->PlayoutDelay(delayMS); }
|
||||||
|
|
||||||
|
// --- Built-in Audio Processing (forward) ---
|
||||||
|
bool BuiltInAECIsAvailable() const override { return inner_->BuiltInAECIsAvailable(); }
|
||||||
|
bool BuiltInAGCIsAvailable() const override { return inner_->BuiltInAGCIsAvailable(); }
|
||||||
|
bool BuiltInNSIsAvailable() const override { return inner_->BuiltInNSIsAvailable(); }
|
||||||
|
int32_t EnableBuiltInAEC(bool enable) override { return inner_->EnableBuiltInAEC(enable); }
|
||||||
|
int32_t EnableBuiltInAGC(bool enable) override { return inner_->EnableBuiltInAGC(enable); }
|
||||||
|
int32_t EnableBuiltInNS(bool enable) override { return inner_->EnableBuiltInNS(enable); }
|
||||||
|
|
||||||
|
// --- Additional AudioDeviceModule methods (forward) ---
|
||||||
|
int32_t GetPlayoutUnderrunCount() const override { return inner_->GetPlayoutUnderrunCount(); }
|
||||||
|
|
||||||
|
// Used to generate RTC stats. If not implemented, RTCAudioPlayoutStats will
|
||||||
|
// not be present in the stats.
|
||||||
|
std::optional<Stats> GetStats() const override { return inner_->GetStats(); }
|
||||||
|
|
||||||
|
// Only supported on iOS.
|
||||||
|
#if defined(WEBRTC_IOS)
|
||||||
|
virtual int GetPlayoutAudioParameters(AudioParameters* params) const override { return inner_->GetPlayoutAudioParameters(params); }
|
||||||
|
virtual int GetRecordAudioParameters(AudioParameters* params) override { return inner_->GetRecordAudioParameters(params); }
|
||||||
|
#endif // WEBRTC_IOS
|
||||||
|
|
||||||
|
virtual int32_t GetPlayoutDevice() const override { return inner_->GetPlayoutDevice(); }
|
||||||
|
virtual int32_t GetRecordingDevice() const override { return inner_->GetRecordingDevice(); }
|
||||||
|
virtual int32_t SetObserver(webrtc::AudioDeviceObserver* observer) override { return inner_->SetObserver(observer); }
|
||||||
|
|
||||||
|
// tuning microphone energy calculations
|
||||||
|
float GetMicrophoneEnergy() { return audio_transport_.GetMicrophoneEnergy(); }
|
||||||
|
void SetTuningMicGain(float gain) { audio_transport_.SetGain(gain); }
|
||||||
|
void SetTuning(bool tuning, bool mute)
|
||||||
|
{
|
||||||
|
tuning_ = tuning;
|
||||||
|
if (tuning)
|
||||||
|
{
|
||||||
|
inner_->InitRecording();
|
||||||
|
inner_->StartRecording();
|
||||||
|
inner_->StopPlayout();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (mute)
|
||||||
|
{
|
||||||
|
inner_->StopRecording();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
inner_->InitRecording();
|
||||||
|
inner_->StartRecording();
|
||||||
|
}
|
||||||
|
inner_->StartPlayout();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
~LLWebRTCAudioDeviceModule() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
webrtc::scoped_refptr<webrtc::AudioDeviceModule> inner_;
|
||||||
|
LLWebRTCAudioTransport audio_transport_;
|
||||||
|
|
||||||
|
bool tuning_;
|
||||||
|
};
|
||||||
|
|
||||||
|
class LLCustomProcessorState
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
float getMicrophoneEnergy() { return mMicrophoneEnergy.load(std::memory_order_relaxed); }
|
||||||
|
void setMicrophoneEnergy(float energy) { mMicrophoneEnergy.store(energy, std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
void setGain(float gain)
|
||||||
|
{
|
||||||
|
mGain.store(gain, std::memory_order_relaxed);
|
||||||
|
mDirty.store(true, std::memory_order_relaxed);
|
||||||
|
}
|
||||||
|
|
||||||
|
float getGain() { return mGain.load(std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
bool getDirty() { return mDirty.exchange(false, std::memory_order_relaxed); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::atomic<bool> mDirty{ true };
|
||||||
|
std::atomic<float> mMicrophoneEnergy{ 0.0f };
|
||||||
|
std::atomic<float> mGain{ 0.0f };
|
||||||
|
};
|
||||||
|
|
||||||
|
using LLCustomProcessorStatePtr = std::shared_ptr<LLCustomProcessorState>;
|
||||||
|
|
||||||
// Used to process/retrieve audio levels after
|
// Used to process/retrieve audio levels after
|
||||||
// all of the processing (AGC, AEC, etc.) for display in-world to the user.
|
// all of the processing (AGC, AEC, etc.) for display in-world to the user.
|
||||||
class LLCustomProcessor : public webrtc::CustomProcessing
|
class LLCustomProcessor : public webrtc::CustomProcessing
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LLCustomProcessor();
|
LLCustomProcessor(LLCustomProcessorStatePtr state);
|
||||||
~LLCustomProcessor() override {}
|
~LLCustomProcessor() override {}
|
||||||
|
|
||||||
// (Re-) Initializes the submodule.
|
// (Re-) Initializes the submodule.
|
||||||
void Initialize(int sample_rate_hz, int num_channels) override;
|
void Initialize(int sample_rate_hz, int num_channels) override;
|
||||||
|
|
||||||
// Analyzes the given capture or render signal.
|
// Analyzes the given capture or render signal.
|
||||||
void Process(webrtc::AudioBuffer *audio) override;
|
void Process(webrtc::AudioBuffer* audio) override;
|
||||||
|
|
||||||
// Returns a string representation of the module state.
|
// Returns a string representation of the module state.
|
||||||
std::string ToString() const override { return ""; }
|
std::string ToString() const override { return ""; }
|
||||||
|
|
||||||
float getMicrophoneEnergy() { return mMicrophoneEnergy; }
|
protected:
|
||||||
|
|
||||||
void setGain(float gain) { mGain = gain; }
|
|
||||||
|
|
||||||
protected:
|
|
||||||
static const int NUM_PACKETS_TO_FILTER = 30; // 300 ms of smoothing
|
static const int NUM_PACKETS_TO_FILTER = 30; // 300 ms of smoothing
|
||||||
int mSampleRateHz;
|
int mSampleRateHz{ 48000 };
|
||||||
int mNumChannels;
|
int mNumChannels{ 2 };
|
||||||
|
int mRampFrames{ 2 };
|
||||||
|
float mCurrentGain{ 0.0f };
|
||||||
|
float mGainStep{ 0.0f };
|
||||||
|
|
||||||
float mSumVector[NUM_PACKETS_TO_FILTER];
|
float mSumVector[NUM_PACKETS_TO_FILTER];
|
||||||
float mMicrophoneEnergy;
|
friend LLCustomProcessorState;
|
||||||
float mGain;
|
LLCustomProcessorStatePtr mState;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Primary singleton implementation for interfacing
|
// Primary singleton implementation for interfacing
|
||||||
// with the native webrtc library.
|
// with the native webrtc library.
|
||||||
class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceSink
|
class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceObserver
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LLWebRTCImpl(LLWebRTCLogCallback* logCallback);
|
LLWebRTCImpl(LLWebRTCLogCallback* logCallback);
|
||||||
|
|
@ -214,10 +443,15 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS
|
||||||
float getTuningAudioLevel() override;
|
float getTuningAudioLevel() override;
|
||||||
float getPeerConnectionAudioLevel() override;
|
float getPeerConnectionAudioLevel() override;
|
||||||
|
|
||||||
void setPeerConnectionGain(float gain) override;
|
void setMicGain(float gain) override;
|
||||||
|
void setTuningMicGain(float gain) override;
|
||||||
|
|
||||||
|
void setMute(bool mute, int delay_ms = 20) override;
|
||||||
|
|
||||||
|
void intSetMute(bool mute, int delay_ms = 20);
|
||||||
|
|
||||||
//
|
//
|
||||||
// AudioDeviceSink
|
// AudioDeviceObserver
|
||||||
//
|
//
|
||||||
void OnDevicesUpdated() override;
|
void OnDevicesUpdated() override;
|
||||||
|
|
||||||
|
|
@ -246,19 +480,19 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS
|
||||||
mNetworkThread->PostTask(std::move(task), location);
|
mNetworkThread->PostTask(std::move(task), location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WorkerBlockingCall(rtc::FunctionView<void()> functor,
|
void WorkerBlockingCall(webrtc::FunctionView<void()> functor,
|
||||||
const webrtc::Location& location = webrtc::Location::Current())
|
const webrtc::Location& location = webrtc::Location::Current())
|
||||||
{
|
{
|
||||||
mWorkerThread->BlockingCall(std::move(functor), location);
|
mWorkerThread->BlockingCall(std::move(functor), location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SignalingBlockingCall(rtc::FunctionView<void()> functor,
|
void SignalingBlockingCall(webrtc::FunctionView<void()> functor,
|
||||||
const webrtc::Location& location = webrtc::Location::Current())
|
const webrtc::Location& location = webrtc::Location::Current())
|
||||||
{
|
{
|
||||||
mSignalingThread->BlockingCall(std::move(functor), location);
|
mSignalingThread->BlockingCall(std::move(functor), location);
|
||||||
}
|
}
|
||||||
|
|
||||||
void NetworkBlockingCall(rtc::FunctionView<void()> functor,
|
void NetworkBlockingCall(webrtc::FunctionView<void()> functor,
|
||||||
const webrtc::Location& location = webrtc::Location::Current())
|
const webrtc::Location& location = webrtc::Location::Current())
|
||||||
{
|
{
|
||||||
mNetworkThread->BlockingCall(std::move(functor), location);
|
mNetworkThread->BlockingCall(std::move(functor), location);
|
||||||
|
|
@ -266,7 +500,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS
|
||||||
|
|
||||||
// Allows the LLWebRTCPeerConnectionImpl class to retrieve the
|
// Allows the LLWebRTCPeerConnectionImpl class to retrieve the
|
||||||
// native webrtc PeerConnectionFactory.
|
// native webrtc PeerConnectionFactory.
|
||||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> getPeerConnectionFactory()
|
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> getPeerConnectionFactory()
|
||||||
{
|
{
|
||||||
return mPeerConnectionFactory;
|
return mPeerConnectionFactory;
|
||||||
}
|
}
|
||||||
|
|
@ -275,23 +509,20 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS
|
||||||
LLWebRTCPeerConnectionInterface* newPeerConnection();
|
LLWebRTCPeerConnectionInterface* newPeerConnection();
|
||||||
void freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection);
|
void freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection);
|
||||||
|
|
||||||
// enables/disables capture via the capture device
|
|
||||||
void setRecording(bool recording);
|
|
||||||
|
|
||||||
void setPlayout(bool playing);
|
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
|
||||||
|
void workerDeployDevices();
|
||||||
LLWebRTCLogSink* mLogSink;
|
LLWebRTCLogSink* mLogSink;
|
||||||
|
|
||||||
// The native webrtc threads
|
// The native webrtc threads
|
||||||
std::unique_ptr<rtc::Thread> mNetworkThread;
|
std::unique_ptr<webrtc::Thread> mNetworkThread;
|
||||||
std::unique_ptr<rtc::Thread> mWorkerThread;
|
std::unique_ptr<webrtc::Thread> mWorkerThread;
|
||||||
std::unique_ptr<rtc::Thread> mSignalingThread;
|
std::unique_ptr<webrtc::Thread> mSignalingThread;
|
||||||
|
|
||||||
// The factory that allows creation of native webrtc PeerConnections.
|
// The factory that allows creation of native webrtc PeerConnections.
|
||||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
|
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::AudioProcessing> mAudioProcessingModule;
|
webrtc::scoped_refptr<webrtc::AudioProcessing> mAudioProcessingModule;
|
||||||
|
|
||||||
// more native webrtc stuff
|
// more native webrtc stuff
|
||||||
std::unique_ptr<webrtc::TaskQueueFactory> mTaskQueueFactory;
|
std::unique_ptr<webrtc::TaskQueueFactory> mTaskQueueFactory;
|
||||||
|
|
@ -299,25 +530,26 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS
|
||||||
|
|
||||||
// Devices
|
// Devices
|
||||||
void updateDevices();
|
void updateDevices();
|
||||||
rtc::scoped_refptr<webrtc::AudioDeviceModule> mTuningDeviceModule;
|
void deployDevices();
|
||||||
rtc::scoped_refptr<webrtc::AudioDeviceModule> mPeerDeviceModule;
|
std::atomic<int> mDevicesDeploying;
|
||||||
|
webrtc::scoped_refptr<LLWebRTCAudioDeviceModule> mDeviceModule;
|
||||||
std::vector<LLWebRTCDevicesObserver *> mVoiceDevicesObserverList;
|
std::vector<LLWebRTCDevicesObserver *> mVoiceDevicesObserverList;
|
||||||
|
|
||||||
// accessors in native webrtc for devices aren't apparently implemented yet.
|
// accessors in native webrtc for devices aren't apparently implemented yet.
|
||||||
bool mTuningMode;
|
bool mTuningMode;
|
||||||
int32_t mRecordingDevice;
|
std::string mRecordingDevice;
|
||||||
LLWebRTCVoiceDeviceList mRecordingDeviceList;
|
LLWebRTCVoiceDeviceList mRecordingDeviceList;
|
||||||
|
|
||||||
int32_t mPlayoutDevice;
|
std::string mPlayoutDevice;
|
||||||
LLWebRTCVoiceDeviceList mPlayoutDeviceList;
|
LLWebRTCVoiceDeviceList mPlayoutDeviceList;
|
||||||
|
|
||||||
bool mMute;
|
bool mMute;
|
||||||
|
float mGain;
|
||||||
|
|
||||||
LLAudioDeviceObserver * mTuningAudioDeviceObserver;
|
LLCustomProcessorStatePtr mPeerCustomProcessor;
|
||||||
LLCustomProcessor * mPeerCustomProcessor;
|
|
||||||
|
|
||||||
// peer connections
|
// peer connections
|
||||||
std::vector<rtc::scoped_refptr<LLWebRTCPeerConnectionImpl>> mPeerConnections;
|
std::vector<webrtc::scoped_refptr<LLWebRTCPeerConnectionImpl>> mPeerConnections;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -342,7 +574,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
|
||||||
void terminate();
|
void terminate();
|
||||||
|
|
||||||
virtual void AddRef() const override = 0;
|
virtual void AddRef() const override = 0;
|
||||||
virtual rtc::RefCountReleaseStatus Release() const override = 0;
|
virtual webrtc::RefCountReleaseStatus Release() const override = 0;
|
||||||
|
|
||||||
//
|
//
|
||||||
// LLWebRTCPeerConnection
|
// LLWebRTCPeerConnection
|
||||||
|
|
@ -373,10 +605,10 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
|
||||||
//
|
//
|
||||||
|
|
||||||
void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {}
|
void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {}
|
||||||
void OnAddTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
|
void OnAddTrack(webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,
|
||||||
const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> &streams) override;
|
const std::vector<webrtc::scoped_refptr<webrtc::MediaStreamInterface>> &streams) override;
|
||||||
void OnRemoveTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) override;
|
void OnRemoveTrack(webrtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) override;
|
||||||
void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> channel) override;
|
void OnDataChannel(webrtc::scoped_refptr<webrtc::DataChannelInterface> channel) override;
|
||||||
void OnRenegotiationNeeded() override {}
|
void OnRenegotiationNeeded() override {}
|
||||||
void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {};
|
void OnIceConnectionChange(webrtc::PeerConnectionInterface::IceConnectionState new_state) override {};
|
||||||
void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override;
|
void OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state) override;
|
||||||
|
|
@ -415,7 +647,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
|
||||||
|
|
||||||
LLWebRTCImpl * mWebRTCImpl;
|
LLWebRTCImpl * mWebRTCImpl;
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
|
webrtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
MUTE_INITIAL,
|
MUTE_INITIAL,
|
||||||
|
|
@ -429,12 +661,12 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,
|
||||||
std::vector<std::unique_ptr<webrtc::IceCandidateInterface>> mCachedIceCandidates;
|
std::vector<std::unique_ptr<webrtc::IceCandidateInterface>> mCachedIceCandidates;
|
||||||
bool mAnswerReceived;
|
bool mAnswerReceived;
|
||||||
|
|
||||||
rtc::scoped_refptr<webrtc::PeerConnectionInterface> mPeerConnection;
|
webrtc::scoped_refptr<webrtc::PeerConnectionInterface> mPeerConnection;
|
||||||
rtc::scoped_refptr<webrtc::MediaStreamInterface> mLocalStream;
|
webrtc::scoped_refptr<webrtc::MediaStreamInterface> mLocalStream;
|
||||||
|
|
||||||
// data
|
// data
|
||||||
std::vector<LLWebRTCDataObserver *> mDataObserverList;
|
std::vector<LLWebRTCDataObserver *> mDataObserverList;
|
||||||
rtc::scoped_refptr<webrtc::DataChannelInterface> mDataChannel;
|
webrtc::scoped_refptr<webrtc::DataChannelInterface> mDataChannel;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -84,7 +84,13 @@ namespace {
|
||||||
|
|
||||||
const F32 MAX_AUDIO_DIST = 50.0f;
|
const F32 MAX_AUDIO_DIST = 50.0f;
|
||||||
const F32 VOLUME_SCALE_WEBRTC = 0.01f;
|
const F32 VOLUME_SCALE_WEBRTC = 0.01f;
|
||||||
const F32 LEVEL_SCALE_WEBRTC = 0.008f;
|
const F32 TUNING_LEVEL_SCALE = 0.01f;
|
||||||
|
const F32 TUNING_LEVEL_START_POINT = 0.8f;
|
||||||
|
const F32 LEVEL_SCALE = 0.005f;
|
||||||
|
const F32 LEVEL_START_POINT = 0.18f;
|
||||||
|
const uint32_t SET_HIDDEN_RESTORE_DELAY_MS = 200; // 200 ms to unmute again after hiding during teleport
|
||||||
|
const uint32_t MUTE_FADE_DELAY_MS = 500; // 20ms fade followed by 480ms silence gets rid of the click just after unmuting.
|
||||||
|
// This is because the buffers and processing is cleared by the silence.
|
||||||
|
|
||||||
const F32 SPEAKING_AUDIO_LEVEL = 0.30f; // <FS:minerjr> add missing f for float
|
const F32 SPEAKING_AUDIO_LEVEL = 0.30f; // <FS:minerjr> add missing f for float
|
||||||
|
|
||||||
|
|
@ -201,7 +207,6 @@ bool LLWebRTCVoiceClient::sShuttingDown = false;
|
||||||
|
|
||||||
LLWebRTCVoiceClient::LLWebRTCVoiceClient() :
|
LLWebRTCVoiceClient::LLWebRTCVoiceClient() :
|
||||||
mHidden(false),
|
mHidden(false),
|
||||||
mTuningMode(false),
|
|
||||||
mTuningMicGain(0.0),
|
mTuningMicGain(0.0),
|
||||||
mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume
|
mTuningSpeakerVolume(50), // Set to 50 so the user can hear themselves when he sets his mic volume
|
||||||
mDevicesListUpdated(false),
|
mDevicesListUpdated(false),
|
||||||
|
|
@ -348,26 +353,46 @@ void LLWebRTCVoiceClient::updateSettings()
|
||||||
static LLCachedControl<std::string> sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice");
|
static LLCachedControl<std::string> sOutputDevice(gSavedSettings, "VoiceOutputAudioDevice");
|
||||||
setRenderDevice(sOutputDevice);
|
setRenderDevice(sOutputDevice);
|
||||||
|
|
||||||
LL_INFOS("Voice") << "Input device: " << std::quoted(sInputDevice()) << ", output device: " << std::quoted(sOutputDevice()) << LL_ENDL;
|
LL_INFOS("Voice") << "Input device: " << std::quoted(sInputDevice()) << ", output device: " << std::quoted(sOutputDevice())
|
||||||
|
<< LL_ENDL;
|
||||||
|
|
||||||
static LLCachedControl<F32> sMicLevel(gSavedSettings, "AudioLevelMic");
|
static LLCachedControl<F32> sMicLevel(gSavedSettings, "AudioLevelMic");
|
||||||
setMicGain(sMicLevel);
|
setMicGain(sMicLevel);
|
||||||
|
|
||||||
llwebrtc::LLWebRTCDeviceInterface::AudioConfig config;
|
llwebrtc::LLWebRTCDeviceInterface::AudioConfig config;
|
||||||
|
|
||||||
|
bool audioConfigChanged = false;
|
||||||
|
|
||||||
static LLCachedControl<bool> sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true);
|
static LLCachedControl<bool> sEchoCancellation(gSavedSettings, "VoiceEchoCancellation", true);
|
||||||
|
if (sEchoCancellation != config.mEchoCancellation)
|
||||||
|
{
|
||||||
config.mEchoCancellation = sEchoCancellation;
|
config.mEchoCancellation = sEchoCancellation;
|
||||||
|
audioConfigChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
static LLCachedControl<bool> sAGC(gSavedSettings, "VoiceAutomaticGainControl", true);
|
static LLCachedControl<bool> sAGC(gSavedSettings, "VoiceAutomaticGainControl", true);
|
||||||
|
if (sAGC != config.mAGC)
|
||||||
|
{
|
||||||
config.mAGC = sAGC;
|
config.mAGC = sAGC;
|
||||||
|
audioConfigChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
static LLCachedControl<U32> sNoiseSuppressionLevel(gSavedSettings,
|
static LLCachedControl<U32> sNoiseSuppressionLevel(
|
||||||
|
gSavedSettings,
|
||||||
"VoiceNoiseSuppressionLevel",
|
"VoiceNoiseSuppressionLevel",
|
||||||
llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH);
|
llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel::NOISE_SUPPRESSION_LEVEL_VERY_HIGH);
|
||||||
config.mNoiseSuppressionLevel = (llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel)(U32)sNoiseSuppressionLevel;
|
auto noiseSuppressionLevel =
|
||||||
|
(llwebrtc::LLWebRTCDeviceInterface::AudioConfig::ENoiseSuppressionLevel)(U32)sNoiseSuppressionLevel;
|
||||||
|
if (noiseSuppressionLevel != config.mNoiseSuppressionLevel)
|
||||||
|
{
|
||||||
|
config.mNoiseSuppressionLevel = noiseSuppressionLevel;
|
||||||
|
audioConfigChanged = true;
|
||||||
|
}
|
||||||
|
if (audioConfigChanged)
|
||||||
|
{
|
||||||
mWebRTCDeviceInterface->setAudioConfig(config);
|
mWebRTCDeviceInterface->setAudioConfig(config);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Observers
|
// Observers
|
||||||
|
|
@ -703,21 +728,38 @@ void LLWebRTCVoiceClient::OnDevicesChangedImpl(const llwebrtc::LLWebRTCVoiceDevi
|
||||||
std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
|
std::string outputDevice = gSavedSettings.getString("VoiceOutputAudioDevice");
|
||||||
|
|
||||||
LL_DEBUGS("Voice") << "Setting devices to-input: '" << inputDevice << "' output: '" << outputDevice << "'" << LL_ENDL;
|
LL_DEBUGS("Voice") << "Setting devices to-input: '" << inputDevice << "' output: '" << outputDevice << "'" << LL_ENDL;
|
||||||
|
|
||||||
|
// only set the render device if the device list has changed.
|
||||||
|
if (mRenderDevices.size() != render_devices.size() || !std::equal(mRenderDevices.begin(),
|
||||||
|
mRenderDevices.end(),
|
||||||
|
render_devices.begin(),
|
||||||
|
[](const LLVoiceDevice& a, const llwebrtc::LLWebRTCVoiceDevice& b) {
|
||||||
|
return a.display_name == b.mDisplayName && a.full_name == b.mID; }))
|
||||||
|
{
|
||||||
clearRenderDevices();
|
clearRenderDevices();
|
||||||
for (auto &device : render_devices)
|
for (auto& device : render_devices)
|
||||||
{
|
{
|
||||||
addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID));
|
addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID));
|
||||||
}
|
}
|
||||||
setRenderDevice(outputDevice);
|
setRenderDevice(outputDevice);
|
||||||
|
}
|
||||||
|
|
||||||
|
// only set the capture device if the device list has changed.
|
||||||
|
if (mCaptureDevices.size() != capture_devices.size() ||!std::equal(mCaptureDevices.begin(),
|
||||||
|
mCaptureDevices.end(),
|
||||||
|
capture_devices.begin(),
|
||||||
|
[](const LLVoiceDevice& a, const llwebrtc::LLWebRTCVoiceDevice& b)
|
||||||
|
{ return a.display_name == b.mDisplayName && a.full_name == b.mID; }))
|
||||||
|
{
|
||||||
clearCaptureDevices();
|
clearCaptureDevices();
|
||||||
for (auto &device : capture_devices)
|
for (auto& device : capture_devices)
|
||||||
{
|
{
|
||||||
LL_DEBUGS("Voice") << "Checking capture device:'" << device.mID << "'" << LL_ENDL;
|
LL_DEBUGS("Voice") << "Checking capture device:'" << device.mID << "'" << LL_ENDL;
|
||||||
|
|
||||||
addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID));
|
addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID));
|
||||||
}
|
}
|
||||||
setCaptureDevice(inputDevice);
|
setCaptureDevice(inputDevice);
|
||||||
|
}
|
||||||
|
|
||||||
setDevicesListUpdated(true);
|
setDevicesListUpdated(true);
|
||||||
}
|
}
|
||||||
|
|
@ -770,7 +812,14 @@ bool LLWebRTCVoiceClient::inTuningMode()
|
||||||
|
|
||||||
void LLWebRTCVoiceClient::tuningSetMicVolume(float volume)
|
void LLWebRTCVoiceClient::tuningSetMicVolume(float volume)
|
||||||
{
|
{
|
||||||
|
if (volume != mTuningMicGain)
|
||||||
|
{
|
||||||
mTuningMicGain = volume;
|
mTuningMicGain = volume;
|
||||||
|
if (mWebRTCDeviceInterface)
|
||||||
|
{
|
||||||
|
mWebRTCDeviceInterface->setTuningMicGain(volume);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
|
void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
|
||||||
|
|
@ -782,21 +831,10 @@ void LLWebRTCVoiceClient::tuningSetSpeakerVolume(float volume)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
float LLWebRTCVoiceClient::getAudioLevel()
|
|
||||||
{
|
|
||||||
if (mIsInTuningMode)
|
|
||||||
{
|
|
||||||
return (1.0f - mWebRTCDeviceInterface->getTuningAudioLevel() * LEVEL_SCALE_WEBRTC) * mTuningMicGain / 2.1f;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return (1.0f - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float LLWebRTCVoiceClient::tuningGetEnergy(void)
|
float LLWebRTCVoiceClient::tuningGetEnergy(void)
|
||||||
{
|
{
|
||||||
return getAudioLevel();
|
float rms = mWebRTCDeviceInterface->getTuningAudioLevel();
|
||||||
|
return TUNING_LEVEL_START_POINT - TUNING_LEVEL_SCALE * rms;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool LLWebRTCVoiceClient::deviceSettingsAvailable()
|
bool LLWebRTCVoiceClient::deviceSettingsAvailable()
|
||||||
|
|
@ -832,6 +870,11 @@ void LLWebRTCVoiceClient::setHidden(bool hidden)
|
||||||
|
|
||||||
if (inSpatialChannel())
|
if (inSpatialChannel())
|
||||||
{
|
{
|
||||||
|
if (mWebRTCDeviceInterface)
|
||||||
|
{
|
||||||
|
mWebRTCDeviceInterface->setMute(mHidden || mMuteMic,
|
||||||
|
mHidden ? 0 : SET_HIDDEN_RESTORE_DELAY_MS); // delay 200ms so as to not pile up mutes/unmutes.
|
||||||
|
}
|
||||||
if (mHidden)
|
if (mHidden)
|
||||||
{
|
{
|
||||||
// get out of the channel entirely
|
// get out of the channel entirely
|
||||||
|
|
@ -998,7 +1041,6 @@ void LLWebRTCVoiceClient::updatePosition(void)
|
||||||
{
|
{
|
||||||
if (participant->mRegion != region->getRegionID()) {
|
if (participant->mRegion != region->getRegionID()) {
|
||||||
participant->mRegion = region->getRegionID();
|
participant->mRegion = region->getRegionID();
|
||||||
setMuteMic(mMuteMic);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1123,13 +1165,14 @@ void LLWebRTCVoiceClient::sendPositionUpdate(bool force)
|
||||||
// Update our own volume on our participant, so it'll show up
|
// Update our own volume on our participant, so it'll show up
|
||||||
// in the UI. This is done on all sessions, so switching
|
// in the UI. This is done on all sessions, so switching
|
||||||
// sessions retains consistent volume levels.
|
// sessions retains consistent volume levels.
|
||||||
void LLWebRTCVoiceClient::updateOwnVolume() {
|
void LLWebRTCVoiceClient::updateOwnVolume()
|
||||||
F32 audio_level = 0.0;
|
{
|
||||||
if (!mMuteMic && !mTuningMode)
|
F32 audio_level = 0.0f;
|
||||||
|
if (!mMuteMic)
|
||||||
{
|
{
|
||||||
audio_level = getAudioLevel();
|
float rms = mWebRTCDeviceInterface->getPeerConnectionAudioLevel();
|
||||||
|
audio_level = LEVEL_START_POINT - LEVEL_SCALE * rms;
|
||||||
}
|
}
|
||||||
|
|
||||||
sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level));
|
sessionState::for_each(boost::bind(predUpdateOwnVolume, _1, audio_level));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1526,6 +1569,17 @@ void LLWebRTCVoiceClient::setMuteMic(bool muted)
|
||||||
}
|
}
|
||||||
|
|
||||||
mMuteMic = muted;
|
mMuteMic = muted;
|
||||||
|
|
||||||
|
if (mIsInTuningMode)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mWebRTCDeviceInterface)
|
||||||
|
{
|
||||||
|
mWebRTCDeviceInterface->setMute(muted, muted ? MUTE_FADE_DELAY_MS : 0); // delay for 40ms on mute to allow buffers to empty
|
||||||
|
}
|
||||||
|
|
||||||
// when you're hidden, your mic is always muted.
|
// when you're hidden, your mic is always muted.
|
||||||
if (!mHidden)
|
if (!mHidden)
|
||||||
{
|
{
|
||||||
|
|
@ -1564,7 +1618,10 @@ void LLWebRTCVoiceClient::setMicGain(F32 gain)
|
||||||
if (gain != mMicGain)
|
if (gain != mMicGain)
|
||||||
{
|
{
|
||||||
mMicGain = gain;
|
mMicGain = gain;
|
||||||
mWebRTCDeviceInterface->setPeerConnectionGain(gain);
|
if (mWebRTCDeviceInterface)
|
||||||
|
{
|
||||||
|
mWebRTCDeviceInterface->setMicGain(gain);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -444,10 +444,6 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// helper function to retrieve the audio level
|
|
||||||
// Used in multiple places.
|
|
||||||
float getAudioLevel();
|
|
||||||
|
|
||||||
// Coroutine support methods
|
// Coroutine support methods
|
||||||
//---
|
//---
|
||||||
void voiceConnectionCoro();
|
void voiceConnectionCoro();
|
||||||
|
|
@ -458,7 +454,6 @@ private:
|
||||||
|
|
||||||
LL::WorkQueue::weak_t mMainQueue;
|
LL::WorkQueue::weak_t mMainQueue;
|
||||||
|
|
||||||
bool mTuningMode;
|
|
||||||
F32 mTuningMicGain;
|
F32 mTuningMicGain;
|
||||||
int mTuningSpeakerVolume;
|
int mTuningSpeakerVolume;
|
||||||
bool mDevicesListUpdated; // set to true when the device list has been updated
|
bool mDevicesListUpdated; // set to true when the device list has been updated
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue