/* * Copyright (c) 2012 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 "voice_engine/voe_base_impl.h" #include "common_audio/signal_processing/include/signal_processing_library.h" #include "modules/audio_coding/include/audio_coding_module.h" #include "modules/audio_device/audio_device_impl.h" #include "modules/audio_processing/include/audio_processing.h" #include "rtc_base/format_macros.h" #include "rtc_base/location.h" #include "rtc_base/logging.h" #include "voice_engine/channel.h" #include "voice_engine/include/voe_errors.h" #include "voice_engine/transmit_mixer.h" #include "voice_engine/voice_engine_impl.h" namespace webrtc { VoEBase* VoEBase::GetInterface(VoiceEngine* voiceEngine) { if (nullptr == voiceEngine) { return nullptr; } VoiceEngineImpl* s = static_cast(voiceEngine); s->AddRef(); return s; } VoEBaseImpl::VoEBaseImpl(voe::SharedData* shared) : shared_(shared) {} VoEBaseImpl::~VoEBaseImpl() { TerminateInternal(); } int32_t VoEBaseImpl::RecordedDataIsAvailable( const void* audio_data, const size_t number_of_frames, const size_t bytes_per_sample, const size_t number_of_channels, const uint32_t sample_rate, const uint32_t audio_delay_milliseconds, const int32_t clock_drift, const uint32_t volume, const bool key_pressed, uint32_t& new_mic_volume) { RTC_DCHECK_EQ(2 * number_of_channels, bytes_per_sample); RTC_DCHECK(shared_->transmit_mixer() != nullptr); RTC_DCHECK(shared_->audio_device() != nullptr); constexpr uint32_t kMaxVolumeLevel = 255; uint32_t max_volume = 0; uint16_t voe_mic_level = 0; // Check for zero to skip this calculation; the consumer may use this to // indicate no volume is available. if (volume != 0) { // Scale from ADM to VoE level range if (shared_->audio_device()->MaxMicrophoneVolume(&max_volume) == 0) { if (max_volume) { voe_mic_level = static_cast( (volume * kMaxVolumeLevel + static_cast(max_volume / 2)) / max_volume); } } // We learned that on certain systems (e.g Linux) the voe_mic_level // can be greater than the maxVolumeLevel therefore // we are going to cap the voe_mic_level to the maxVolumeLevel // and change the maxVolume to volume if it turns out that // the voe_mic_level is indeed greater than the maxVolumeLevel. if (voe_mic_level > kMaxVolumeLevel) { voe_mic_level = kMaxVolumeLevel; max_volume = volume; } } // Perform channel-independent operations // (APM, mix with file, record to file, mute, etc.) shared_->transmit_mixer()->PrepareDemux( audio_data, number_of_frames, number_of_channels, sample_rate, static_cast(audio_delay_milliseconds), clock_drift, voe_mic_level, key_pressed); // Copy the audio frame to each sending channel and perform // channel-dependent operations (file mixing, mute, etc.), encode and // packetize+transmit the RTP packet. shared_->transmit_mixer()->ProcessAndEncodeAudio(); // Scale from VoE to ADM level range. uint32_t new_voe_mic_level = shared_->transmit_mixer()->CaptureLevel(); if (new_voe_mic_level != voe_mic_level) { // Return the new volume if AGC has changed the volume. return static_cast((new_voe_mic_level * max_volume + static_cast(kMaxVolumeLevel / 2)) / kMaxVolumeLevel); } return 0; } int32_t VoEBaseImpl::NeedMorePlayData(const size_t nSamples, const size_t nBytesPerSample, const size_t nChannels, const uint32_t samplesPerSec, void* audioSamples, size_t& nSamplesOut, int64_t* elapsed_time_ms, int64_t* ntp_time_ms) { RTC_NOTREACHED(); return 0; } void VoEBaseImpl::PushCaptureData(int voe_channel, const void* audio_data, int bits_per_sample, int sample_rate, size_t number_of_channels, size_t number_of_frames) { voe::ChannelOwner ch = shared_->channel_manager().GetChannel(voe_channel); voe::Channel* channel = ch.channel(); if (!channel) return; if (channel->Sending()) { // Send the audio to each channel directly without using the APM in the // transmit mixer. channel->ProcessAndEncodeAudio(static_cast(audio_data), sample_rate, number_of_frames, number_of_channels); } } void VoEBaseImpl::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) { RTC_NOTREACHED(); } int VoEBaseImpl::Init( AudioDeviceModule* audio_device, AudioProcessing* audio_processing, const rtc::scoped_refptr& decoder_factory) { RTC_DCHECK(audio_device); RTC_DCHECK(audio_processing); rtc::CritScope cs(shared_->crit_sec()); if (shared_->process_thread()) { shared_->process_thread()->Start(); } shared_->set_audio_device(audio_device); shared_->set_audio_processing(audio_processing); RTC_DCHECK(decoder_factory); decoder_factory_ = decoder_factory; return 0; } void VoEBaseImpl::Terminate() { rtc::CritScope cs(shared_->crit_sec()); TerminateInternal(); } int VoEBaseImpl::CreateChannel() { return CreateChannel(ChannelConfig()); } int VoEBaseImpl::CreateChannel(const ChannelConfig& config) { rtc::CritScope cs(shared_->crit_sec()); ChannelConfig config_copy(config); config_copy.acm_config.decoder_factory = decoder_factory_; voe::ChannelOwner channel_owner = shared_->channel_manager().CreateChannel(config_copy); return InitializeChannel(&channel_owner); } int VoEBaseImpl::InitializeChannel(voe::ChannelOwner* channel_owner) { if (channel_owner->channel()->SetEngineInformation( *shared_->process_thread(), *shared_->audio_device(), shared_->encoder_queue()) != 0) { RTC_LOG(LS_ERROR) << "CreateChannel() failed to associate engine and channel." " Destroying channel."; shared_->channel_manager().DestroyChannel( channel_owner->channel()->ChannelId()); return -1; } else if (channel_owner->channel()->Init() != 0) { RTC_LOG(LS_ERROR) << "CreateChannel() failed to initialize channel. Destroying" " channel."; shared_->channel_manager().DestroyChannel( channel_owner->channel()->ChannelId()); return -1; } return channel_owner->channel()->ChannelId(); } int VoEBaseImpl::DeleteChannel(int channel) { rtc::CritScope cs(shared_->crit_sec()); { voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel); voe::Channel* channelPtr = ch.channel(); if (channelPtr == nullptr) { RTC_LOG(LS_ERROR) << "DeleteChannel() failed to locate channel"; return -1; } } shared_->channel_manager().DestroyChannel(channel); if (StopSend() != 0) { return -1; } if (StopPlayout() != 0) { return -1; } return 0; } int VoEBaseImpl::StartPlayout(int channel) { rtc::CritScope cs(shared_->crit_sec()); voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel); voe::Channel* channelPtr = ch.channel(); if (channelPtr == nullptr) { RTC_LOG(LS_ERROR) << "StartPlayout() failed to locate channel"; return -1; } if (channelPtr->Playing()) { return 0; } if (StartPlayout() != 0) { RTC_LOG(LS_ERROR) << "StartPlayout() failed to start playout"; return -1; } return channelPtr->StartPlayout(); } int VoEBaseImpl::StopPlayout(int channel) { rtc::CritScope cs(shared_->crit_sec()); voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel); voe::Channel* channelPtr = ch.channel(); if (channelPtr == nullptr) { RTC_LOG(LS_ERROR) << "StopPlayout() failed to locate channel"; return -1; } if (channelPtr->StopPlayout() != 0) { RTC_LOG_F(LS_WARNING) << "StopPlayout() failed to stop playout for channel " << channel; } return StopPlayout(); } int VoEBaseImpl::StartSend(int channel) { rtc::CritScope cs(shared_->crit_sec()); voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel); voe::Channel* channelPtr = ch.channel(); if (channelPtr == nullptr) { RTC_LOG(LS_ERROR) << "StartSend() failed to locate channel"; return -1; } if (channelPtr->Sending()) { return 0; } if (StartSend() != 0) { RTC_LOG(LS_ERROR) << "StartSend() failed to start recording"; return -1; } return channelPtr->StartSend(); } int VoEBaseImpl::StopSend(int channel) { rtc::CritScope cs(shared_->crit_sec()); voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel); voe::Channel* channelPtr = ch.channel(); if (channelPtr == nullptr) { RTC_LOG(LS_ERROR) << "StopSend() failed to locate channel"; return -1; } channelPtr->StopSend(); return StopSend(); } int32_t VoEBaseImpl::StartPlayout() { if (!shared_->audio_device()->Playing()) { if (shared_->audio_device()->InitPlayout() != 0) { RTC_LOG_F(LS_ERROR) << "Failed to initialize playout"; return -1; } if (playout_enabled_ && shared_->audio_device()->StartPlayout() != 0) { RTC_LOG_F(LS_ERROR) << "Failed to start playout"; return -1; } } return 0; } int32_t VoEBaseImpl::StopPlayout() { if (!playout_enabled_) { return 0; } // Stop audio-device playing if no channel is playing out. if (shared_->NumOfPlayingChannels() == 0) { if (shared_->audio_device()->StopPlayout() != 0) { RTC_LOG(LS_ERROR) << "StopPlayout() failed to stop playout"; return -1; } } return 0; } int32_t VoEBaseImpl::StartSend() { if (!shared_->audio_device()->Recording()) { if (shared_->audio_device()->InitRecording() != 0) { RTC_LOG_F(LS_ERROR) << "Failed to initialize recording"; return -1; } if (recording_enabled_ && shared_->audio_device()->StartRecording() != 0) { RTC_LOG_F(LS_ERROR) << "Failed to start recording"; return -1; } } return 0; } int32_t VoEBaseImpl::StopSend() { if (!recording_enabled_) { return 0; } // Stop audio-device recording if no channel is recording. if (shared_->NumOfSendingChannels() == 0) { if (shared_->audio_device()->StopRecording() != 0) { RTC_LOG(LS_ERROR) << "StopSend() failed to stop recording"; return -1; } shared_->transmit_mixer()->StopSend(); } return 0; } int32_t VoEBaseImpl::SetPlayout(bool enabled) { RTC_LOG(INFO) << "SetPlayout(" << enabled << ")"; if (playout_enabled_ == enabled) { return 0; } playout_enabled_ = enabled; if (shared_->NumOfPlayingChannels() == 0) { // If there are no channels attempting to play out yet, there's nothing to // be done; we should be in a "not playing out" state either way. return 0; } int32_t ret; if (enabled) { ret = shared_->audio_device()->StartPlayout(); if (ret != 0) { RTC_LOG(LS_ERROR) << "SetPlayout(true) failed to start playout"; } } else { ret = shared_->audio_device()->StopPlayout(); if (ret != 0) { RTC_LOG(LS_ERROR) << "SetPlayout(false) failed to stop playout"; } } return ret; } int32_t VoEBaseImpl::SetRecording(bool enabled) { RTC_LOG(INFO) << "SetRecording(" << enabled << ")"; if (recording_enabled_ == enabled) { return 0; } recording_enabled_ = enabled; if (shared_->NumOfSendingChannels() == 0) { // If there are no channels attempting to record out yet, there's nothing to // be done; we should be in a "not recording" state either way. return 0; } int32_t ret; if (enabled) { ret = shared_->audio_device()->StartRecording(); if (ret != 0) { RTC_LOG(LS_ERROR) << "SetRecording(true) failed to start recording"; } } else { ret = shared_->audio_device()->StopRecording(); if (ret != 0) { RTC_LOG(LS_ERROR) << "SetRecording(false) failed to stop recording"; } } return ret; } void VoEBaseImpl::TerminateInternal() { // Delete any remaining channel objects shared_->channel_manager().DestroyAllChannels(); if (shared_->process_thread()) { shared_->process_thread()->Stop(); } shared_->set_audio_device(nullptr); shared_->set_audio_processing(nullptr); } } // namespace webrtc