diff options
| -rw-r--r-- | indra/llwebrtc/llwebrtc.cpp | 213 | ||||
| -rw-r--r-- | indra/llwebrtc/llwebrtc.h | 129 | ||||
| -rw-r--r-- | indra/llwebrtc/llwebrtc_impl.h | 68 | ||||
| -rw-r--r-- | indra/newview/llvoicewebrtc.cpp | 50 | ||||
| -rw-r--r-- | indra/newview/llvoicewebrtc.h | 6 | 
5 files changed, 333 insertions, 133 deletions
diff --git a/indra/llwebrtc/llwebrtc.cpp b/indra/llwebrtc/llwebrtc.cpp index 74af16679a..dc5fd9657e 100644 --- a/indra/llwebrtc/llwebrtc.cpp +++ b/indra/llwebrtc/llwebrtc.cpp @@ -44,12 +44,16 @@ LLAudioDeviceObserver::LLAudioDeviceObserver() : mSumVector {0}, mMicrophoneEner  float LLAudioDeviceObserver::getMicrophoneEnergy() { return mMicrophoneEnergy; } +// TODO: Pull smoothing/filtering code into a common helper function +// for LLAudioDeviceObserver and LLCustomProcessor +  void LLAudioDeviceObserver::OnCaptureData(const void    *audio_samples,                                            const size_t   num_samples,                                            const size_t   bytes_per_sample,                                            const size_t   num_channels,                                            const uint32_t samples_per_sec)  { +    // calculate the energy      float        energy  = 0;      const short *samples = (const short *) audio_samples;      for (size_t index = 0; index < num_samples * num_channels; index++) @@ -82,7 +86,8 @@ void LLAudioDeviceObserver::OnRenderData(const void    *audio_samples,  LLCustomProcessor::LLCustomProcessor() :      mSampleRateHz(0),  -    mNumChannels(0)  +    mNumChannels(0), +    mMicrophoneEnergy(0.0)  {       memset(mSumVector, 0, sizeof(mSumVector));  } @@ -107,6 +112,7 @@ void LLCustomProcessor::Process(webrtc::AudioBuffer *audio_in)          return;      } +    // grab the input audio      frame_samples.resize(stream_config.num_samples());      frame.resize(stream_config.num_channels());      for (size_t ch = 0; ch < stream_config.num_channels(); ++ch) @@ -116,6 +122,7 @@ void LLCustomProcessor::Process(webrtc::AudioBuffer *audio_in)      audio_in->CopyTo(stream_config, &frame[0]); +    // calculate the energy      float        energy  = 0;      for (size_t index = 0; index < stream_config.num_samples(); index++)      { @@ -137,17 +144,33 @@ void LLCustomProcessor::Process(webrtc::AudioBuffer *audio_in)      mMicrophoneEnergy = std::sqrt(totalSum / (stream_config.num_samples() * buffer_size));  } +// +// LLWebRTCImpl implementation +// + +LLWebRTCImpl::LLWebRTCImpl() : +    mPeerCustomProcessor(nullptr), +    mMute(true), +    mPlayoutDevice(0), +    mRecordingDevice(0), +    mTuningAudioDeviceObserver(nullptr) +{ +} +  void LLWebRTCImpl::init()  {      RTC_DCHECK(mPeerConnectionFactory);      mPlayoutDevice  = 0;      mRecordingDevice = 0;      rtc::InitializeSSL(); + +    // Normal logging is rather spammy, so turn it off.      rtc::LogMessage::LogToDebug(rtc::LS_NONE);      rtc::LogMessage::SetLogToStderr(true);      mTaskQueueFactory = webrtc::CreateDefaultTaskQueueFactory(); -     + +    // Create the native threads.      mNetworkThread = rtc::Thread::CreateWithSocketServer();      mNetworkThread->SetName("WebRTCNetworkThread", nullptr);      mNetworkThread->Start(); @@ -162,9 +185,12 @@ void LLWebRTCImpl::init()      mWorkerThread->PostTask(                              [this]()                              { +                                 // Initialize the audio devices on the Worker Thread                                   mTuningDeviceModule =  webrtc::CreateAudioDeviceWithDataObserver( -                                    webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, mTaskQueueFactory.get(), -                std::unique_ptr<webrtc::AudioDeviceDataObserver>(mTuningAudioDeviceObserver)); +                                     webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio,  +                                     mTaskQueueFactory.get(), +                                     std::unique_ptr<webrtc::AudioDeviceDataObserver>(mTuningAudioDeviceObserver)); +                                  mTuningDeviceModule->Init();                                  mTuningDeviceModule->SetStereoRecording(true);                                  mTuningDeviceModule->SetStereoPlayout(true); @@ -176,9 +202,13 @@ void LLWebRTCImpl::init()      mWorkerThread->BlockingCall(                                  [this]()                                  { +                                    // the peer device module doesn't need an observer +                                    // as we pull peer data after audio processing.                                      mPeerDeviceModule = -                                    webrtc::CreateAudioDeviceWithDataObserver(webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, -                                                                              mTaskQueueFactory.get(), nullptr); +                                        webrtc::CreateAudioDeviceWithDataObserver( +                                            webrtc::AudioDeviceModule::AudioLayer::kPlatformDefaultAudio, +                                            mTaskQueueFactory.get(), +                                            nullptr);                                      mPeerDeviceModule->Init();                                      mPeerDeviceModule->SetPlayoutDevice(mPlayoutDevice);                                      mPeerDeviceModule->SetRecordingDevice(mRecordingDevice); @@ -191,11 +221,15 @@ void LLWebRTCImpl::init()                                      mPeerDeviceModule->InitPlayout();                                  }); +    // The custom processor allows us to retrieve audio data (and levels) +    // from after other audio processing such as AEC, AGC, etc.      mPeerCustomProcessor = new LLCustomProcessor;      webrtc::AudioProcessingBuilder apb;      apb.SetCapturePostProcessing(std::unique_ptr<webrtc::CustomProcessing>(mPeerCustomProcessor));      rtc::scoped_refptr<webrtc::AudioProcessing> apm = apb.Create(); +    // TODO: wire some of these to the primary interface and ultimately +    // to the UI to allow user config.      webrtc::AudioProcessing::Config             apm_config;      apm_config.echo_canceller.enabled         = true;      apm_config.echo_canceller.mobile_mode     = false; @@ -242,6 +276,41 @@ void LLWebRTCImpl::init()          });  } +void LLWebRTCImpl::terminate() +{ +    for (auto &connection : mPeerConnections) +    { +        connection->terminate(); +    } +    mPeerConnections.clear(); + +    mSignalingThread->BlockingCall([this]() { mPeerConnectionFactory = nullptr; }); +    mWorkerThread->BlockingCall( +        [this]() +        { +            if (mTuningDeviceModule) +            { +                mTuningDeviceModule->StopRecording(); +                mTuningDeviceModule->Terminate(); +            } +            if (mPeerDeviceModule) +            { +                mPeerDeviceModule->StopRecording(); +                mPeerDeviceModule->Terminate(); +            } +            mTuningDeviceModule = nullptr; +            mPeerDeviceModule   = nullptr; +            mTaskQueueFactory   = nullptr; +        }); +} + +// +// Devices functions +// +// Most device-related functionality needs to happen +// on the worker thread (the audio thread,) so those calls will be +// proxied over to that thread. +//  void LLWebRTCImpl::setRecording(bool recording)  {      mWorkerThread->PostTask( @@ -258,38 +327,6 @@ void LLWebRTCImpl::setRecording(bool recording)          });  } -void LLWebRTCImpl::terminate() -{ -    for (auto& connection : mPeerConnections) -    { -        connection->terminate(); -    } -    mPeerConnections.clear(); - -    mSignalingThread->BlockingCall( -                                   [this]() -                                   { -                                       mPeerConnectionFactory = nullptr; -                                   }); -    mWorkerThread->BlockingCall( -                                [this]() -                                { -                                    if (mTuningDeviceModule) -                                    { -                                        mTuningDeviceModule->StopRecording(); -                                        mTuningDeviceModule->Terminate(); -                                    } -                                    if (mPeerDeviceModule) -                                    { -                                        mPeerDeviceModule->StopRecording(); -                                        mPeerDeviceModule->Terminate(); -                                    } -                                    mTuningDeviceModule = nullptr; -                                    mPeerDeviceModule = nullptr; -                                    mTaskQueueFactory   = nullptr; -                                }); -} -  void LLWebRTCImpl::refreshDevices()  {      mWorkerThread->PostTask([this]() { updateDevices(); }); @@ -307,6 +344,8 @@ void LLWebRTCImpl::unsetDevicesObserver(LLWebRTCDevicesObserver *observer)      }  } +// TODO: There's potential for shared code here as the patterns +// are similar.  void LLWebRTCImpl::setCaptureDevice(const std::string &id)  {      mWorkerThread->PostTask( @@ -377,7 +416,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)                                      mTuningDeviceModule->PlayoutDeviceName(i, name, guid);                                      if (id == guid || id == "Default")                                      { -                                        RTC_LOG(LS_INFO) << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; +                                        RTC_LOG(LS_INFO) << __FUNCTION__ << "Set playout device to " << name << " " << guid << " " << i;                                          tuningPlayoutDevice = i;                                          break;                                      } @@ -407,7 +446,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)                                          if (id == guid || id == "Default")                                          {                                              RTC_LOG(LS_INFO) -                                                << __FUNCTION__ << "Set recording device to " << name << " " << guid << " " << i; +                                                << __FUNCTION__ << "Set playout device to " << name << " " << guid << " " << i;                                              mPlayoutDevice = i;                                              break;                                          } @@ -422,6 +461,7 @@ void LLWebRTCImpl::setRenderDevice(const std::string &id)                              });  } +// updateDevices needs to happen on the worker thread.  void LLWebRTCImpl::updateDevices()  {      int16_t renderDeviceCount = mTuningDeviceModule->PlayoutDevices(); @@ -482,13 +522,14 @@ void LLWebRTCImpl::setTuningMode(bool enable)  float LLWebRTCImpl::getTuningAudioLevel() { return -20 * log10f(mTuningAudioDeviceObserver->getMicrophoneEnergy()); } -float LLWebRTCImpl::getPeerAudioLevel() { return -20 * log10f(mPeerCustomProcessor->getMicrophoneEnergy()); } +float LLWebRTCImpl::getPeerConnectionAudioLevel() { return -20 * log10f(mPeerCustomProcessor->getMicrophoneEnergy()); } +  // -// Helpers +// Peer Connection Helpers  // -LLWebRTCPeerConnection * LLWebRTCImpl::newPeerConnection() +LLWebRTCPeerConnectionInterface *LLWebRTCImpl::newPeerConnection()  {      rtc::scoped_refptr<LLWebRTCPeerConnectionImpl> peerConnection = rtc::scoped_refptr<LLWebRTCPeerConnectionImpl>(new rtc::RefCountedObject<LLWebRTCPeerConnectionImpl>());      peerConnection->init(this); @@ -498,7 +539,7 @@ LLWebRTCPeerConnection * LLWebRTCImpl::newPeerConnection()      return peerConnection.get();  } -void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnection * peer_connection) +void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection)  {      std::vector<rtc::scoped_refptr<LLWebRTCPeerConnectionImpl>>::iterator it =      std::find(mPeerConnections.begin(), mPeerConnections.end(), peer_connection); @@ -513,6 +554,20 @@ void LLWebRTCImpl::freePeerConnection(LLWebRTCPeerConnection * peer_connection)      }  } + +// +// LLWebRTCPeerConnectionImpl implementation. +// +// Most peer connection (signaling) happens on +// the signaling thread. + +LLWebRTCPeerConnectionImpl::LLWebRTCPeerConnectionImpl() : +    mWebRTCImpl(nullptr), +    mMute(false), +    mAnswerReceived(false) +{ +} +  //  // LLWebRTCPeerConnection interface  // @@ -547,6 +602,9 @@ void LLWebRTCPeerConnectionImpl::unsetSignalingObserver(LLWebRTCSignalingObserve      }  } +// TODO: Add initialization structure through which +// stun and turn servers may be passed in from +// the sim or login.  bool LLWebRTCPeerConnectionImpl::initializeConnection()  {      RTC_DCHECK(!mPeerConnection); @@ -663,7 +721,7 @@ bool LLWebRTCPeerConnectionImpl::shutdownConnection()                  }                  for (auto &observer : mSignalingObserverList)                  { -                    observer->OnPeerShutDown(); +                    observer->OnPeerConnectionShutdown();                  }              });          return true; @@ -673,7 +731,7 @@ bool LLWebRTCPeerConnectionImpl::shutdownConnection()  void LLWebRTCPeerConnectionImpl::enableSenderTracks(bool enable)  { -    // set_enabled shouldn't be done on the worker thread +    // set_enabled shouldn't be done on the worker thread.      if (mPeerConnection)      {          auto senders = mPeerConnection->GetSenders(); @@ -697,6 +755,7 @@ void LLWebRTCPeerConnectionImpl::enableReceiverTracks(bool enable)      }  } +// Tell the peer connection that we've received a SDP answer from the sim.  void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)  {      RTC_LOG(LS_INFO) << __FUNCTION__ << " Remote SDP: " << sdp; @@ -713,6 +772,11 @@ void LLWebRTCPeerConnectionImpl::AnswerAvailable(const std::string &sdp)                                 });  } + +// +// LLWebRTCAudioInterface implementation +// +  void LLWebRTCPeerConnectionImpl::setMute(bool mute)  {      mMute = mute; @@ -812,21 +876,21 @@ void LLWebRTCPeerConnectionImpl::OnDataChannel(rtc::scoped_refptr<webrtc::DataCh  void LLWebRTCPeerConnectionImpl::OnIceGatheringChange(webrtc::PeerConnectionInterface::IceGatheringState new_state)  { -    LLWebRTCSignalingObserver::IceGatheringState webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; +    LLWebRTCSignalingObserver::EIceGatheringState webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW;      switch (new_state)      {          case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringNew: -            webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; +            webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW;              break;          case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringGathering: -            webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_GATHERING; +            webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_GATHERING;              break;          case webrtc::PeerConnectionInterface::IceGatheringState::kIceGatheringComplete: -            webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE; +            webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_COMPLETE;              break;          default:              RTC_LOG(LS_ERROR) << __FUNCTION__ << " Bad Ice Gathering State" << new_state; -            webrtc_new_state = LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW; +            webrtc_new_state = LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW;              return;      } @@ -875,6 +939,8 @@ void LLWebRTCPeerConnectionImpl::OnConnectionChange(webrtc::PeerConnectionInterf      }  } +// Convert an ICE candidate into a string appropriate for trickling +// to the Secondlife WebRTC server via the sim.  static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterface *candidate)  {      std::ostringstream candidate_stream; @@ -920,6 +986,7 @@ static std::string iceCandidateToTrickleString(const webrtc::IceCandidateInterfa      return candidate_stream.str();  } +// The webrtc library has a new ice candidate.  void LLWebRTCPeerConnectionImpl::OnIceCandidate(const webrtc::IceCandidateInterface *candidate)  {      RTC_LOG(LS_INFO) << __FUNCTION__ << " " << candidate->sdp_mline_index(); @@ -931,19 +998,24 @@ void LLWebRTCPeerConnectionImpl::OnIceCandidate(const webrtc::IceCandidateInterf      }      if (mAnswerReceived)      { +        // We've already received an answer SDP from the Secondlife WebRTC server +        // so simply tell observers about our new ice candidate.          for (auto &observer : mSignalingObserverList)          {              LLWebRTCIceCandidate ice_candidate; -            ice_candidate.candidate   = iceCandidateToTrickleString(candidate); -            ice_candidate.mline_index = candidate->sdp_mline_index(); -            ice_candidate.sdp_mid     = candidate->sdp_mid(); +            ice_candidate.mCandidate  = iceCandidateToTrickleString(candidate); +            ice_candidate.mMLineIndex = candidate->sdp_mline_index(); +            ice_candidate.mSdpMid     = candidate->sdp_mid();              observer->OnIceCandidate(ice_candidate);          }      }      else      { +        // As we've not yet received our answer, cache the candidate.          mCachedIceCandidates.push_back( -            webrtc::CreateIceCandidate(candidate->sdp_mid(), candidate->sdp_mline_index(), candidate->candidate())); +            webrtc::CreateIceCandidate(candidate->sdp_mid(), +                                       candidate->sdp_mline_index(), +                                       candidate->candidate()));      }  } @@ -994,13 +1066,18 @@ void LLWebRTCPeerConnectionImpl::OnSuccess(webrtc::SessionDescriptionInterface *                                           rtc::scoped_refptr<webrtc::SetLocalDescriptionObserverInterface>(this));  } -void LLWebRTCPeerConnectionImpl::OnFailure(webrtc::RTCError error) { RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); } +void LLWebRTCPeerConnectionImpl::OnFailure(webrtc::RTCError error) +{  +    RTC_LOG(LS_ERROR) << ToString(error.type()) << ": " << error.message(); +}  //  // SetRemoteDescriptionObserverInterface implementation.  //  void LLWebRTCPeerConnectionImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError error)  { +    // we've received an answer SDP from the sim. +      RTC_LOG(LS_INFO) << __FUNCTION__ << " " << mPeerConnection->signaling_state();      if (!error.ok())      { @@ -1008,14 +1085,16 @@ void LLWebRTCPeerConnectionImpl::OnSetRemoteDescriptionComplete(webrtc::RTCError          return;      }      mAnswerReceived = true; + +    // tell the observers about any cached ICE candidates.      for (auto &observer : mSignalingObserverList)      {          for (auto &candidate : mCachedIceCandidates)          {              LLWebRTCIceCandidate ice_candidate; -            ice_candidate.candidate   = iceCandidateToTrickleString(candidate.get()); -            ice_candidate.mline_index = candidate->sdp_mline_index(); -            ice_candidate.sdp_mid     = candidate->sdp_mid(); +            ice_candidate.mCandidate  = iceCandidateToTrickleString(candidate.get()); +            ice_candidate.mMLineIndex = candidate->sdp_mline_index(); +            ice_candidate.mSdpMid     = candidate->sdp_mid();              observer->OnIceCandidate(ice_candidate);          }      } @@ -1061,7 +1140,6 @@ void LLWebRTCPeerConnectionImpl::OnStateChange()      }  } -  void LLWebRTCPeerConnectionImpl::OnMessage(const webrtc::DataBuffer& buffer)  {      std::string data((const char*)buffer.data.cdata(), buffer.size()); @@ -1071,6 +1149,10 @@ void LLWebRTCPeerConnectionImpl::OnMessage(const webrtc::DataBuffer& buffer)      }  } +// +// LLWebRTCDataInterface +// +  void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary)  {      if (mDataChannel) @@ -1081,7 +1163,10 @@ void LLWebRTCPeerConnectionImpl::sendData(const std::string& data, bool binary)      }  } -void LLWebRTCPeerConnectionImpl::setDataObserver(LLWebRTCDataObserver* observer) { mDataObserverList.emplace_back(observer); } +void LLWebRTCPeerConnectionImpl::setDataObserver(LLWebRTCDataObserver* observer) +{  +    mDataObserverList.emplace_back(observer); +}  void LLWebRTCPeerConnectionImpl::unsetDataObserver(LLWebRTCDataObserver* observer)  { @@ -1099,12 +1184,12 @@ LLWebRTCDeviceInterface * getDeviceInterface()      return gWebRTCImpl;  } -LLWebRTCPeerConnection * newPeerConnection() +LLWebRTCPeerConnectionInterface* newPeerConnection()  {      return gWebRTCImpl->newPeerConnection();  } -void freePeerConnection(LLWebRTCPeerConnection *peer_connection) +void freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection)  {      gWebRTCImpl->freePeerConnection(peer_connection);  } @@ -1112,7 +1197,7 @@ void freePeerConnection(LLWebRTCPeerConnection *peer_connection)  void init()  { -    gWebRTCImpl =  new LLWebRTCImpl(); +    gWebRTCImpl = new LLWebRTCImpl();      gWebRTCImpl->init();  } diff --git a/indra/llwebrtc/llwebrtc.h b/indra/llwebrtc/llwebrtc.h index 7743ac7ba0..789bde452e 100644 --- a/indra/llwebrtc/llwebrtc.h +++ b/indra/llwebrtc/llwebrtc.h @@ -24,6 +24,17 @@   * $/LicenseInfo$   */ +/* + * llwebrtc wraps the native webrtc c++ library in a dynamic library with a simlified interface + * so that the viewer can use it.  This is done because native webrtc has a different + * overall threading model than the viewer. + * The native webrtc library is also compiled with clang, and has memory management + * functions that conflict namespace-wise with those in the viewer. + *  + * Due to these differences, code from the viewer cannot be pulled in to this + * dynamic library, so it remains very simple. + */ +  #ifndef LLWEBRTC_H  #define LLWEBRTC_H @@ -45,52 +56,74 @@  namespace llwebrtc  { -struct LLWebRTCIceCandidate -{ -    std::string candidate; -    std::string sdp_mid; -    int         mline_index; -}; +// LLWebRTCVoiceDevice is a simple representation of the  +// components of a device, used to communicate this +// information to the viewer. + + +// A note on threading. +// Native WebRTC has it's own threading model.  Some discussion +// can be found here (https://webrtc.github.io/webrtc-org/native-code/native-apis/) +// +// Note that all callbacks to observers will occurr on one of the WebRTC native threads +// (signaling, worker, etc.)  Care should be taken to assure there are not +// bad interactions with the viewer threads.  class LLWebRTCVoiceDevice  {    public: -    std::string display_name;  // friendly value for the user -    std::string id; // internal value for selection -    bool current;  // current device +    std::string mDisplayName;  // friendly name for user interface purposes +    std::string mID;           // internal value for selection +    bool        mCurrent;      // current device      LLWebRTCVoiceDevice(const std::string &display_name, const std::string &id, bool current) : -        display_name(display_name), -        id(id), -        current(current) +        mDisplayName(display_name), +        mID(id), +        mCurrent(current)      {};  };  typedef std::vector<LLWebRTCVoiceDevice> LLWebRTCVoiceDeviceList; + +// The LLWebRTCDeviceObserver should be implemented by the viewer +// webrtc module, which will receive notifications when devices +// change (are unplugged, etc.)  class LLWebRTCDevicesObserver  {    public: -    virtual void OnDevicesChanged(const LLWebRTCVoiceDeviceList &render_devices, const LLWebRTCVoiceDeviceList &capture_devices) = 0; +    virtual void OnDevicesChanged(const LLWebRTCVoiceDeviceList &render_devices, +                                  const LLWebRTCVoiceDeviceList &capture_devices) = 0;  }; + +// The LLWebRTCDeviceInterface provides a way for the viewer +// to enumerate, set, and get notifications of changes +// for both capture (microphone) and render (speaker) +// devices.  class LLWebRTCDeviceInterface  {    public: +    // instructs webrtc to refresh the device list.      virtual void refreshDevices() = 0; +    // set the capture and render devices using the unique identifier for the device      virtual void setCaptureDevice(const std::string& id) = 0;      virtual void setRenderDevice(const std::string& id) = 0; +    // Device observers for device change callbacks.      virtual void setDevicesObserver(LLWebRTCDevicesObserver *observer) = 0;      virtual void unsetDevicesObserver(LLWebRTCDevicesObserver *observer) = 0; +    // tuning and audio levels      virtual void setTuningMode(bool enable) = 0; -    virtual float getTuningAudioLevel() = 0; -    virtual float getPeerAudioLevel() = 0; +    virtual float getTuningAudioLevel() = 0; // for use during tuning +    virtual float getPeerConnectionAudioLevel() = 0; // for use when not tuning  }; +// LLWebRTCAudioInterface provides the viewer with a way +// to set audio characteristics (mute, send and receive volume)  class LLWebRTCAudioInterface  {    public: @@ -99,41 +132,81 @@ class LLWebRTCAudioInterface      virtual void setSendVolume(float volume) = 0;  // volume between 0.0 and 1.0  }; +// LLWebRTCDataObserver allows the viewer voice module to be notified when +// data is received over the data channel.  class LLWebRTCDataObserver  {  public:      virtual void OnDataReceived(const std::string& data, bool binary) = 0;  }; +// LLWebRTCDataInterface allows the viewer to send data over the data channel.  class LLWebRTCDataInterface  {  public: +      virtual void sendData(const std::string& data, bool binary=false) = 0;      virtual void setDataObserver(LLWebRTCDataObserver *observer) = 0;      virtual void unsetDataObserver(LLWebRTCDataObserver *observer) = 0;  }; +// LLWebRTCIceCandidate is a basic structure containing +// information needed for ICE trickling. +struct LLWebRTCIceCandidate +{ +    std::string mCandidate; +    std::string mSdpMid; +    int         mMLineIndex; +}; + +// LLWebRTCSignalingObserver provides a way for the native +// webrtc library to notify the viewer voice module of +// various state changes.  class LLWebRTCSignalingObserver  { -  public:  -    enum IceGatheringState{ +  public: + +    typedef enum e_ice_gathering_state {          ICE_GATHERING_NEW,          ICE_GATHERING_GATHERING,          ICE_GATHERING_COMPLETE -    }; -    virtual void OnIceGatheringState(IceGatheringState state) = 0; +    } EIceGatheringState; + +    // Called when ICE gathering states have changed. +    // This may be called at any time, as ICE gathering +    // can be redone while a connection is up. +    virtual void OnIceGatheringState(EIceGatheringState state) = 0; + +    // Called when a new ice candidate is available.      virtual void OnIceCandidate(const LLWebRTCIceCandidate& candidate) = 0; + +    // Called when an offer is available after a connection is requested.      virtual void OnOfferAvailable(const std::string& sdp) = 0; + +    // Called when a connection enters a failure state and renegotiation is needed.      virtual void OnRenegotiationNeeded() = 0; + +    // Called when the audio channel has been established and audio +    // can begin.      virtual void OnAudioEstablished(LLWebRTCAudioInterface *audio_interface) = 0; + +    // Called when the data channel has been established and data +    // transfer can begin.      virtual void OnDataChannelReady(LLWebRTCDataInterface *data_interface) = 0; -    virtual void OnPeerShutDown() = 0; + +    // Called when a peer connection has finished shutting down. +    virtual void OnPeerConnectionShutdown() = 0;  }; -class LLWebRTCPeerConnection + +// LLWebRTCPeerConnectionInterface representsd a connection to a peer, +// in most cases a Secondlife WebRTC server.  This interface +// allows for management of this peer connection. +class LLWebRTCPeerConnectionInterface  {    public: +      virtual void setSignalingObserver(LLWebRTCSignalingObserver* observer) = 0;      virtual void unsetSignalingObserver(LLWebRTCSignalingObserver* observer) = 0; @@ -142,11 +215,21 @@ class LLWebRTCPeerConnection      virtual void AnswerAvailable(const std::string &sdp) = 0;  }; +// The following define the dynamic linked library +// exports. + +// This library must be initialized before use.  LLSYMEXPORT void init(); + +// And should be terminated as part of shutdown.  LLSYMEXPORT void terminate(); + +// Return an interface for device management.  LLSYMEXPORT LLWebRTCDeviceInterface* getDeviceInterface(); -LLSYMEXPORT LLWebRTCPeerConnection* newPeerConnection(); -LLSYMEXPORT void freePeerConnection(LLWebRTCPeerConnection *connection); + +// Allocate and free peer connections. +LLSYMEXPORT LLWebRTCPeerConnectionInterface* newPeerConnection(); +LLSYMEXPORT void freePeerConnection(LLWebRTCPeerConnectionInterface *connection);  }  #endif // LLWEBRTC_H diff --git a/indra/llwebrtc/llwebrtc_impl.h b/indra/llwebrtc/llwebrtc_impl.h index 3c182c4b02..c2d0a4413e 100644 --- a/indra/llwebrtc/llwebrtc_impl.h +++ b/indra/llwebrtc/llwebrtc_impl.h @@ -1,6 +1,6 @@  /**   * @file llwebrtc_impl.h - * @brief WebRTC interface implementation header + * @brief WebRTC dynamic library implementation header   *   * $LicenseInfo:firstyear=2023&license=viewerlgpl$   * Second Life Viewer Source Code @@ -65,19 +65,27 @@ namespace llwebrtc  class LLWebRTCPeerConnectionImpl; + +// Implements a class allowing capture of audio data +// to determine audio level of the microphone.  class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver  {    public:      LLAudioDeviceObserver(); +    // Retrieve the RMS audio loudness      float getMicrophoneEnergy(); +    // Data retrieved from the caputure device is +    // passed in here for processing.      void OnCaptureData(const void    *audio_samples,                         const size_t   num_samples,                         const size_t   bytes_per_sample,                         const size_t   num_channels,                         const uint32_t samples_per_sec) override; +    // This is for data destined for the render device. +    // not currently used.      void OnRenderData(const void    *audio_samples,                        const size_t   num_samples,                        const size_t   bytes_per_sample, @@ -85,10 +93,13 @@ class LLAudioDeviceObserver : public webrtc::AudioDeviceDataObserver                        const uint32_t samples_per_sec) override;    protected: -    float mSumVector[30];  // 300 ms of smoothing +    static const int NUM_PACKETS_TO_FILTER = 30;  // 300 ms of smoothing (30 frames) +    float mSumVector[NUM_PACKETS_TO_FILTER];      float mMicrophoneEnergy;  }; +// Used to process/retrieve audio levels after +// all of the processing (AGC, AEC, etc.) for display in-world to the user.  class LLCustomProcessor : public webrtc::CustomProcessing  {    public: @@ -97,8 +108,10 @@ class LLCustomProcessor : public webrtc::CustomProcessing      // (Re-) Initializes the submodule.      void Initialize(int sample_rate_hz, int num_channels) override; +      // Analyzes the given capture or render signal.      void Process(webrtc::AudioBuffer *audio) override; +      // Returns a string representation of the module state.      std::string ToString() const override { return ""; } @@ -113,13 +126,13 @@ class LLCustomProcessor : public webrtc::CustomProcessing      float mMicrophoneEnergy;  }; + +// Primary singleton implementation for interfacing +// with the native webrtc library.  class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceSink  {    public: -    LLWebRTCImpl() :  -        mPeerCustomProcessor(nullptr), mMute(true) -    { -    } +    LLWebRTCImpl();      ~LLWebRTCImpl() {}      void init(); @@ -139,7 +152,7 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS      void setTuningMode(bool enable) override;      float getTuningAudioLevel() override; -    float getPeerAudioLevel() override; +    float getPeerConnectionAudioLevel() override;      //      // AudioDeviceSink @@ -150,6 +163,9 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS      // Helpers      // +    // The following thread helpers allow the +    // LLWebRTCPeerConnectionImpl class to post +    // tasks to the native webrtc threads.      void PostWorkerTask(absl::AnyInvocable<void() &&> task,                    const webrtc::Location& location = webrtc::Location::Current())      { @@ -185,21 +201,31 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS      {          mNetworkThread->BlockingCall(std::move(functor), location);      } -     -    rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> getPeerConnectionFactory() { return mPeerConnectionFactory; } -    LLWebRTCPeerConnection *  newPeerConnection(); -    void freePeerConnection(LLWebRTCPeerConnection * peer_connection); +    // Allows the LLWebRTCPeerConnectionImpl class to retrieve the +    // native webrtc PeerConnectionFactory. +    rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> getPeerConnectionFactory() +    {  +        return mPeerConnectionFactory; +    } +    // create or destroy a peer connection. +    LLWebRTCPeerConnectionInterface* newPeerConnection(); +    void freePeerConnection(LLWebRTCPeerConnectionInterface* peer_connection); + +    // enables/disables capture via the capture device      void setRecording(bool recording);    protected: - +    // The native webrtc threads      std::unique_ptr<rtc::Thread>                               mNetworkThread;      std::unique_ptr<rtc::Thread>                               mWorkerThread;      std::unique_ptr<rtc::Thread>                               mSignalingThread; + +    // The factory that allows creation of native webrtc PeerConnections.      rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory; -    webrtc::PeerConnectionInterface::RTCConfiguration          mConfiguration; + +    // more native webrtc stuff      std::unique_ptr<webrtc::TaskQueueFactory>                  mTaskQueueFactory; @@ -209,11 +235,11 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS      rtc::scoped_refptr<webrtc::AudioDeviceModule>              mPeerDeviceModule;      std::vector<LLWebRTCDevicesObserver *>                     mVoiceDevicesObserverList; -    // accessors in webrtc aren't apparently implemented yet. +    // accessors in native webrtc for devices aren't apparently implemented yet.      int32_t                                                    mPlayoutDevice;      int32_t                                                    mRecordingDevice;      bool                                                       mMute; -     +      LLAudioDeviceObserver *                                    mTuningAudioDeviceObserver;      LLCustomProcessor *                                        mPeerCustomProcessor; @@ -221,7 +247,11 @@ class LLWebRTCImpl : public LLWebRTCDeviceInterface, public webrtc::AudioDeviceS      std::vector<rtc::scoped_refptr<LLWebRTCPeerConnectionImpl>>     mPeerConnections;  }; -class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection, + +// The implementation of a peer connection, which contains +// the various interfaces used by the viewer to interact with +// the webrtc connection. +class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnectionInterface,                                     public LLWebRTCAudioInterface,                                     public LLWebRTCDataInterface,                                     public webrtc::PeerConnectionObserver, @@ -232,7 +262,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection,  {    public: -    LLWebRTCPeerConnectionImpl() {} +    LLWebRTCPeerConnectionImpl();      ~LLWebRTCPeerConnectionImpl() {}      void init(LLWebRTCImpl * webrtc_impl); @@ -270,7 +300,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection,      //      void OnSignalingChange(webrtc::PeerConnectionInterface::SignalingState new_state) override {} -    void OnAddTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface>                     receiver, +    void OnAddTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver,                      const std::vector<rtc::scoped_refptr<webrtc::MediaStreamInterface>> &streams) override;      void OnRemoveTrack(rtc::scoped_refptr<webrtc::RtpReceiverInterface> receiver) override;      void OnDataChannel(rtc::scoped_refptr<webrtc::DataChannelInterface> channel) override; @@ -311,6 +341,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection,    protected:      LLWebRTCImpl * mWebRTCImpl; +      rtc::scoped_refptr<webrtc::PeerConnectionFactoryInterface> mPeerConnectionFactory;      bool                                                       mMute; @@ -323,6 +354,7 @@ class LLWebRTCPeerConnectionImpl : public LLWebRTCPeerConnection,      rtc::scoped_refptr<webrtc::PeerConnectionInterface>        mPeerConnection;      rtc::scoped_refptr<webrtc::MediaStreamInterface>           mLocalStream; +    // data      std::vector<LLWebRTCDataObserver *>                        mDataObserverList;      rtc::scoped_refptr<webrtc::DataChannelInterface>           mDataChannel;  }; diff --git a/indra/newview/llvoicewebrtc.cpp b/indra/newview/llvoicewebrtc.cpp index 6e0cd32249..6eee8e70b2 100644 --- a/indra/newview/llvoicewebrtc.cpp +++ b/indra/newview/llvoicewebrtc.cpp @@ -597,8 +597,8 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi      bool renderDeviceSet = false;      for (auto &device : render_devices)      { -        addRenderDevice(LLVoiceDevice(device.display_name, device.id)); -        if (device.current && outputDevice == device.id) +        addRenderDevice(LLVoiceDevice(device.mDisplayName, device.mID)); +        if (device.mCurrent && outputDevice == device.mID)          {              setRenderDevice(outputDevice);              renderDeviceSet = true; @@ -613,8 +613,8 @@ void LLWebRTCVoiceClient::OnDevicesChanged(const llwebrtc::LLWebRTCVoiceDeviceLi      bool captureDeviceSet = false;      for (auto &device : capture_devices)      { -        addCaptureDevice(LLVoiceDevice(device.display_name, device.id)); -        if (device.current && inputDevice == device.id) +        addCaptureDevice(LLVoiceDevice(device.mDisplayName, device.mID)); +        if (device.mCurrent && inputDevice == device.mID)          {              setCaptureDevice(outputDevice);              captureDeviceSet = true; @@ -696,7 +696,7 @@ float LLWebRTCVoiceClient::getAudioLevel()      }      else      { -        return (1.0 - mWebRTCDeviceInterface->getPeerAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1; +        return (1.0 - mWebRTCDeviceInterface->getPeerConnectionAudioLevel() * LEVEL_SCALE_WEBRTC) * mMicGain / 2.1;      }  } @@ -2030,8 +2030,8 @@ LLVoiceWebRTCConnection::LLVoiceWebRTCConnection(const LLUUID ®ionID, const s      mChannelID(channelID),      mRegionID(regionID)  { -    mWebRTCPeerConnection = llwebrtc::newPeerConnection(); -    mWebRTCPeerConnection->setSignalingObserver(this); +    mWebRTCPeerConnectionInterface = llwebrtc::newPeerConnection(); +    mWebRTCPeerConnectionInterface->setSignalingObserver(this);  }  LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection() @@ -2042,10 +2042,10 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection()          // by llwebrtc::terminate() on shutdown.          return;      } -    if (mWebRTCPeerConnection) +    if (mWebRTCPeerConnectionInterface)      { -        llwebrtc::freePeerConnection(mWebRTCPeerConnection); -        mWebRTCPeerConnection = nullptr; +        llwebrtc::freePeerConnection(mWebRTCPeerConnectionInterface); +        mWebRTCPeerConnectionInterface = nullptr;      }  } @@ -2058,19 +2058,19 @@ LLVoiceWebRTCConnection::~LLVoiceWebRTCConnection()  // negotiated, updates about the best connectivity paths may trickle in.  These need to be  // sent to the Secondlife WebRTC server via the simulator so that both sides have a clear  // view of the network environment. -void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::IceGatheringState state) +void LLVoiceWebRTCConnection::OnIceGatheringState(llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState state)  {      LL_DEBUGS("Voice") << "Ice Gathering voice account. " << state << LL_ENDL;      switch (state)      { -        case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_COMPLETE: +        case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_COMPLETE:          {              LLMutexLock lock(&mVoiceStateMutex);              mIceCompleted = true;              break;          } -        case llwebrtc::LLWebRTCSignalingObserver::IceGatheringState::ICE_GATHERING_NEW: +        case llwebrtc::LLWebRTCSignalingObserver::EIceGatheringState::ICE_GATHERING_NEW:          {              LLMutexLock lock(&mVoiceStateMutex);              mIceCompleted = false; @@ -2167,9 +2167,9 @@ void LLVoiceWebRTCConnection::processIceUpdates()                      for (auto &ice_candidate : mIceCandidates)                      {                          LLSD body_candidate; -                        body_candidate["sdpMid"]        = ice_candidate.sdp_mid; -                        body_candidate["sdpMLineIndex"] = ice_candidate.mline_index; -                        body_candidate["candidate"]     = ice_candidate.candidate; +                        body_candidate["sdpMid"]        = ice_candidate.mSdpMid; +                        body_candidate["sdpMLineIndex"] = ice_candidate.mMLineIndex; +                        body_candidate["candidate"]     = ice_candidate.mCandidate;                          candidates.append(body_candidate);                      }                      body["candidates"] = candidates; @@ -2242,7 +2242,7 @@ void LLVoiceWebRTCConnection::OnRenegotiationNeeded()      }  } -void LLVoiceWebRTCConnection::OnPeerShutDown() +void LLVoiceWebRTCConnection::OnPeerConnectionShutdown()  {      setVoiceConnectionState(VOICE_STATE_SESSION_EXIT);      mOutstandingRequests--;  // shut down is an async call which is handled on a webrtc thread. @@ -2364,9 +2364,9 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestSuccess(const LLSD &res          return;      } -    if (mWebRTCPeerConnection) +    if (mWebRTCPeerConnectionInterface)      { -        if (mWebRTCPeerConnection->shutdownConnection()) +        if (mWebRTCPeerConnectionInterface->shutdownConnection())          {              mOutstandingRequests++;          } @@ -2395,10 +2395,10 @@ void LLVoiceWebRTCConnection::OnVoiceDisconnectionRequestFailure(std::string url              boost::bind(&LLVoiceWebRTCSpatialConnection::OnVoiceDisconnectionRequestFailure, this, url, retries - 1, body, _1));          return;      } -    if (mWebRTCPeerConnection) +    if (mWebRTCPeerConnectionInterface)      {          mOutstandingRequests++; -        mWebRTCPeerConnection->shutdownConnection(); +        mWebRTCPeerConnectionInterface->shutdownConnection();      }      else      { @@ -2482,7 +2482,7 @@ void LLVoiceWebRTCConnection::OnVoiceConnectionRequestSuccess(const LLSD &result      LL_DEBUGS("Voice") << "ProvisionVoiceAccountRequest response"                         << " channel sdp " << mRemoteChannelSDP << LL_ENDL; -    mWebRTCPeerConnection->AnswerAvailable(mRemoteChannelSDP); +    mWebRTCPeerConnectionInterface->AnswerAvailable(mRemoteChannelSDP);  } @@ -2531,7 +2531,7 @@ bool LLVoiceWebRTCConnection::connectionStateMachine()              // tell the webrtc library that we want a connection.  The library will              // respond with an offer on a separate thread, which will cause              // the session state to change. -            if (!mWebRTCPeerConnection->initializeConnection()) +            if (!mWebRTCPeerConnectionInterface->initializeConnection())              {                  setVoiceConnectionState(VOICE_STATE_SESSION_RETRY);              } @@ -2830,7 +2830,7 @@ LLVoiceWebRTCSpatialConnection::~LLVoiceWebRTCSpatialConnection()          return;      }      assert(mOutstandingRequests == 0); -    mWebRTCPeerConnection->unsetSignalingObserver(this); +    mWebRTCPeerConnectionInterface->unsetSignalingObserver(this);  }  void LLVoiceWebRTCSpatialConnection::setMuteMic(bool muted) @@ -2873,7 +2873,7 @@ LLVoiceWebRTCAdHocConnection::~LLVoiceWebRTCAdHocConnection()          return;      }      assert(mOutstandingRequests == 0); -    mWebRTCPeerConnection->unsetSignalingObserver(this); +    mWebRTCPeerConnectionInterface->unsetSignalingObserver(this);  }  // Add-hoc connections require a different channel type diff --git a/indra/newview/llvoicewebrtc.h b/indra/newview/llvoicewebrtc.h index e6f01fd181..3ff801ed56 100644 --- a/indra/newview/llvoicewebrtc.h +++ b/indra/newview/llvoicewebrtc.h @@ -584,12 +584,12 @@ class LLVoiceWebRTCConnection :      /// @name Signaling notification      //  LLWebRTCSignalingObserver      //@{ -    void OnIceGatheringState(IceGatheringState state) override; +    void OnIceGatheringState(EIceGatheringState state) override;      void OnIceCandidate(const llwebrtc::LLWebRTCIceCandidate &candidate) override;      void OnOfferAvailable(const std::string &sdp) override;      void OnRenegotiationNeeded() override;      void OnAudioEstablished(llwebrtc::LLWebRTCAudioInterface *audio_interface) override; -    void OnPeerShutDown() override; +    void OnPeerConnectionShutdown() override;      //@}      ///////////////////////// @@ -702,7 +702,7 @@ class LLVoiceWebRTCConnection :      bool                                        mIceCompleted;      bool                                        mTrickling; -    llwebrtc::LLWebRTCPeerConnection *mWebRTCPeerConnection; +    llwebrtc::LLWebRTCPeerConnectionInterface *mWebRTCPeerConnectionInterface;      llwebrtc::LLWebRTCAudioInterface *mWebRTCAudioInterface;      llwebrtc::LLWebRTCDataInterface  *mWebRTCDataInterface;  };  | 
