mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-16 15:20:42 +01:00

Bug: webrtc:342905193 No-Try: True Change-Id: Icc968be43b8830038ea9a1f5f604307220457807 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/361021 Auto-Submit: Florent Castelli <orphis@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Commit-Queue: Florent Castelli <orphis@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42911}
499 lines
15 KiB
C++
499 lines
15 KiB
C++
/*
|
|
* 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(const Environment& env,
|
|
rtc::scoped_refptr<AudioEncoderFactory> encoder_factory,
|
|
rtc::scoped_refptr<AudioDecoderFactory> decoder_factory,
|
|
rtc::scoped_refptr<AudioDeviceModule> audio_device_module,
|
|
rtc::scoped_refptr<AudioProcessing> audio_processing)
|
|
: env_(env),
|
|
encoder_factory_(std::move(encoder_factory)),
|
|
decoder_factory_(std::move(decoder_factory)),
|
|
audio_processing_(std::move(audio_processing)),
|
|
audio_device_module_(std::move(audio_device_module)) {
|
|
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,
|
|
std::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>(env_, transport, local_ssrc.value(),
|
|
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_->Create(env_, encoder_format,
|
|
{.payload_type = payload_type});
|
|
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
|