webrtc_m130/webrtc/voice_engine/voe_base_impl.cc
Jelena Marusic f353dd59b5 VoE: cleanup VoENetwork implementation
Changes:
1. Documented return values of VoENetwork methods.
2. In VoENetworkImpl: replaced calls to SetLastError() with LOG_F(). SetLastError() is not used anymore because no one is calling LastError() to check for last error. Also, its usage is being removed in Video Engine and we want to be consistent.
3. In VoENetworkImpl: removed WEBRTC_TRACE() usage.
4. In VoENetworkImpl: replaced some defensive code with assert(). We are now assuming that the caller has called VoEBase::Init() where needed. We are also assuming that it is invalid to pass nullptr where data is expected.
5. Updated unit tests accordingly.

R=henrika@webrtc.org, kwiberg@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/53369004

Cr-Commit-Position: refs/heads/master@{#9145}
2015-05-06 13:04:29 +00:00

841 lines
29 KiB
C++

/*
* 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 "webrtc/voice_engine/voe_base_impl.h"
#include "webrtc/common.h"
#include "webrtc/common_audio/signal_processing/include/signal_processing_library.h"
#include "webrtc/modules/audio_coding/main/interface/audio_coding_module.h"
#include "webrtc/modules/audio_device/audio_device_impl.h"
#include "webrtc/modules/audio_processing/include/audio_processing.h"
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
#include "webrtc/system_wrappers/interface/file_wrapper.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/voice_engine/channel.h"
#include "webrtc/voice_engine/include/voe_errors.h"
#include "webrtc/voice_engine/output_mixer.h"
#include "webrtc/voice_engine/transmit_mixer.h"
#include "webrtc/voice_engine/utility.h"
#include "webrtc/voice_engine/voice_engine_impl.h"
namespace webrtc {
VoEBase* VoEBase::GetInterface(VoiceEngine* voiceEngine) {
if (nullptr == voiceEngine) {
return nullptr;
}
VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
s->AddRef();
return s;
}
VoEBaseImpl::VoEBaseImpl(voe::SharedData* shared)
: voiceEngineObserverPtr_(nullptr),
callbackCritSect_(*CriticalSectionWrapper::CreateCriticalSection()),
shared_(shared) {}
VoEBaseImpl::~VoEBaseImpl() {
TerminateInternal();
delete &callbackCritSect_;
}
void VoEBaseImpl::OnErrorIsReported(ErrorCode error) {
CriticalSectionScoped cs(&callbackCritSect_);
int errCode = 0;
if (error == AudioDeviceObserver::kRecordingError) {
errCode = VE_RUNTIME_REC_ERROR;
LOG_F(LS_ERROR) << "VE_RUNTIME_REC_ERROR";
} else if (error == AudioDeviceObserver::kPlayoutError) {
errCode = VE_RUNTIME_PLAY_ERROR;
LOG_F(LS_ERROR) << "VE_RUNTIME_PLAY_ERROR";
}
if (voiceEngineObserverPtr_) {
// Deliver callback (-1 <=> no channel dependency)
voiceEngineObserverPtr_->CallbackOnError(-1, errCode);
}
}
void VoEBaseImpl::OnWarningIsReported(WarningCode warning) {
CriticalSectionScoped cs(&callbackCritSect_);
int warningCode = 0;
if (warning == AudioDeviceObserver::kRecordingWarning) {
warningCode = VE_RUNTIME_REC_WARNING;
LOG_F(LS_WARNING) << "VE_RUNTIME_REC_WARNING";
} else if (warning == AudioDeviceObserver::kPlayoutWarning) {
warningCode = VE_RUNTIME_PLAY_WARNING;
LOG_F(LS_WARNING) << "VE_RUNTIME_PLAY_WARNING";
}
if (voiceEngineObserverPtr_) {
// Deliver callback (-1 <=> no channel dependency)
voiceEngineObserverPtr_->CallbackOnError(-1, warningCode);
}
}
int32_t VoEBaseImpl::RecordedDataIsAvailable(
const void* audioSamples, uint32_t nSamples, uint8_t nBytesPerSample,
uint8_t nChannels, uint32_t samplesPerSec, uint32_t totalDelayMS,
int32_t clockDrift, uint32_t micLevel, bool keyPressed,
uint32_t& newMicLevel) {
newMicLevel = static_cast<uint32_t>(ProcessRecordedDataWithAPM(
nullptr, 0, audioSamples, samplesPerSec, nChannels, nSamples,
totalDelayMS, clockDrift, micLevel, keyPressed));
return 0;
}
int32_t VoEBaseImpl::NeedMorePlayData(uint32_t nSamples,
uint8_t nBytesPerSample,
uint8_t nChannels, uint32_t samplesPerSec,
void* audioSamples, uint32_t& nSamplesOut,
int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) {
GetPlayoutData(static_cast<int>(samplesPerSec), static_cast<int>(nChannels),
static_cast<int>(nSamples), true, audioSamples,
elapsed_time_ms, ntp_time_ms);
nSamplesOut = audioFrame_.samples_per_channel_;
return 0;
}
int VoEBaseImpl::OnDataAvailable(const int voe_channels[],
int number_of_voe_channels,
const int16_t* audio_data, int sample_rate,
int number_of_channels, int number_of_frames,
int audio_delay_milliseconds, int volume,
bool key_pressed, bool need_audio_processing) {
if (number_of_voe_channels == 0) return 0;
if (need_audio_processing) {
return ProcessRecordedDataWithAPM(
voe_channels, number_of_voe_channels, audio_data, sample_rate,
number_of_channels, number_of_frames, audio_delay_milliseconds, 0,
volume, key_pressed);
}
// No need to go through the APM, demultiplex the data to each VoE channel,
// encode and send to the network.
for (int i = 0; i < number_of_voe_channels; ++i) {
// TODO(ajm): In the case where multiple channels are using the same codec
// rate, this path needlessly does extra conversions. We should convert once
// and share between channels.
PushCaptureData(voe_channels[i], audio_data, 16, sample_rate,
number_of_channels, number_of_frames);
}
// Return 0 to indicate no need to change the volume.
return 0;
}
void VoEBaseImpl::OnData(int voe_channel, const void* audio_data,
int bits_per_sample, int sample_rate,
int number_of_channels, int number_of_frames) {
PushCaptureData(voe_channel, audio_data, bits_per_sample, sample_rate,
number_of_channels, number_of_frames);
}
void VoEBaseImpl::PushCaptureData(int voe_channel, const void* audio_data,
int bits_per_sample, int sample_rate,
int number_of_channels,
int number_of_frames) {
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(voe_channel);
voe::Channel* channel_ptr = ch.channel();
if (!channel_ptr) return;
if (channel_ptr->Sending()) {
channel_ptr->Demultiplex(static_cast<const int16_t*>(audio_data),
sample_rate, number_of_frames, number_of_channels);
channel_ptr->PrepareEncodeAndSend(sample_rate);
channel_ptr->EncodeAndSend();
}
}
void VoEBaseImpl::PullRenderData(int bits_per_sample, int sample_rate,
int number_of_channels, int number_of_frames,
void* audio_data, int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) {
assert(bits_per_sample == 16);
assert(number_of_frames == static_cast<int>(sample_rate / 100));
GetPlayoutData(sample_rate, number_of_channels, number_of_frames, false,
audio_data, elapsed_time_ms, ntp_time_ms);
}
int VoEBaseImpl::RegisterVoiceEngineObserver(VoiceEngineObserver& observer) {
CriticalSectionScoped cs(&callbackCritSect_);
if (voiceEngineObserverPtr_) {
shared_->SetLastError(
VE_INVALID_OPERATION, kTraceError,
"RegisterVoiceEngineObserver() observer already enabled");
return -1;
}
// Register the observer in all active channels
for (voe::ChannelManager::Iterator it(&shared_->channel_manager());
it.IsValid(); it.Increment()) {
it.GetChannel()->RegisterVoiceEngineObserver(observer);
}
shared_->transmit_mixer()->RegisterVoiceEngineObserver(observer);
voiceEngineObserverPtr_ = &observer;
return 0;
}
int VoEBaseImpl::DeRegisterVoiceEngineObserver() {
CriticalSectionScoped cs(&callbackCritSect_);
if (!voiceEngineObserverPtr_) {
shared_->SetLastError(
VE_INVALID_OPERATION, kTraceError,
"DeRegisterVoiceEngineObserver() observer already disabled");
return 0;
}
voiceEngineObserverPtr_ = nullptr;
// Deregister the observer in all active channels
for (voe::ChannelManager::Iterator it(&shared_->channel_manager());
it.IsValid(); it.Increment()) {
it.GetChannel()->DeRegisterVoiceEngineObserver();
}
return 0;
}
int VoEBaseImpl::Init(AudioDeviceModule* external_adm,
AudioProcessing* audioproc) {
CriticalSectionScoped cs(shared_->crit_sec());
WebRtcSpl_Init();
if (shared_->statistics().Initialized()) {
return 0;
}
if (shared_->process_thread()) {
shared_->process_thread()->Start();
}
// Create an internal ADM if the user has not added an external
// ADM implementation as input to Init().
if (external_adm == nullptr) {
// Create the internal ADM implementation.
shared_->set_audio_device(AudioDeviceModuleImpl::Create(
VoEId(shared_->instance_id(), -1), shared_->audio_device_layer()));
if (shared_->audio_device() == nullptr) {
shared_->SetLastError(VE_NO_MEMORY, kTraceCritical,
"Init() failed to create the ADM");
return -1;
}
} else {
// Use the already existing external ADM implementation.
shared_->set_audio_device(external_adm);
LOG_F(LS_INFO)
<< "An external ADM implementation will be used in VoiceEngine";
}
// Register the ADM to the process thread, which will drive the error
// callback mechanism
if (shared_->process_thread()) {
shared_->process_thread()->RegisterModule(shared_->audio_device());
}
bool available = false;
// --------------------
// Reinitialize the ADM
// Register the AudioObserver implementation
if (shared_->audio_device()->RegisterEventObserver(this) != 0) {
shared_->SetLastError(
VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning,
"Init() failed to register event observer for the ADM");
}
// Register the AudioTransport implementation
if (shared_->audio_device()->RegisterAudioCallback(this) != 0) {
shared_->SetLastError(
VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning,
"Init() failed to register audio callback for the ADM");
}
// ADM initialization
if (shared_->audio_device()->Init() != 0) {
shared_->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
"Init() failed to initialize the ADM");
return -1;
}
// Initialize the default speaker
if (shared_->audio_device()->SetPlayoutDevice(
WEBRTC_VOICE_ENGINE_DEFAULT_DEVICE) != 0) {
shared_->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceInfo,
"Init() failed to set the default output device");
}
if (shared_->audio_device()->InitSpeaker() != 0) {
shared_->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceInfo,
"Init() failed to initialize the speaker");
}
// Initialize the default microphone
if (shared_->audio_device()->SetRecordingDevice(
WEBRTC_VOICE_ENGINE_DEFAULT_DEVICE) != 0) {
shared_->SetLastError(VE_SOUNDCARD_ERROR, kTraceInfo,
"Init() failed to set the default input device");
}
if (shared_->audio_device()->InitMicrophone() != 0) {
shared_->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceInfo,
"Init() failed to initialize the microphone");
}
// Set number of channels
if (shared_->audio_device()->StereoPlayoutIsAvailable(&available) != 0) {
shared_->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"Init() failed to query stereo playout mode");
}
if (shared_->audio_device()->SetStereoPlayout(available) != 0) {
shared_->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"Init() failed to set mono/stereo playout mode");
}
// TODO(andrew): These functions don't tell us whether stereo recording
// is truly available. We simply set the AudioProcessing input to stereo
// here, because we have to wait until receiving the first frame to
// determine the actual number of channels anyway.
//
// These functions may be changed; tracked here:
// http://code.google.com/p/webrtc/issues/detail?id=204
shared_->audio_device()->StereoRecordingIsAvailable(&available);
if (shared_->audio_device()->SetStereoRecording(available) != 0) {
shared_->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"Init() failed to set mono/stereo recording mode");
}
if (!audioproc) {
audioproc = AudioProcessing::Create();
if (!audioproc) {
LOG(LS_ERROR) << "Failed to create AudioProcessing.";
shared_->SetLastError(VE_NO_MEMORY);
return -1;
}
}
shared_->set_audio_processing(audioproc);
// Set the error state for any failures in this block.
shared_->SetLastError(VE_APM_ERROR);
// Configure AudioProcessing components.
if (audioproc->high_pass_filter()->Enable(true) != 0) {
LOG_F(LS_ERROR) << "Failed to enable high pass filter.";
return -1;
}
if (audioproc->echo_cancellation()->enable_drift_compensation(false) != 0) {
LOG_F(LS_ERROR) << "Failed to disable drift compensation.";
return -1;
}
if (audioproc->noise_suppression()->set_level(kDefaultNsMode) != 0) {
LOG_F(LS_ERROR) << "Failed to set noise suppression level: "
<< kDefaultNsMode;
return -1;
}
GainControl* agc = audioproc->gain_control();
if (agc->set_analog_level_limits(kMinVolumeLevel, kMaxVolumeLevel) != 0) {
LOG_F(LS_ERROR) << "Failed to set analog level limits with minimum: "
<< kMinVolumeLevel << " and maximum: " << kMaxVolumeLevel;
return -1;
}
if (agc->set_mode(kDefaultAgcMode) != 0) {
LOG_F(LS_ERROR) << "Failed to set mode: " << kDefaultAgcMode;
return -1;
}
if (agc->Enable(kDefaultAgcState) != 0) {
LOG_F(LS_ERROR) << "Failed to set agc state: " << kDefaultAgcState;
return -1;
}
shared_->SetLastError(0); // Clear error state.
#ifdef WEBRTC_VOICE_ENGINE_AGC
bool agc_enabled =
agc->mode() == GainControl::kAdaptiveAnalog && agc->is_enabled();
if (shared_->audio_device()->SetAGC(agc_enabled) != 0) {
LOG_F(LS_ERROR) << "Failed to set agc to enabled: " << agc_enabled;
shared_->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR);
// TODO(ajm): No error return here due to
// https://code.google.com/p/webrtc/issues/detail?id=1464
}
#endif
return shared_->statistics().SetInitialized();
}
int VoEBaseImpl::Terminate() {
CriticalSectionScoped cs(shared_->crit_sec());
return TerminateInternal();
}
int VoEBaseImpl::CreateChannel() {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner channel_owner = shared_->channel_manager().CreateChannel();
return InitializeChannel(&channel_owner);
}
int VoEBaseImpl::CreateChannel(const Config& config) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner channel_owner =
shared_->channel_manager().CreateChannel(config);
return InitializeChannel(&channel_owner);
}
int VoEBaseImpl::InitializeChannel(voe::ChannelOwner* channel_owner) {
if (channel_owner->channel()->SetEngineInformation(
shared_->statistics(), *shared_->output_mixer(),
*shared_->transmit_mixer(), *shared_->process_thread(),
*shared_->audio_device(), voiceEngineObserverPtr_,
&callbackCritSect_) != 0) {
shared_->SetLastError(
VE_CHANNEL_NOT_CREATED, kTraceError,
"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) {
shared_->SetLastError(
VE_CHANNEL_NOT_CREATED, kTraceError,
"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) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
{
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == nullptr) {
shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"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::StartReceive(int channel) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == nullptr) {
shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StartReceive() failed to locate channel");
return -1;
}
return channelPtr->StartReceiving();
}
int VoEBaseImpl::StopReceive(int channel) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == nullptr) {
shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetLocalReceiver() failed to locate channel");
return -1;
}
return channelPtr->StopReceiving();
}
int VoEBaseImpl::StartPlayout(int channel) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == nullptr) {
shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StartPlayout() failed to locate channel");
return -1;
}
if (channelPtr->Playing()) {
return 0;
}
if (StartPlayout() != 0) {
shared_->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
"StartPlayout() failed to start playout");
return -1;
}
return channelPtr->StartPlayout();
}
int VoEBaseImpl::StopPlayout(int channel) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == nullptr) {
shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StopPlayout() failed to locate channel");
return -1;
}
if (channelPtr->StopPlayout() != 0) {
LOG_F(LS_WARNING) << "StopPlayout() failed to stop playout for channel "
<< channel;
}
return StopPlayout();
}
int VoEBaseImpl::StartSend(int channel) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == nullptr) {
shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StartSend() failed to locate channel");
return -1;
}
if (channelPtr->Sending()) {
return 0;
}
if (StartSend() != 0) {
shared_->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
"StartSend() failed to start recording");
return -1;
}
return channelPtr->StartSend();
}
int VoEBaseImpl::StopSend(int channel) {
CriticalSectionScoped cs(shared_->crit_sec());
if (!shared_->statistics().Initialized()) {
shared_->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = shared_->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == nullptr) {
shared_->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StopSend() failed to locate channel");
return -1;
}
if (channelPtr->StopSend() != 0) {
LOG_F(LS_WARNING) << "StopSend() failed to stop sending for channel "
<< channel;
}
return StopSend();
}
int VoEBaseImpl::GetVersion(char version[1024]) {
assert(kVoiceEngineVersionMaxMessageSize == 1024);
if (version == nullptr) {
shared_->SetLastError(VE_INVALID_ARGUMENT, kTraceError);
return (-1);
}
char versionBuf[kVoiceEngineVersionMaxMessageSize];
char* versionPtr = versionBuf;
int32_t len = 0;
int32_t accLen = 0;
len = AddVoEVersion(versionPtr);
if (len == -1) {
return -1;
}
versionPtr += len;
accLen += len;
assert(accLen < kVoiceEngineVersionMaxMessageSize);
#ifdef WEBRTC_EXTERNAL_TRANSPORT
len = AddExternalTransportBuild(versionPtr);
if (len == -1) {
return -1;
}
versionPtr += len;
accLen += len;
assert(accLen < kVoiceEngineVersionMaxMessageSize);
#endif
memcpy(version, versionBuf, accLen);
version[accLen] = '\0';
// to avoid the truncation in the trace, split the string into parts
char partOfVersion[256];
for (int partStart = 0; partStart < accLen;) {
memset(partOfVersion, 0, sizeof(partOfVersion));
int partEnd = partStart + 180;
while (version[partEnd] != '\n' && version[partEnd] != '\0') {
partEnd--;
}
if (partEnd < accLen) {
memcpy(partOfVersion, &version[partStart], partEnd - partStart);
} else {
memcpy(partOfVersion, &version[partStart], accLen - partStart);
}
partStart = partEnd;
}
return 0;
}
int32_t VoEBaseImpl::AddVoEVersion(char* str) const {
return sprintf(str, "VoiceEngine 4.1.0\n");
}
#ifdef WEBRTC_EXTERNAL_TRANSPORT
int32_t VoEBaseImpl::AddExternalTransportBuild(char* str) const {
return sprintf(str, "External transport build\n");
}
#endif
int VoEBaseImpl::LastError() { return (shared_->statistics().LastError()); }
int32_t VoEBaseImpl::StartPlayout() {
if (shared_->audio_device()->Playing()) {
return 0;
}
if (!shared_->ext_playout()) {
if (shared_->audio_device()->InitPlayout() != 0) {
LOG_F(LS_ERROR) << "Failed to initialize playout";
return -1;
}
if (shared_->audio_device()->StartPlayout() != 0) {
LOG_F(LS_ERROR) << "Failed to start playout";
return -1;
}
}
return 0;
}
int32_t VoEBaseImpl::StopPlayout() {
// Stop audio-device playing if no channel is playing out
if (shared_->NumOfPlayingChannels() == 0) {
if (shared_->audio_device()->StopPlayout() != 0) {
shared_->SetLastError(VE_CANNOT_STOP_PLAYOUT, kTraceError,
"StopPlayout() failed to stop playout");
return -1;
}
}
return 0;
}
int32_t VoEBaseImpl::StartSend() {
if (shared_->audio_device()->Recording()) {
return 0;
}
if (!shared_->ext_recording()) {
if (shared_->audio_device()->InitRecording() != 0) {
LOG_F(LS_ERROR) << "Failed to initialize recording";
return -1;
}
if (shared_->audio_device()->StartRecording() != 0) {
LOG_F(LS_ERROR) << "Failed to start recording";
return -1;
}
}
return 0;
}
int32_t VoEBaseImpl::StopSend() {
if (shared_->NumOfSendingChannels() == 0 &&
!shared_->transmit_mixer()->IsRecordingMic()) {
// Stop audio-device recording if no channel is recording
if (shared_->audio_device()->StopRecording() != 0) {
shared_->SetLastError(VE_CANNOT_STOP_RECORDING, kTraceError,
"StopSend() failed to stop recording");
return -1;
}
shared_->transmit_mixer()->StopSend();
}
return 0;
}
int32_t VoEBaseImpl::TerminateInternal() {
// Delete any remaining channel objects
shared_->channel_manager().DestroyAllChannels();
if (shared_->process_thread()) {
if (shared_->audio_device()) {
shared_->process_thread()->DeRegisterModule(shared_->audio_device());
}
shared_->process_thread()->Stop();
}
if (shared_->audio_device()) {
if (shared_->audio_device()->StopPlayout() != 0) {
shared_->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"TerminateInternal() failed to stop playout");
}
if (shared_->audio_device()->StopRecording() != 0) {
shared_->SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"TerminateInternal() failed to stop recording");
}
if (shared_->audio_device()->RegisterEventObserver(nullptr) != 0) {
shared_->SetLastError(
VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning,
"TerminateInternal() failed to de-register event observer "
"for the ADM");
}
if (shared_->audio_device()->RegisterAudioCallback(nullptr) != 0) {
shared_->SetLastError(
VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning,
"TerminateInternal() failed to de-register audio callback "
"for the ADM");
}
if (shared_->audio_device()->Terminate() != 0) {
shared_->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
"TerminateInternal() failed to terminate the ADM");
}
shared_->set_audio_device(nullptr);
}
if (shared_->audio_processing()) {
shared_->set_audio_processing(nullptr);
}
return shared_->statistics().SetUnInitialized();
}
int VoEBaseImpl::ProcessRecordedDataWithAPM(
const int voe_channels[], int number_of_voe_channels,
const void* audio_data, uint32_t sample_rate, uint8_t number_of_channels,
uint32_t number_of_frames, uint32_t audio_delay_milliseconds,
int32_t clock_drift, uint32_t volume, bool key_pressed) {
assert(shared_->transmit_mixer() != nullptr);
assert(shared_->audio_device() != nullptr);
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<uint16_t>(
(volume * kMaxVolumeLevel + static_cast<int>(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<uint16_t>(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. When |number_of_voe_channels| == 0,
// do the operations on all the existing VoE channels; otherwise the
// operations will be done on specific channels.
if (number_of_voe_channels == 0) {
shared_->transmit_mixer()->DemuxAndMix();
shared_->transmit_mixer()->EncodeAndSend();
} else {
shared_->transmit_mixer()->DemuxAndMix(voe_channels,
number_of_voe_channels);
shared_->transmit_mixer()->EncodeAndSend(voe_channels,
number_of_voe_channels);
}
// 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<int>((new_voe_mic_level * max_volume +
static_cast<int>(kMaxVolumeLevel / 2)) /
kMaxVolumeLevel);
}
// Return 0 to indicate no change on the volume.
return 0;
}
void VoEBaseImpl::GetPlayoutData(int sample_rate, int number_of_channels,
int number_of_frames, bool feed_data_to_apm,
void* audio_data, int64_t* elapsed_time_ms,
int64_t* ntp_time_ms) {
assert(shared_->output_mixer() != nullptr);
// TODO(andrew): if the device is running in mono, we should tell the mixer
// here so that it will only request mono from AudioCodingModule.
// Perform mixing of all active participants (channel-based mixing)
shared_->output_mixer()->MixActiveChannels();
// Additional operations on the combined signal
shared_->output_mixer()->DoOperationsOnCombinedSignal(feed_data_to_apm);
// Retrieve the final output mix (resampled to match the ADM)
shared_->output_mixer()->GetMixedAudio(sample_rate, number_of_channels,
&audioFrame_);
assert(number_of_frames == audioFrame_.samples_per_channel_);
assert(sample_rate == audioFrame_.sample_rate_hz_);
// Deliver audio (PCM) samples to the ADM
memcpy(audio_data, audioFrame_.data_,
sizeof(int16_t) * number_of_frames * number_of_channels);
*elapsed_time_ms = audioFrame_.elapsed_time_ms_;
*ntp_time_ms = audioFrame_.ntp_time_ms_;
}
} // namespace webrtc