diff options
Diffstat (limited to 'third_party/libwebrtc/audio/voip/voip_core.cc')
-rw-r--r-- | third_party/libwebrtc/audio/voip/voip_core.cc | 500 |
1 files changed, 500 insertions, 0 deletions
diff --git a/third_party/libwebrtc/audio/voip/voip_core.cc b/third_party/libwebrtc/audio/voip/voip_core.cc new file mode 100644 index 0000000000..8df1c594aa --- /dev/null +++ b/third_party/libwebrtc/audio/voip/voip_core.cc @@ -0,0 +1,500 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "audio/voip/voip_core.h" + +#include <algorithm> +#include <memory> +#include <utility> + +#include "api/audio_codecs/audio_format.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +namespace { + +// For Windows, use specific enum type to initialize default audio device as +// defined in AudioDeviceModule::WindowsDeviceType. +#if defined(WEBRTC_WIN) +constexpr AudioDeviceModule::WindowsDeviceType kAudioDeviceId = + AudioDeviceModule::WindowsDeviceType::kDefaultCommunicationDevice; +#else +constexpr uint16_t kAudioDeviceId = 0; +#endif // defined(WEBRTC_WIN) + +// Maximum value range limit on ChannelId. This can be increased without any +// side effect and only set at this moderate value for better readability for +// logging. +static constexpr int kMaxChannelId = 100000; + +} // namespace + +VoipCore::VoipCore(rtc::scoped_refptr<AudioEncoderFactory> encoder_factory, + rtc::scoped_refptr<AudioDecoderFactory> decoder_factory, + std::unique_ptr<TaskQueueFactory> task_queue_factory, + rtc::scoped_refptr<AudioDeviceModule> audio_device_module, + rtc::scoped_refptr<AudioProcessing> audio_processing) { + encoder_factory_ = std::move(encoder_factory); + decoder_factory_ = std::move(decoder_factory); + task_queue_factory_ = std::move(task_queue_factory); + audio_device_module_ = std::move(audio_device_module); + audio_processing_ = std::move(audio_processing); + audio_mixer_ = AudioMixerImpl::Create(); + + // AudioTransportImpl depends on audio mixer and audio processing instances. + audio_transport_ = std::make_unique<AudioTransportImpl>( + audio_mixer_.get(), audio_processing_.get(), nullptr); +} + +bool VoipCore::InitializeIfNeeded() { + // `audio_device_module_` internally owns a lock and the whole logic here + // needs to be executed atomically once using another lock in VoipCore. + // Further changes in this method will need to make sure that no deadlock is + // introduced in the future. + MutexLock lock(&lock_); + + if (initialized_) { + return true; + } + + // Initialize ADM. + if (audio_device_module_->Init() != 0) { + RTC_LOG(LS_ERROR) << "Failed to initialize the ADM."; + return false; + } + + // Note that failures on initializing default recording/speaker devices are + // not considered to be fatal here. In certain case, caller may not care about + // recording device functioning (e.g webinar where only speaker is available). + // It's also possible that there are other audio devices available that may + // work. + + // Initialize default speaker device. + if (audio_device_module_->SetPlayoutDevice(kAudioDeviceId) != 0) { + RTC_LOG(LS_WARNING) << "Unable to set playout device."; + } + if (audio_device_module_->InitSpeaker() != 0) { + RTC_LOG(LS_WARNING) << "Unable to access speaker."; + } + + // Initialize default recording device. + if (audio_device_module_->SetRecordingDevice(kAudioDeviceId) != 0) { + RTC_LOG(LS_WARNING) << "Unable to set recording device."; + } + if (audio_device_module_->InitMicrophone() != 0) { + RTC_LOG(LS_WARNING) << "Unable to access microphone."; + } + + // Set number of channels on speaker device. + bool available = false; + if (audio_device_module_->StereoPlayoutIsAvailable(&available) != 0) { + RTC_LOG(LS_WARNING) << "Unable to query stereo playout."; + } + if (audio_device_module_->SetStereoPlayout(available) != 0) { + RTC_LOG(LS_WARNING) << "Unable to set mono/stereo playout mode."; + } + + // Set number of channels on recording device. + available = false; + if (audio_device_module_->StereoRecordingIsAvailable(&available) != 0) { + RTC_LOG(LS_WARNING) << "Unable to query stereo recording."; + } + if (audio_device_module_->SetStereoRecording(available) != 0) { + RTC_LOG(LS_WARNING) << "Unable to set stereo recording mode."; + } + + if (audio_device_module_->RegisterAudioCallback(audio_transport_.get()) != + 0) { + RTC_LOG(LS_WARNING) << "Unable to register audio callback."; + } + + initialized_ = true; + + return true; +} + +ChannelId VoipCore::CreateChannel(Transport* transport, + absl::optional<uint32_t> local_ssrc) { + ChannelId channel_id; + + // Set local ssrc to random if not set by caller. + if (!local_ssrc) { + Random random(rtc::TimeMicros()); + local_ssrc = random.Rand<uint32_t>(); + } + + rtc::scoped_refptr<AudioChannel> channel = + rtc::make_ref_counted<AudioChannel>(transport, local_ssrc.value(), + task_queue_factory_.get(), + audio_mixer_.get(), decoder_factory_); + + { + MutexLock lock(&lock_); + + channel_id = static_cast<ChannelId>(next_channel_id_); + channels_[channel_id] = channel; + next_channel_id_++; + if (next_channel_id_ >= kMaxChannelId) { + next_channel_id_ = 0; + } + } + + // Set ChannelId in audio channel for logging/debugging purpose. + channel->SetId(channel_id); + + return channel_id; +} + +VoipResult VoipCore::ReleaseChannel(ChannelId channel_id) { + // Destroy channel outside of the lock. + rtc::scoped_refptr<AudioChannel> channel; + + bool no_channels_after_release = false; + + { + MutexLock lock(&lock_); + + auto iter = channels_.find(channel_id); + if (iter != channels_.end()) { + channel = std::move(iter->second); + channels_.erase(iter); + } + + no_channels_after_release = channels_.empty(); + } + + VoipResult status_code = VoipResult::kOk; + if (!channel) { + RTC_LOG(LS_WARNING) << "Channel " << channel_id << " not found"; + status_code = VoipResult::kInvalidArgument; + } + + if (no_channels_after_release) { + // TODO(bugs.webrtc.org/11581): unclear if we still need to clear `channel` + // here. + channel = nullptr; + + // Make sure to stop playout on ADM if it is playing. + if (audio_device_module_->Playing()) { + if (audio_device_module_->StopPlayout() != 0) { + RTC_LOG(LS_WARNING) << "StopPlayout failed"; + status_code = VoipResult::kInternal; + } + } + } + + return status_code; +} + +rtc::scoped_refptr<AudioChannel> VoipCore::GetChannel(ChannelId channel_id) { + rtc::scoped_refptr<AudioChannel> channel; + { + MutexLock lock(&lock_); + auto iter = channels_.find(channel_id); + if (iter != channels_.end()) { + channel = iter->second; + } + } + if (!channel) { + RTC_LOG(LS_ERROR) << "Channel " << channel_id << " not found"; + } + return channel; +} + +bool VoipCore::UpdateAudioTransportWithSenders() { + std::vector<AudioSender*> audio_senders; + + // Gather a list of audio channel that are currently sending along with + // highest sampling rate and channel numbers to configure into audio + // transport. + int max_sampling_rate = 8000; + size_t max_num_channels = 1; + { + MutexLock lock(&lock_); + // Reserve to prevent run time vector re-allocation. + audio_senders.reserve(channels_.size()); + for (auto kv : channels_) { + rtc::scoped_refptr<AudioChannel>& channel = kv.second; + if (channel->IsSendingMedia()) { + auto encoder_format = channel->GetEncoderFormat(); + if (!encoder_format) { + RTC_LOG(LS_ERROR) + << "channel " << channel->GetId() << " encoder is not set"; + continue; + } + audio_senders.push_back(channel->GetAudioSender()); + max_sampling_rate = + std::max(max_sampling_rate, encoder_format->clockrate_hz); + max_num_channels = + std::max(max_num_channels, encoder_format->num_channels); + } + } + } + + audio_transport_->UpdateAudioSenders(audio_senders, max_sampling_rate, + max_num_channels); + + // Depending on availability of senders, turn on or off ADM recording. + if (!audio_senders.empty()) { + // Initialize audio device module and default device if needed. + if (!InitializeIfNeeded()) { + return false; + } + + if (!audio_device_module_->Recording()) { + if (audio_device_module_->InitRecording() != 0) { + RTC_LOG(LS_ERROR) << "InitRecording failed"; + return false; + } + if (audio_device_module_->StartRecording() != 0) { + RTC_LOG(LS_ERROR) << "StartRecording failed"; + return false; + } + } + } else { + if (audio_device_module_->Recording() && + audio_device_module_->StopRecording() != 0) { + RTC_LOG(LS_ERROR) << "StopRecording failed"; + return false; + } + } + return true; +} + +VoipResult VoipCore::StartSend(ChannelId channel_id) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + if (!channel->StartSend()) { + return VoipResult::kFailedPrecondition; + } + + return UpdateAudioTransportWithSenders() ? VoipResult::kOk + : VoipResult::kInternal; +} + +VoipResult VoipCore::StopSend(ChannelId channel_id) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel->StopSend(); + + return UpdateAudioTransportWithSenders() ? VoipResult::kOk + : VoipResult::kInternal; +} + +VoipResult VoipCore::StartPlayout(ChannelId channel_id) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + if (channel->IsPlaying()) { + return VoipResult::kOk; + } + + if (!channel->StartPlay()) { + return VoipResult::kFailedPrecondition; + } + + // Initialize audio device module and default device if needed. + if (!InitializeIfNeeded()) { + return VoipResult::kInternal; + } + + if (!audio_device_module_->Playing()) { + if (audio_device_module_->InitPlayout() != 0) { + RTC_LOG(LS_ERROR) << "InitPlayout failed"; + return VoipResult::kInternal; + } + if (audio_device_module_->StartPlayout() != 0) { + RTC_LOG(LS_ERROR) << "StartPlayout failed"; + return VoipResult::kInternal; + } + } + + return VoipResult::kOk; +} + +VoipResult VoipCore::StopPlayout(ChannelId channel_id) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel->StopPlay(); + + return VoipResult::kOk; +} + +VoipResult VoipCore::ReceivedRTPPacket( + ChannelId channel_id, + rtc::ArrayView<const uint8_t> rtp_packet) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel->ReceivedRTPPacket(rtp_packet); + + return VoipResult::kOk; +} + +VoipResult VoipCore::ReceivedRTCPPacket( + ChannelId channel_id, + rtc::ArrayView<const uint8_t> rtcp_packet) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel->ReceivedRTCPPacket(rtcp_packet); + + return VoipResult::kOk; +} + +VoipResult VoipCore::SetSendCodec(ChannelId channel_id, + int payload_type, + const SdpAudioFormat& encoder_format) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + auto encoder = encoder_factory_->MakeAudioEncoder( + payload_type, encoder_format, absl::nullopt); + channel->SetEncoder(payload_type, encoder_format, std::move(encoder)); + + return VoipResult::kOk; +} + +VoipResult VoipCore::SetReceiveCodecs( + ChannelId channel_id, + const std::map<int, SdpAudioFormat>& decoder_specs) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel->SetReceiveCodecs(decoder_specs); + + return VoipResult::kOk; +} + +VoipResult VoipCore::RegisterTelephoneEventType(ChannelId channel_id, + int rtp_payload_type, + int sample_rate_hz) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel->RegisterTelephoneEventType(rtp_payload_type, sample_rate_hz); + + return VoipResult::kOk; +} + +VoipResult VoipCore::SendDtmfEvent(ChannelId channel_id, + DtmfEvent dtmf_event, + int duration_ms) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + return (channel->SendTelephoneEvent(static_cast<int>(dtmf_event), duration_ms) + ? VoipResult::kOk + : VoipResult::kFailedPrecondition); +} + +VoipResult VoipCore::GetIngressStatistics(ChannelId channel_id, + IngressStatistics& ingress_stats) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + ingress_stats = channel->GetIngressStatistics(); + + return VoipResult::kOk; +} + +VoipResult VoipCore::GetChannelStatistics(ChannelId channel_id, + ChannelStatistics& channel_stats) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel_stats = channel->GetChannelStatistics(); + + return VoipResult::kOk; +} + +VoipResult VoipCore::SetInputMuted(ChannelId channel_id, bool enable) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel->SetMute(enable); + + return VoipResult::kOk; +} + +VoipResult VoipCore::GetInputVolumeInfo(ChannelId channel_id, + VolumeInfo& input_volume) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + input_volume.audio_level = channel->GetInputAudioLevel(); + input_volume.total_energy = channel->GetInputTotalEnergy(); + input_volume.total_duration = channel->GetInputTotalDuration(); + + return VoipResult::kOk; +} + +VoipResult VoipCore::GetOutputVolumeInfo(ChannelId channel_id, + VolumeInfo& output_volume) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + output_volume.audio_level = channel->GetOutputAudioLevel(); + output_volume.total_energy = channel->GetOutputTotalEnergy(); + output_volume.total_duration = channel->GetOutputTotalDuration(); + + return VoipResult::kOk; +} + +} // namespace webrtc |