blob: cdc0ee3fe1b8e822c79be7d80b4b93f4944214a4 [file] [log] [blame]
/*
* 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 "voe_base_impl.h"
#include "audio_coding_module.h"
#include "audio_device_impl.h"
#include "audio_processing.h"
#include "channel.h"
#include "critical_section_wrapper.h"
#include "file_wrapper.h"
#include "output_mixer.h"
#include "signal_processing_library.h"
#include "trace.h"
#include "transmit_mixer.h"
#include "utility.h"
#include "voe_errors.h"
#include "voice_engine_impl.h"
#if (defined(_WIN32) && defined(_DLL) && (_MSC_VER == 1400))
// Fix for VS 2005 MD/MDd link problem
#include <stdio.h>
extern "C"
{ FILE _iob[3] = { __iob_func()[0], __iob_func()[1], __iob_func()[2]}; }
#endif
namespace webrtc
{
VoEBase* VoEBase::GetInterface(VoiceEngine* voiceEngine)
{
if (NULL == voiceEngine)
{
return NULL;
}
VoiceEngineImpl* s = reinterpret_cast<VoiceEngineImpl*> (voiceEngine);
VoEBaseImpl* d = s;
(*d)++;
return (d);
}
VoEBaseImpl::VoEBaseImpl() :
_voiceEngineObserverPtr(NULL),
_callbackCritSect(*CriticalSectionWrapper::CreateCriticalSection()),
_voiceEngineObserver(false), _oldVoEMicLevel(0), _oldMicLevel(0)
{
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl() - ctor");
}
VoEBaseImpl::~VoEBaseImpl()
{
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId, -1),
"~VoEBaseImpl() - dtor");
TerminateInternal();
delete &_callbackCritSect;
}
int VoEBaseImpl::Release()
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::Release()");
(*this)--;
int refCount = GetCount();
if (refCount < 0)
{
Reset();
_engineStatistics.SetLastError(VE_INTERFACE_NOT_FOUND, kTraceWarning);
return (-1);
}
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl reference counter = %d", refCount);
return (refCount);
}
void VoEBaseImpl::OnErrorIsReported(const ErrorCode error)
{
CriticalSectionScoped cs(_callbackCritSect);
if (_voiceEngineObserver)
{
if (_voiceEngineObserverPtr)
{
int errCode(0);
if (error == AudioDeviceObserver::kRecordingError)
{
errCode = VE_RUNTIME_REC_ERROR;
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::OnErrorIsReported() => "
"VE_RUNTIME_REC_ERROR");
}
else if (error == AudioDeviceObserver::kPlayoutError)
{
errCode = VE_RUNTIME_PLAY_ERROR;
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::OnErrorIsReported() => "
"VE_RUNTIME_PLAY_ERROR");
}
// Deliver callback (-1 <=> no channel dependency)
_voiceEngineObserverPtr->CallbackOnError(-1, errCode);
}
}
}
void VoEBaseImpl::OnWarningIsReported(const WarningCode warning)
{
CriticalSectionScoped cs(_callbackCritSect);
if (_voiceEngineObserver)
{
if (_voiceEngineObserverPtr)
{
int warningCode(0);
if (warning == AudioDeviceObserver::kRecordingWarning)
{
warningCode = VE_RUNTIME_REC_WARNING;
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::OnErrorIsReported() => "
"VE_RUNTIME_REC_WARNING");
}
else if (warning == AudioDeviceObserver::kPlayoutWarning)
{
warningCode = VE_RUNTIME_PLAY_WARNING;
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::OnErrorIsReported() => "
"VE_RUNTIME_PLAY_WARNING");
}
// Deliver callback (-1 <=> no channel dependency)
_voiceEngineObserverPtr->CallbackOnError(-1, warningCode);
}
}
}
WebRtc_Word32 VoEBaseImpl::RecordedDataIsAvailable(
const WebRtc_Word8* audioSamples,
const WebRtc_UWord32 nSamples,
const WebRtc_UWord8 nBytesPerSample,
const WebRtc_UWord8 nChannels,
const WebRtc_UWord32 samplesPerSec,
const WebRtc_UWord32 totalDelayMS,
const WebRtc_Word32 clockDrift,
const WebRtc_UWord32 currentMicLevel,
WebRtc_UWord32& newMicLevel)
{
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::RecordedDataIsAvailable(nSamples=%u, "
"nBytesPerSample=%u, nChannels=%u, samplesPerSec=%u, "
"totalDelayMS=%u, clockDrift=%d, currentMicLevel=%u)",
nSamples, nBytesPerSample, nChannels, samplesPerSec,
totalDelayMS, clockDrift, currentMicLevel);
assert(_transmitMixerPtr != NULL);
assert(_audioDevicePtr != NULL);
bool isAnalogAGC(false);
WebRtc_UWord32 maxVolume(0);
WebRtc_UWord16 currentVoEMicLevel(0);
WebRtc_UWord32 newVoEMicLevel(0);
if (_audioProcessingModulePtr
&& (_audioProcessingModulePtr->gain_control()->mode()
== GainControl::kAdaptiveAnalog))
{
isAnalogAGC = true;
}
// Will only deal with the volume in adaptive analog mode
if (isAnalogAGC)
{
// Scale from ADM to VoE level range
if (_audioDevicePtr->MaxMicrophoneVolume(&maxVolume) == 0)
{
if (0 != maxVolume)
{
currentVoEMicLevel = (WebRtc_UWord16) ((currentMicLevel
* kMaxVolumeLevel + (int) (maxVolume / 2))
/ (maxVolume));
}
}
// We learned that on certain systems (e.g Linux) the currentVoEMicLevel
// can be greater than the maxVolumeLevel therefore
// we are going to cap the currentVoEMicLevel to the maxVolumeLevel
// and change the maxVolume to currentMicLevel if it turns out that
// the currentVoEMicLevel is indeed greater than the maxVolumeLevel.
if (currentVoEMicLevel > kMaxVolumeLevel)
{
currentVoEMicLevel = kMaxVolumeLevel;
maxVolume = currentMicLevel;
}
}
// Keep track if the MicLevel has been changed by the AGC, if not,
// use the old value AGC returns to let AGC continue its trend,
// so eventually the AGC is able to change the mic level. This handles
// issues with truncation introduced by the scaling.
if (_oldMicLevel == currentMicLevel)
{
currentVoEMicLevel = (WebRtc_UWord16) _oldVoEMicLevel;
}
// Perform channel-independent operations
// (APM, mix with file, record to file, mute, etc.)
_transmitMixerPtr->PrepareDemux(audioSamples, nSamples, nChannels,
samplesPerSec,
(WebRtc_UWord16) totalDelayMS, clockDrift,
currentVoEMicLevel);
// Copy the audio frame to each sending channel and perform
// channel-dependent operations (file mixing, mute, etc.) to prepare
// for encoding.
_transmitMixerPtr->DemuxAndMix();
// Do the encoding and packetize+transmit the RTP packet when encoding
// is done.
_transmitMixerPtr->EncodeAndSend();
// Will only deal with the volume in adaptive analog mode
if (isAnalogAGC)
{
// Scale from VoE to ADM level range
newVoEMicLevel = _transmitMixerPtr->CaptureLevel();
if (newVoEMicLevel != currentVoEMicLevel)
{
// Add (kMaxVolumeLevel/2) to round the value
newMicLevel = (WebRtc_UWord32) ((newVoEMicLevel * maxVolume
+ (int) (kMaxVolumeLevel / 2)) / (kMaxVolumeLevel));
}
else
{
// Pass zero if the level is unchanged
newMicLevel = 0;
}
// Keep track of the value AGC returns
_oldVoEMicLevel = newVoEMicLevel;
_oldMicLevel = currentMicLevel;
}
return 0;
}
WebRtc_Word32 VoEBaseImpl::NeedMorePlayData(
const WebRtc_UWord32 nSamples,
const WebRtc_UWord8 nBytesPerSample,
const WebRtc_UWord8 nChannels,
const WebRtc_UWord32 samplesPerSec,
WebRtc_Word8* audioSamples,
WebRtc_UWord32& nSamplesOut)
{
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::NeedMorePlayData(nSamples=%u, "
"nBytesPerSample=%d, nChannels=%d, samplesPerSec=%u)",
nSamples, nBytesPerSample, nChannels, samplesPerSec);
assert(_outputMixerPtr != NULL);
// Perform mixing of all active participants (channel-based mixing)
_outputMixerPtr->MixActiveChannels();
// Additional operations on the combined signal
_outputMixerPtr->DoOperationsOnCombinedSignal();
// Retrieve the final output mix (resampled to match the ADM)
_outputMixerPtr->GetMixedAudio(samplesPerSec, nChannels, _audioFrame);
assert(nSamples == _audioFrame._payloadDataLengthInSamples);
assert(samplesPerSec ==
static_cast<WebRtc_UWord32>(_audioFrame._frequencyInHz));
// Deliver audio (PCM) samples to the ADM
memcpy(
(WebRtc_Word16*) audioSamples,
(const WebRtc_Word16*) _audioFrame._payloadData,
sizeof(WebRtc_Word16) * (_audioFrame._payloadDataLengthInSamples
* _audioFrame._audioChannel));
nSamplesOut = _audioFrame._payloadDataLengthInSamples;
return 0;
}
int VoEBaseImpl::RegisterVoiceEngineObserver(VoiceEngineObserver& observer)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"RegisterVoiceEngineObserver(observer=0x%d)", &observer);
CriticalSectionScoped cs(_callbackCritSect);
if (_voiceEngineObserverPtr)
{
_engineStatistics.SetLastError(VE_INVALID_OPERATION, kTraceError,
"RegisterVoiceEngineObserver() observer"
" already enabled");
return -1;
}
// Register the observer in all active channels
voe::ScopedChannel sc(_channelManager);
void* iterator(NULL);
voe::Channel* channelPtr = sc.GetFirstChannel(iterator);
while (channelPtr != NULL)
{
channelPtr->RegisterVoiceEngineObserver(observer);
channelPtr = sc.GetNextChannel(iterator);
}
_transmitMixerPtr->RegisterVoiceEngineObserver(observer);
_voiceEngineObserverPtr = &observer;
_voiceEngineObserver = true;
return 0;
}
int VoEBaseImpl::DeRegisterVoiceEngineObserver()
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"DeRegisterVoiceEngineObserver()");
CriticalSectionScoped cs(_callbackCritSect);
if (!_voiceEngineObserverPtr)
{
_engineStatistics.SetLastError(VE_INVALID_OPERATION, kTraceError,
"DeRegisterVoiceEngineObserver() observer already disabled");
return 0;
}
_voiceEngineObserver = false;
_voiceEngineObserverPtr = NULL;
// Deregister the observer in all active channels
voe::ScopedChannel sc(_channelManager);
void* iterator(NULL);
voe::Channel* channelPtr = sc.GetFirstChannel(iterator);
while (channelPtr != NULL)
{
channelPtr->DeRegisterVoiceEngineObserver();
channelPtr = sc.GetNextChannel(iterator);
}
return 0;
}
int VoEBaseImpl::Init(AudioDeviceModule* external_adm)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"Init(external_adm=0x%p)", external_adm);
CriticalSectionScoped cs(*_apiCritPtr);
if (_engineStatistics.Initialized())
{
return 0;
}
if (_moduleProcessThreadPtr)
{
if (_moduleProcessThreadPtr->Start() != 0)
{
_engineStatistics.SetLastError(VE_THREAD_ERROR, kTraceError,
"Init() failed to start module process thread");
return -1;
}
}
// Create an internal ADM if the user has not added an external
// ADM implementation as input to Init().
if (external_adm == NULL)
{
// Create the internal ADM implementation.
_audioDevicePtr = AudioDeviceModuleImpl::Create(
VoEId(_instanceId, -1), _audioDeviceLayer);
if (_audioDevicePtr == NULL)
{
_engineStatistics.SetLastError(VE_NO_MEMORY, kTraceCritical,
"Init() failed to create the ADM");
return -1;
}
}
else
{
// Use the already existing external ADM implementation.
_audioDevicePtr = external_adm;
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"An external ADM implementation will be used in VoiceEngine");
}
// Increase the reference counter for both external and internal usage.
_audioDevicePtr->AddRef();
// Register the ADM to the process thread, which will drive the error
// callback mechanism
if (_moduleProcessThreadPtr &&
_moduleProcessThreadPtr->RegisterModule(_audioDevicePtr) != 0)
{
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceError,
"Init() failed to register the ADM");
return -1;
}
bool available(false);
// --------------------
// Reinitialize the ADM
// Register the AudioObserver implementation
if (_audioDevicePtr->RegisterEventObserver(this) != 0) {
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceWarning,
"Init() failed to register event observer "
"for the ADM");
}
// Register the AudioTransport implementation
if (_audioDevicePtr->RegisterAudioCallback(this) != 0) {
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceWarning,
"Init() failed to register audio callback "
"for the ADM");
}
// ADM initialization
if (_audioDevicePtr->Init() != 0)
{
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceError,
"Init() failed to initialize the ADM");
return -1;
}
// Initialize the default speaker
if (_audioDevicePtr->SetPlayoutDevice(WEBRTC_VOICE_ENGINE_DEFAULT_DEVICE)
!= 0)
{
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceInfo,
"Init() failed to set the default output device");
}
if (_audioDevicePtr->SpeakerIsAvailable(&available) != 0)
{
_engineStatistics.SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceInfo,
"Init() failed to check speaker availability, trying to "
"initialize speaker anyway");
}
else if (!available)
{
_engineStatistics.SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceInfo,
"Init() speaker not available, trying to initialize speaker "
"anyway");
}
if (_audioDevicePtr->InitSpeaker() != 0)
{
_engineStatistics.SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceInfo,
"Init() failed to initialize the speaker");
}
// Initialize the default microphone
if (_audioDevicePtr->SetRecordingDevice(WEBRTC_VOICE_ENGINE_DEFAULT_DEVICE)
!= 0)
{
_engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceInfo,
"Init() failed to set the default input device");
}
if (_audioDevicePtr->MicrophoneIsAvailable(&available) != 0)
{
_engineStatistics.SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceInfo,
"Init() failed to check microphone availability, trying to "
"initialize microphone anyway");
}
else if (!available)
{
_engineStatistics.SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceInfo,
"Init() microphone not available, trying to initialize "
"microphone anyway");
}
if (_audioDevicePtr->InitMicrophone() != 0)
{
_engineStatistics.SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceInfo,
"Init() failed to initialize the microphone");
}
// Set number of channels
if (_audioDevicePtr->StereoPlayoutIsAvailable(&available) != 0) {
_engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"Init() failed to query stereo playout "
"mode");
}
if (_audioDevicePtr->SetStereoPlayout(available) != 0)
{
_engineStatistics.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
_audioDevicePtr->StereoRecordingIsAvailable(&available);
if (_audioDevicePtr->SetStereoRecording(available) != 0)
{
_engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"Init() failed to set mono/stereo recording mode");
}
// APM initialization done after sound card since we need
// to know if we support stereo recording or not.
// Create the AudioProcessing Module if it does not exist.
if (_audioProcessingModulePtr == NULL)
{
_audioProcessingModulePtr = AudioProcessing::Create(
VoEId(_instanceId, -1));
if (_audioProcessingModulePtr == NULL)
{
_engineStatistics.SetLastError(VE_NO_MEMORY, kTraceCritical,
"Init() failed to create the AP module");
return -1;
}
// Ensure that mixers in both directions has access to the created APM
_transmitMixerPtr->SetAudioProcessingModule(_audioProcessingModulePtr);
_outputMixerPtr->SetAudioProcessingModule(_audioProcessingModulePtr);
if (_audioProcessingModulePtr->echo_cancellation()->
set_device_sample_rate_hz(
kVoiceEngineAudioProcessingDeviceSampleRateHz))
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set the device sample rate to 48K for AP "
" module");
return -1;
}
// Using 8 kHz as inital Fs. Might be changed already at first call.
if (_audioProcessingModulePtr->set_sample_rate_hz(8000))
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set the sample rate to 8K for AP module");
return -1;
}
// Assume mono output until a send codec is set, and stereo input until
// we receive the first captured frame. We set stereo input here to
// avoid triggering a possible error in SetSendCodec when a stereo
// codec is selected.
if (_audioProcessingModulePtr->set_num_channels(2, 1) != 0)
{
_engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceError,
"Init() failed to set channels for the primary audio stream");
return -1;
}
if (_audioProcessingModulePtr->set_num_reverse_channels(1) != 0)
{
_engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceError,
"Init() failed to set channels for the primary audio stream");
return -1;
}
// high-pass filter
if (_audioProcessingModulePtr->high_pass_filter()->Enable(
WEBRTC_VOICE_ENGINE_HP_DEFAULT_STATE) != 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set the high-pass filter for AP module");
return -1;
}
// Echo Cancellation
if (_audioProcessingModulePtr->echo_cancellation()->
enable_drift_compensation(false) != 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set drift compensation for AP module");
return -1;
}
if (_audioProcessingModulePtr->echo_cancellation()->Enable(
WEBRTC_VOICE_ENGINE_EC_DEFAULT_STATE))
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set echo cancellation state for AP module");
return -1;
}
// Noise Reduction
if (_audioProcessingModulePtr->noise_suppression()->set_level(
(NoiseSuppression::Level) WEBRTC_VOICE_ENGINE_NS_DEFAULT_MODE)
!= 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set noise reduction level for AP module");
return -1;
}
if (_audioProcessingModulePtr->noise_suppression()->Enable(
WEBRTC_VOICE_ENGINE_NS_DEFAULT_STATE) != 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set noise reduction state for AP module");
return -1;
}
// Automatic Gain control
if (_audioProcessingModulePtr->gain_control()->set_analog_level_limits(
kMinVolumeLevel,kMaxVolumeLevel) != 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set AGC analog level for AP module");
return -1;
}
if (_audioProcessingModulePtr->gain_control()->set_mode(
(GainControl::Mode) WEBRTC_VOICE_ENGINE_AGC_DEFAULT_MODE)
!= 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set AGC mode for AP module");
return -1;
}
if (_audioProcessingModulePtr->gain_control()->Enable(
WEBRTC_VOICE_ENGINE_AGC_DEFAULT_STATE)
!= 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set AGC state for AP module");
return -1;
}
// VAD
if (_audioProcessingModulePtr->voice_detection()->Enable(
WEBRTC_VOICE_ENGINE_VAD_DEFAULT_STATE)
!= 0)
{
_engineStatistics.SetLastError(VE_APM_ERROR, kTraceError,
"Init() failed to set VAD state for AP module");
return -1;
}
}
// Set default AGC mode for the ADM
#ifdef WEBRTC_VOICE_ENGINE_AGC
bool enable(false);
if (_audioProcessingModulePtr->gain_control()->mode()
!= GainControl::kFixedDigital)
{
enable = _audioProcessingModulePtr->gain_control()->is_enabled();
// Only set the AGC mode for the ADM when Adaptive AGC mode is selected
if (_audioDevicePtr->SetAGC(enable) != 0)
{
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceError, "Init() failed to set default AGC mode in ADM 0");
}
}
#endif
return _engineStatistics.SetInitialized();
}
int VoEBaseImpl::Terminate()
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"Terminate()");
CriticalSectionScoped cs(*_apiCritPtr);
return TerminateInternal();
}
int VoEBaseImpl::MaxNumOfChannels()
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"MaxNumOfChannels()");
WebRtc_Word32 maxNumOfChannels = _channelManager.MaxNumOfChannels();
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
"MaxNumOfChannels() => %d", maxNumOfChannels);
return (maxNumOfChannels);
}
int VoEBaseImpl::CreateChannel()
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"CreateChannel()");
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
WebRtc_Word32 channelId = -1;
if (!_channelManager.CreateChannel(channelId))
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_CREATED, kTraceError,
"CreateChannel() failed to allocate "
"memory for channel");
return -1;
}
bool destroyChannel(false);
{
voe::ScopedChannel sc(_channelManager, channelId);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_CREATED, kTraceError,
"CreateChannel() failed to allocate"
" memory for channel");
return -1;
}
else if (channelPtr->SetEngineInformation(_engineStatistics,
*_outputMixerPtr,
*_transmitMixerPtr,
*_moduleProcessThreadPtr,
*_audioDevicePtr,
_voiceEngineObserverPtr,
&_callbackCritSect) != 0)
{
destroyChannel = true;
_engineStatistics.SetLastError(VE_CHANNEL_NOT_CREATED, kTraceError,
"CreateChannel() failed to "
"associate engine and channel."
" Destroying channel.");
}
else if (channelPtr->Init() != 0)
{
destroyChannel = true;
_engineStatistics.SetLastError(VE_CHANNEL_NOT_CREATED, kTraceError,
"CreateChannel() failed to "
"initialize channel. Destroying"
" channel.");
}
}
if (destroyChannel)
{
_channelManager.DestroyChannel(channelId);
return -1;
}
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
"CreateChannel() => %d", channelId);
return channelId;
}
int VoEBaseImpl::DeleteChannel(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"DeleteChannel(channel=%d)", channel);
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
{
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"DeleteChannel() failed to locate "
"channel");
return -1;
}
}
if (_channelManager.DestroyChannel(channel) != 0)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"DeleteChannel() failed to destroy "
"channel");
return -1;
}
if (StopSend() != 0)
{
return -1;
}
if (StopPlayout() != 0)
{
return -1;
}
return 0;
}
int VoEBaseImpl::SetLocalReceiver(int channel, int port, int RTCPport,
const char ipAddr[64],
const char multiCastAddr[64])
{
// Inititialize local receive sockets (RTP and RTCP).
//
// The sockets are always first closed and then created again by this
// function call. The created sockets are by default also used for
// transmission (unless source port is set in SetSendDestination).
//
// Note that, sockets can also be created automatically if a user calls
// SetSendDestination and StartSend without having called SetLocalReceiver
// first. The sockets are then created at the first packet transmission.
CriticalSectionScoped cs(*_apiCritPtr);
if (ipAddr == NULL && multiCastAddr == NULL)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"SetLocalReceiver(channel=%d, port=%d, RTCPport=%d)",
channel, port, RTCPport);
}
else if (ipAddr != NULL && multiCastAddr == NULL)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"SetLocalReceiver(channel=%d, port=%d, RTCPport=%d, "
"ipAddr=%s)", channel, port, RTCPport, ipAddr);
}
else if (ipAddr == NULL && multiCastAddr != NULL)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"SetLocalReceiver(channel=%d, port=%d, RTCPport=%d, "
"multiCastAddr=%s)", channel, port, RTCPport,
multiCastAddr);
}
else
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"SetLocalReceiver(channel=%d, port=%d, RTCPport=%d, "
"ipAddr=%s, multiCastAddr=%s)", channel, port,
RTCPport, ipAddr, multiCastAddr);
}
#ifndef WEBRTC_EXTERNAL_TRANSPORT
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
if ((port < 0) || (port > 65535))
{
_engineStatistics.SetLastError(VE_INVALID_PORT_NMBR, kTraceError,
"SetLocalReceiver() invalid RTP port");
return -1;
}
if (((RTCPport != kVoEDefault) && (RTCPport < 0)) || ((RTCPport
!= kVoEDefault) && (RTCPport > 65535)))
{
_engineStatistics.SetLastError(VE_INVALID_PORT_NMBR, kTraceError,
"SetLocalReceiver() invalid RTCP port");
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetLocalReceiver() failed to locate "
"channel");
return -1;
}
// Cast RTCP port. In the RTP module 0 corresponds to RTP port + 1 in
// the module, which is the default.
WebRtc_UWord16 rtcpPortUW16(0);
if (RTCPport != kVoEDefault)
{
rtcpPortUW16 = static_cast<WebRtc_UWord16> (RTCPport);
}
return channelPtr->SetLocalReceiver(port, rtcpPortUW16, ipAddr,
multiCastAddr);
#else
_engineStatistics.SetLastError(VE_EXTERNAL_TRANSPORT_ENABLED,
kTraceWarning,
"SetLocalReceiver() VoE is built for "
"external transport");
return -1;
#endif
}
int VoEBaseImpl::GetLocalReceiver(int channel, int& port, int& RTCPport,
char ipAddr[64])
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"GetLocalReceiver(channel=%d, ipAddr[]=?)", channel);
#ifndef WEBRTC_EXTERNAL_TRANSPORT
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetLocalReceiver() failed to locate "
"channel");
return -1;
}
WebRtc_Word32 ret = channelPtr->GetLocalReceiver(port, RTCPport, ipAddr);
if (ipAddr != NULL)
{
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
"GetLocalReceiver() => port=%d, RTCPport=%d, ipAddr=%s",
port, RTCPport, ipAddr);
}
else
{
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
"GetLocalReceiver() => port=%d, RTCPport=%d", port,
RTCPport);
}
return ret;
#else
_engineStatistics.SetLastError(VE_EXTERNAL_TRANSPORT_ENABLED,
kTraceWarning,
"SetLocalReceiver() VoE is built for "
"external transport");
return -1;
#endif
}
int VoEBaseImpl::SetSendDestination(int channel, int port, const char* ipaddr,
int sourcePort, int RTCPport)
{
WEBRTC_TRACE(
kTraceApiCall,
kTraceVoice,
VoEId(_instanceId, -1),
"SetSendDestination(channel=%d, port=%d, ipaddr=%s,"
"sourcePort=%d, RTCPport=%d)",
channel, port, ipaddr, sourcePort, RTCPport);
CriticalSectionScoped cs(*_apiCritPtr);
#ifndef WEBRTC_EXTERNAL_TRANSPORT
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetSendDestination() failed to locate "
"channel");
return -1;
}
if ((port < 0) || (port > 65535))
{
_engineStatistics.SetLastError(VE_INVALID_PORT_NMBR, kTraceError,
"SetSendDestination() invalid RTP port");
return -1;
}
if (((RTCPport != kVoEDefault) && (RTCPport < 0)) || ((RTCPport
!= kVoEDefault) && (RTCPport > 65535)))
{
_engineStatistics.SetLastError(VE_INVALID_PORT_NMBR, kTraceError,
"SetSendDestination() invalid RTCP "
"port");
return -1;
}
if (((sourcePort != kVoEDefault) && (sourcePort < 0)) || ((sourcePort
!= kVoEDefault) && (sourcePort > 65535)))
{
_engineStatistics.SetLastError(VE_INVALID_PORT_NMBR, kTraceError,
"SetSendDestination() invalid source "
"port");
return -1;
}
// Cast RTCP port. In the RTP module 0 corresponds to RTP port + 1 in the
// module, which is the default.
WebRtc_UWord16 rtcpPortUW16(0);
if (RTCPport != kVoEDefault)
{
rtcpPortUW16 = static_cast<WebRtc_UWord16> (RTCPport);
WEBRTC_TRACE(
kTraceInfo,
kTraceVoice,
VoEId(_instanceId, channel),
"SetSendDestination() non default RTCP port %u will be "
"utilized",
rtcpPortUW16);
}
return channelPtr->SetSendDestination(port, ipaddr, sourcePort,
rtcpPortUW16);
#else
_engineStatistics.SetLastError(VE_EXTERNAL_TRANSPORT_ENABLED,
kTraceWarning,
"SetSendDestination() VoE is built for "
"external transport");
return -1;
#endif
}
int VoEBaseImpl::GetSendDestination(int channel, int& port, char ipAddr[64],
int& sourcePort, int& RTCPport)
{
WEBRTC_TRACE(
kTraceApiCall,
kTraceVoice,
VoEId(_instanceId, -1),
"GetSendDestination(channel=%d, ipAddr[]=?, sourcePort=?,"
"RTCPport=?)",
channel);
#ifndef WEBRTC_EXTERNAL_TRANSPORT
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"GetSendDestination() failed to locate "
"channel");
return -1;
}
WebRtc_Word32 ret = channelPtr->GetSendDestination(port, ipAddr,
sourcePort, RTCPport);
if (ipAddr != NULL)
{
WEBRTC_TRACE(
kTraceStateInfo,
kTraceVoice,
VoEId(_instanceId, -1),
"GetSendDestination() => port=%d, RTCPport=%d, ipAddr=%s, "
"sourcePort=%d, RTCPport=%d",
port, RTCPport, ipAddr, sourcePort, RTCPport);
}
else
{
WEBRTC_TRACE(
kTraceStateInfo,
kTraceVoice,
VoEId(_instanceId, -1),
"GetSendDestination() => port=%d, RTCPport=%d, "
"sourcePort=%d, RTCPport=%d",
port, RTCPport, sourcePort, RTCPport);
}
return ret;
#else
_engineStatistics.SetLastError(VE_EXTERNAL_TRANSPORT_ENABLED,
kTraceWarning,
"GetSendDestination() VoE is built for "
"external transport");
return -1;
#endif
}
int VoEBaseImpl::StartReceive(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"StartReceive(channel=%d)", channel);
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StartReceive() failed to locate "
"channel");
return -1;
}
return channelPtr->StartReceiving();
}
int VoEBaseImpl::StopReceive(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"StopListen(channel=%d)", channel);
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetLocalReceiver() failed to locate "
"channel");
return -1;
}
return channelPtr->StopReceiving();
}
int VoEBaseImpl::StartPlayout(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"StartPlayout(channel=%d)", channel);
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StartPlayout() failed to locate "
"channel");
return -1;
}
if (channelPtr->Playing())
{
return 0;
}
if (StartPlayout() != 0)
{
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceError,
"StartPlayout() failed to start "
"playout");
return -1;
}
return channelPtr->StartPlayout();
}
int VoEBaseImpl::StopPlayout(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"StopPlayout(channel=%d)", channel);
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StopPlayout() failed to locate "
"channel");
return -1;
}
if (channelPtr->StopPlayout() != 0)
{
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
"StopPlayout() failed to stop playout for channel %d",
channel);
}
return StopPlayout();
}
int VoEBaseImpl::StartSend(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"StartSend(channel=%d)", channel);
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StartSend() failed to locate channel");
return -1;
}
if (channelPtr->Sending())
{
return 0;
}
#ifndef WEBRTC_EXTERNAL_TRANSPORT
if (!channelPtr->ExternalTransport()
&& !channelPtr->SendSocketsInitialized())
{
_engineStatistics.SetLastError(VE_DESTINATION_NOT_INITED, kTraceError,
"StartSend() must set send destination "
"first");
return -1;
}
#endif
if (StartSend() != 0)
{
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceError,
"StartSend() failed to start recording");
return -1;
}
return channelPtr->StartSend();
}
int VoEBaseImpl::StopSend(int channel)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"StopSend(channel=%d)", channel);
CriticalSectionScoped cs(*_apiCritPtr);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"StopSend() failed to locate channel");
return -1;
}
if (channelPtr->StopSend() != 0)
{
WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, -1),
"StopSend() failed to stop sending for channel %d",
channel);
}
return StopSend();
}
int VoEBaseImpl::GetVersion(char version[1024])
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"GetVersion(version=?)");
assert(kVoiceEngineVersionMaxMessageSize == 1024);
if (version == NULL)
{
_engineStatistics.SetLastError(VE_INVALID_ARGUMENT, kTraceError);
return (-1);
}
char versionBuf[kVoiceEngineVersionMaxMessageSize];
char* versionPtr = versionBuf;
WebRtc_Word32 len = 0;
WebRtc_Word32 accLen = 0;
len = AddVoEVersion(versionPtr);
if (len == -1)
{
return -1;
}
versionPtr += len;
accLen += len;
assert(accLen < kVoiceEngineVersionMaxMessageSize);
len = AddBuildInfo(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
#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT
len = AddExternalRecAndPlayoutBuild(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];
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
"GetVersion() =>");
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;
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId, -1),
"%s", partOfVersion);
}
return 0;
}
WebRtc_Word32 VoEBaseImpl::AddBuildInfo(char* str) const
{
return sprintf(str, "Build: %s\n", BUILDINFO);
}
WebRtc_Word32 VoEBaseImpl::AddVoEVersion(char* str) const
{
return sprintf(str, "VoiceEngine 4.1.0\n");
}
#ifdef WEBRTC_EXTERNAL_TRANSPORT
WebRtc_Word32 VoEBaseImpl::AddExternalTransportBuild(char* str) const
{
return sprintf(str, "External transport build\n");
}
#endif
#ifdef WEBRTC_VOE_EXTERNAL_REC_AND_PLAYOUT
WebRtc_Word32 VoEBaseImpl::AddExternalRecAndPlayoutBuild(char* str) const
{
return sprintf(str, "External recording and playout build\n");
}
#endif
int VoEBaseImpl::LastError()
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"LastError()");
return (_engineStatistics.LastError());
}
int VoEBaseImpl::SetNetEQPlayoutMode(int channel, NetEqModes mode)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"SetNetEQPlayoutMode(channel=%i, mode=%i)", channel, mode);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetNetEQPlayoutMode() failed to locate"
" channel");
return -1;
}
return channelPtr->SetNetEQPlayoutMode(mode);
}
int VoEBaseImpl::GetNetEQPlayoutMode(int channel, NetEqModes& mode)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"GetNetEQPlayoutMode(channel=%i, mode=?)", channel);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"GetNetEQPlayoutMode() failed to locate"
" channel");
return -1;
}
return channelPtr->GetNetEQPlayoutMode(mode);
}
int VoEBaseImpl::SetNetEQBGNMode(int channel, NetEqBgnModes mode)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"SetNetEQBGNMode(channel=%i, mode=%i)", channel, mode);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetNetEQBGNMode() failed to locate "
"channel");
return -1;
}
return channelPtr->SetNetEQBGNMode(mode);
}
int VoEBaseImpl::GetNetEQBGNMode(int channel, NetEqBgnModes& mode)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"GetNetEQBGNMode(channel=%i, mode=?)", channel);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"GetNetEQBGNMode() failed to locate "
"channel");
return -1;
}
return channelPtr->GetNetEQBGNMode(mode);
}
int VoEBaseImpl::SetOnHoldStatus(int channel, bool enable, OnHoldModes mode)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"SetOnHoldStatus(channel=%d, enable=%d, mode=%d)", channel,
enable, mode);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"SetOnHoldStatus() failed to locate "
"channel");
return -1;
}
return channelPtr->SetOnHoldStatus(enable, mode);
}
int VoEBaseImpl::GetOnHoldStatus(int channel, bool& enabled, OnHoldModes& mode)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"GetOnHoldStatus(channel=%d, enabled=?, mode=?)", channel);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ScopedChannel sc(_channelManager, channel);
voe::Channel* channelPtr = sc.ChannelPtr();
if (channelPtr == NULL)
{
_engineStatistics.SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"GetOnHoldStatus() failed to locate "
"channel");
return -1;
}
return channelPtr->GetOnHoldStatus(enabled, mode);
}
WebRtc_Word32 VoEBaseImpl::StartPlayout()
{
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::StartPlayout()");
if (_audioDevicePtr->Playing())
{
return 0;
}
if (!_externalPlayout)
{
if (_audioDevicePtr->InitPlayout() != 0)
{
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
"StartPlayout() failed to initialize playout");
return -1;
}
if (_audioDevicePtr->StartPlayout() != 0)
{
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
"StartPlayout() failed to start playout");
return -1;
}
}
return 0;
}
WebRtc_Word32 VoEBaseImpl::StopPlayout()
{
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::StopPlayout()");
WebRtc_Word32 numOfChannels = _channelManager.NumOfChannels();
if (numOfChannels <= 0)
{
return 0;
}
WebRtc_UWord16 nChannelsPlaying(0);
WebRtc_Word32* channelsArray = new WebRtc_Word32[numOfChannels];
// Get number of playing channels
_channelManager.GetChannelIds(channelsArray, numOfChannels);
for (int i = 0; i < numOfChannels; i++)
{
voe::ScopedChannel sc(_channelManager, channelsArray[i]);
voe::Channel* chPtr = sc.ChannelPtr();
if (chPtr)
{
if (chPtr->Playing())
{
nChannelsPlaying++;
}
}
}
delete[] channelsArray;
// Stop audio-device playing if no channel is playing out
if (nChannelsPlaying == 0)
{
if (_audioDevicePtr->StopPlayout() != 0)
{
_engineStatistics.SetLastError(VE_CANNOT_STOP_PLAYOUT, kTraceError,
"StopPlayout() failed to stop "
"playout");
return -1;
}
}
return 0;
}
WebRtc_Word32 VoEBaseImpl::StartSend()
{
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::StartSend()");
if (_audioDevicePtr->Recording())
{
return 0;
}
if (!_externalRecording)
{
if (_audioDevicePtr->InitRecording() != 0)
{
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
"StartSend() failed to initialize recording");
return -1;
}
if (_audioDevicePtr->StartRecording() != 0)
{
WEBRTC_TRACE(kTraceError, kTraceVoice, VoEId(_instanceId, -1),
"StartSend() failed to start recording");
return -1;
}
}
return 0;
}
WebRtc_Word32 VoEBaseImpl::StopSend()
{
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::StopSend()");
if ((NumOfSendingChannels() == 0) && !_transmitMixerPtr->IsRecordingMic())
{
// Stop audio-device recording if no channel is recording
if (_audioDevicePtr->StopRecording() != 0)
{
_engineStatistics.SetLastError(VE_CANNOT_STOP_RECORDING,
kTraceError,
"StopSend() failed to stop "
"recording");
return -1;
}
_transmitMixerPtr->StopSend();
}
return 0;
}
WebRtc_Word32 VoEBaseImpl::TerminateInternal()
{
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, -1),
"VoEBaseImpl::TerminateInternal()");
// Delete any remaining channel objects
WebRtc_Word32 numOfChannels = _channelManager.NumOfChannels();
if (numOfChannels > 0)
{
WebRtc_Word32* channelsArray = new WebRtc_Word32[numOfChannels];
_channelManager.GetChannelIds(channelsArray, numOfChannels);
for (int i = 0; i < numOfChannels; i++)
{
DeleteChannel(channelsArray[i]);
}
delete[] channelsArray;
}
if (_moduleProcessThreadPtr)
{
if (_audioDevicePtr)
{
if (_moduleProcessThreadPtr->DeRegisterModule(_audioDevicePtr) != 0)
{
_engineStatistics.SetLastError(VE_THREAD_ERROR, kTraceError,
"TerminateInternal() failed to "
"deregister ADM");
}
}
if (_moduleProcessThreadPtr->Stop() != 0)
{
_engineStatistics.SetLastError(VE_THREAD_ERROR, kTraceError,
"TerminateInternal() failed to stop "
"module process thread");
}
}
// Audio Device Module
if (_audioDevicePtr != NULL)
{
if (_audioDevicePtr->StopPlayout() != 0)
{
_engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"TerminateInternal() failed to stop "
"playout");
}
if (_audioDevicePtr->StopRecording() != 0)
{
_engineStatistics.SetLastError(VE_SOUNDCARD_ERROR, kTraceWarning,
"TerminateInternal() failed to stop "
"recording");
}
if (_audioDevicePtr->RegisterEventObserver(NULL) != 0) {
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceWarning,
"TerminateInternal() failed to de-"
"register event observer for the ADM");
}
if (_audioDevicePtr->RegisterAudioCallback(NULL) != 0) {
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceWarning,
"TerminateInternal() failed to de-"
"register audio callback for the ADM");
}
if (_audioDevicePtr->Terminate() != 0)
{
_engineStatistics.SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR,
kTraceError,
"TerminateInternal() failed to "
"terminate the ADM");
}
_audioDevicePtr->Release();
_audioDevicePtr = NULL;
}
// AP module
if (_audioProcessingModulePtr != NULL)
{
_transmitMixerPtr->SetAudioProcessingModule(NULL);
AudioProcessing::Destroy(_audioProcessingModulePtr);
_audioProcessingModulePtr = NULL;
}
return _engineStatistics.SetUnInitialized();
}
} // namespace webrtc