517 lines
16 KiB
C++
517 lines
16 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_hardware_impl.h"
|
|
|
|
#include <assert.h>
|
|
|
|
#include "webrtc/system_wrappers/include/trace.h"
|
|
#include "webrtc/voice_engine/include/voe_errors.h"
|
|
#include "webrtc/voice_engine/voice_engine_impl.h"
|
|
|
|
namespace webrtc {
|
|
|
|
VoEHardware* VoEHardware::GetInterface(VoiceEngine* voiceEngine) {
|
|
#ifndef WEBRTC_VOICE_ENGINE_HARDWARE_API
|
|
return NULL;
|
|
#else
|
|
if (NULL == voiceEngine) {
|
|
return NULL;
|
|
}
|
|
VoiceEngineImpl* s = static_cast<VoiceEngineImpl*>(voiceEngine);
|
|
s->AddRef();
|
|
return s;
|
|
#endif
|
|
}
|
|
|
|
#ifdef WEBRTC_VOICE_ENGINE_HARDWARE_API
|
|
|
|
VoEHardwareImpl::VoEHardwareImpl(voe::SharedData* shared) : _shared(shared) {
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"VoEHardwareImpl() - ctor");
|
|
}
|
|
|
|
VoEHardwareImpl::~VoEHardwareImpl() {
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"~VoEHardwareImpl() - dtor");
|
|
}
|
|
|
|
int VoEHardwareImpl::SetAudioDeviceLayer(AudioLayers audioLayer) {
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"SetAudioDeviceLayer(audioLayer=%d)", audioLayer);
|
|
|
|
// Don't allow a change if VoE is initialized
|
|
if (_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_ALREADY_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
|
|
// Map to AudioDeviceModule::AudioLayer
|
|
AudioDeviceModule::AudioLayer wantedLayer(
|
|
AudioDeviceModule::kPlatformDefaultAudio);
|
|
switch (audioLayer) {
|
|
case kAudioPlatformDefault:
|
|
// already set above
|
|
break;
|
|
case kAudioWindowsCore:
|
|
wantedLayer = AudioDeviceModule::kWindowsCoreAudio;
|
|
break;
|
|
case kAudioWindowsWave:
|
|
wantedLayer = AudioDeviceModule::kWindowsWaveAudio;
|
|
break;
|
|
case kAudioLinuxAlsa:
|
|
wantedLayer = AudioDeviceModule::kLinuxAlsaAudio;
|
|
break;
|
|
case kAudioLinuxPulse:
|
|
wantedLayer = AudioDeviceModule::kLinuxPulseAudio;
|
|
break;
|
|
}
|
|
|
|
// Save the audio device layer for Init()
|
|
_shared->set_audio_device_layer(wantedLayer);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEHardwareImpl::GetAudioDeviceLayer(AudioLayers& audioLayer) {
|
|
// Can always be called regardless of VoE state
|
|
|
|
AudioDeviceModule::AudioLayer activeLayer(
|
|
AudioDeviceModule::kPlatformDefaultAudio);
|
|
|
|
if (_shared->audio_device()) {
|
|
// Get active audio layer from ADM
|
|
if (_shared->audio_device()->ActiveAudioLayer(&activeLayer) != 0) {
|
|
_shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError,
|
|
" Audio Device error");
|
|
return -1;
|
|
}
|
|
} else {
|
|
// Return VoE's internal layer setting
|
|
activeLayer = _shared->audio_device_layer();
|
|
}
|
|
|
|
// Map to AudioLayers
|
|
switch (activeLayer) {
|
|
case AudioDeviceModule::kPlatformDefaultAudio:
|
|
audioLayer = kAudioPlatformDefault;
|
|
break;
|
|
case AudioDeviceModule::kWindowsCoreAudio:
|
|
audioLayer = kAudioWindowsCore;
|
|
break;
|
|
case AudioDeviceModule::kWindowsWaveAudio:
|
|
audioLayer = kAudioWindowsWave;
|
|
break;
|
|
case AudioDeviceModule::kLinuxAlsaAudio:
|
|
audioLayer = kAudioLinuxAlsa;
|
|
break;
|
|
case AudioDeviceModule::kLinuxPulseAudio:
|
|
audioLayer = kAudioLinuxPulse;
|
|
break;
|
|
default:
|
|
_shared->SetLastError(VE_UNDEFINED_SC_ERR, kTraceError,
|
|
" unknown audio layer");
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
int VoEHardwareImpl::GetNumOfRecordingDevices(int& devices) {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
|
|
devices = static_cast<int>(_shared->audio_device()->RecordingDevices());
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEHardwareImpl::GetNumOfPlayoutDevices(int& devices) {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
|
|
devices = static_cast<int>(_shared->audio_device()->PlayoutDevices());
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEHardwareImpl::GetRecordingDeviceName(int index,
|
|
char strNameUTF8[128],
|
|
char strGuidUTF8[128]) {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
if (strNameUTF8 == NULL) {
|
|
_shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
|
|
"GetRecordingDeviceName() invalid argument");
|
|
return -1;
|
|
}
|
|
|
|
// Note that strGuidUTF8 is allowed to be NULL
|
|
|
|
// Init len variable to length of supplied vectors
|
|
const uint16_t strLen = 128;
|
|
|
|
// Check if length has been changed in module
|
|
static_assert(strLen == kAdmMaxDeviceNameSize, "");
|
|
static_assert(strLen == kAdmMaxGuidSize, "");
|
|
|
|
char name[strLen];
|
|
char guid[strLen];
|
|
|
|
// Get names from module
|
|
if (_shared->audio_device()->RecordingDeviceName(index, name, guid) != 0) {
|
|
_shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError,
|
|
"GetRecordingDeviceName() failed to get device name");
|
|
return -1;
|
|
}
|
|
|
|
// Copy to vectors supplied by user
|
|
strncpy(strNameUTF8, name, strLen);
|
|
|
|
if (strGuidUTF8 != NULL) {
|
|
strncpy(strGuidUTF8, guid, strLen);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEHardwareImpl::GetPlayoutDeviceName(int index,
|
|
char strNameUTF8[128],
|
|
char strGuidUTF8[128]) {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
if (strNameUTF8 == NULL) {
|
|
_shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
|
|
"GetPlayoutDeviceName() invalid argument");
|
|
return -1;
|
|
}
|
|
|
|
// Note that strGuidUTF8 is allowed to be NULL
|
|
|
|
// Init len variable to length of supplied vectors
|
|
const uint16_t strLen = 128;
|
|
|
|
// Check if length has been changed in module
|
|
static_assert(strLen == kAdmMaxDeviceNameSize, "");
|
|
static_assert(strLen == kAdmMaxGuidSize, "");
|
|
|
|
char name[strLen];
|
|
char guid[strLen];
|
|
|
|
// Get names from module
|
|
if (_shared->audio_device()->PlayoutDeviceName(index, name, guid) != 0) {
|
|
_shared->SetLastError(VE_CANNOT_RETRIEVE_DEVICE_NAME, kTraceError,
|
|
"GetPlayoutDeviceName() failed to get device name");
|
|
return -1;
|
|
}
|
|
|
|
// Copy to vectors supplied by user
|
|
strncpy(strNameUTF8, name, strLen);
|
|
|
|
if (strGuidUTF8 != NULL) {
|
|
strncpy(strGuidUTF8, guid, strLen);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEHardwareImpl::SetRecordingDevice(int index,
|
|
StereoChannel recordingChannel) {
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"SetRecordingDevice(index=%d, recordingChannel=%d)", index,
|
|
(int)recordingChannel);
|
|
rtc::CritScope cs(_shared->crit_sec());
|
|
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
|
|
bool isRecording(false);
|
|
|
|
// Store state about activated recording to be able to restore it after the
|
|
// recording device has been modified.
|
|
if (_shared->audio_device()->Recording()) {
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"SetRecordingDevice() device is modified while recording"
|
|
" is active...");
|
|
isRecording = true;
|
|
if (_shared->audio_device()->StopRecording() == -1) {
|
|
_shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
|
|
"SetRecordingDevice() unable to stop recording");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// We let the module do the index sanity
|
|
|
|
// Set recording channel
|
|
AudioDeviceModule::ChannelType recCh = AudioDeviceModule::kChannelBoth;
|
|
switch (recordingChannel) {
|
|
case kStereoLeft:
|
|
recCh = AudioDeviceModule::kChannelLeft;
|
|
break;
|
|
case kStereoRight:
|
|
recCh = AudioDeviceModule::kChannelRight;
|
|
break;
|
|
case kStereoBoth:
|
|
// default setting kChannelBoth (<=> mono)
|
|
break;
|
|
}
|
|
|
|
if (_shared->audio_device()->SetRecordingChannel(recCh) != 0) {
|
|
_shared->SetLastError(
|
|
VE_AUDIO_DEVICE_MODULE_ERROR, kTraceWarning,
|
|
"SetRecordingChannel() unable to set the recording channel");
|
|
}
|
|
|
|
// Map indices to unsigned since underlying functions need that
|
|
uint16_t indexU = static_cast<uint16_t>(index);
|
|
|
|
int32_t res(0);
|
|
|
|
if (index == -1) {
|
|
res = _shared->audio_device()->SetRecordingDevice(
|
|
AudioDeviceModule::kDefaultCommunicationDevice);
|
|
} else if (index == -2) {
|
|
res = _shared->audio_device()->SetRecordingDevice(
|
|
AudioDeviceModule::kDefaultDevice);
|
|
} else {
|
|
res = _shared->audio_device()->SetRecordingDevice(indexU);
|
|
}
|
|
|
|
if (res != 0) {
|
|
_shared->SetLastError(
|
|
VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
|
|
"SetRecordingDevice() unable to set the recording device");
|
|
return -1;
|
|
}
|
|
|
|
// Init microphone, so user can do volume settings etc
|
|
if (_shared->audio_device()->InitMicrophone() == -1) {
|
|
_shared->SetLastError(VE_CANNOT_ACCESS_MIC_VOL, kTraceWarning,
|
|
"SetRecordingDevice() cannot access microphone");
|
|
}
|
|
|
|
// Set number of channels
|
|
bool available = false;
|
|
if (_shared->audio_device()->StereoRecordingIsAvailable(&available) != 0) {
|
|
_shared->SetLastError(
|
|
VE_SOUNDCARD_ERROR, kTraceWarning,
|
|
"StereoRecordingIsAvailable() failed to query stereo recording");
|
|
}
|
|
|
|
if (_shared->audio_device()->SetStereoRecording(available) != 0) {
|
|
_shared->SetLastError(
|
|
VE_SOUNDCARD_ERROR, kTraceWarning,
|
|
"SetRecordingDevice() failed to set mono recording mode");
|
|
}
|
|
|
|
// Restore recording if it was enabled already when calling this function.
|
|
if (isRecording) {
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"SetRecordingDevice() recording is now being restored...");
|
|
if (_shared->audio_device()->InitRecording() != 0) {
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice,
|
|
VoEId(_shared->instance_id(), -1),
|
|
"SetRecordingDevice() failed to initialize recording");
|
|
return -1;
|
|
}
|
|
if (_shared->audio_device()->StartRecording() != 0) {
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice,
|
|
VoEId(_shared->instance_id(), -1),
|
|
"SetRecordingDevice() failed to start recording");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEHardwareImpl::SetPlayoutDevice(int index) {
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"SetPlayoutDevice(index=%d)", index);
|
|
rtc::CritScope cs(_shared->crit_sec());
|
|
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
|
|
bool isPlaying(false);
|
|
|
|
// Store state about activated playout to be able to restore it after the
|
|
// playout device has been modified.
|
|
if (_shared->audio_device()->Playing()) {
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"SetPlayoutDevice() device is modified while playout is "
|
|
"active...");
|
|
isPlaying = true;
|
|
if (_shared->audio_device()->StopPlayout() == -1) {
|
|
_shared->SetLastError(VE_AUDIO_DEVICE_MODULE_ERROR, kTraceError,
|
|
"SetPlayoutDevice() unable to stop playout");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
// We let the module do the index sanity
|
|
|
|
// Map indices to unsigned since underlying functions need that
|
|
uint16_t indexU = static_cast<uint16_t>(index);
|
|
|
|
int32_t res(0);
|
|
|
|
if (index == -1) {
|
|
res = _shared->audio_device()->SetPlayoutDevice(
|
|
AudioDeviceModule::kDefaultCommunicationDevice);
|
|
} else if (index == -2) {
|
|
res = _shared->audio_device()->SetPlayoutDevice(
|
|
AudioDeviceModule::kDefaultDevice);
|
|
} else {
|
|
res = _shared->audio_device()->SetPlayoutDevice(indexU);
|
|
}
|
|
|
|
if (res != 0) {
|
|
_shared->SetLastError(
|
|
VE_SOUNDCARD_ERROR, kTraceError,
|
|
"SetPlayoutDevice() unable to set the playout device");
|
|
return -1;
|
|
}
|
|
|
|
// Init speaker, so user can do volume settings etc
|
|
if (_shared->audio_device()->InitSpeaker() == -1) {
|
|
_shared->SetLastError(VE_CANNOT_ACCESS_SPEAKER_VOL, kTraceWarning,
|
|
"SetPlayoutDevice() cannot access speaker");
|
|
}
|
|
|
|
// Set number of channels
|
|
bool available = false;
|
|
_shared->audio_device()->StereoPlayoutIsAvailable(&available);
|
|
if (_shared->audio_device()->SetStereoPlayout(available) != 0) {
|
|
_shared->SetLastError(
|
|
VE_SOUNDCARD_ERROR, kTraceWarning,
|
|
"SetPlayoutDevice() failed to set stereo playout mode");
|
|
}
|
|
|
|
// Restore playout if it was enabled already when calling this function.
|
|
if (isPlaying) {
|
|
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"SetPlayoutDevice() playout is now being restored...");
|
|
if (_shared->audio_device()->InitPlayout() != 0) {
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice,
|
|
VoEId(_shared->instance_id(), -1),
|
|
"SetPlayoutDevice() failed to initialize playout");
|
|
return -1;
|
|
}
|
|
if (_shared->audio_device()->StartPlayout() != 0) {
|
|
WEBRTC_TRACE(kTraceError, kTraceVoice,
|
|
VoEId(_shared->instance_id(), -1),
|
|
"SetPlayoutDevice() failed to start playout");
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEHardwareImpl::SetRecordingSampleRate(unsigned int samples_per_sec) {
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"%s", __FUNCTION__);
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return false;
|
|
}
|
|
return _shared->audio_device()->SetRecordingSampleRate(samples_per_sec);
|
|
}
|
|
|
|
int VoEHardwareImpl::RecordingSampleRate(unsigned int* samples_per_sec) const {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return false;
|
|
}
|
|
return _shared->audio_device()->RecordingSampleRate(samples_per_sec);
|
|
}
|
|
|
|
int VoEHardwareImpl::SetPlayoutSampleRate(unsigned int samples_per_sec) {
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
|
|
"%s", __FUNCTION__);
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return false;
|
|
}
|
|
return _shared->audio_device()->SetPlayoutSampleRate(samples_per_sec);
|
|
}
|
|
|
|
int VoEHardwareImpl::PlayoutSampleRate(unsigned int* samples_per_sec) const {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return false;
|
|
}
|
|
return _shared->audio_device()->PlayoutSampleRate(samples_per_sec);
|
|
}
|
|
|
|
bool VoEHardwareImpl::BuiltInAECIsAvailable() const {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return false;
|
|
}
|
|
return _shared->audio_device()->BuiltInAECIsAvailable();
|
|
}
|
|
|
|
int VoEHardwareImpl::EnableBuiltInAEC(bool enable) {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
return _shared->audio_device()->EnableBuiltInAEC(enable);
|
|
}
|
|
|
|
bool VoEHardwareImpl::BuiltInAGCIsAvailable() const {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return false;
|
|
}
|
|
return _shared->audio_device()->BuiltInAGCIsAvailable();
|
|
}
|
|
|
|
int VoEHardwareImpl::EnableBuiltInAGC(bool enable) {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
return _shared->audio_device()->EnableBuiltInAGC(enable);
|
|
}
|
|
|
|
bool VoEHardwareImpl::BuiltInNSIsAvailable() const {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return false;
|
|
}
|
|
return _shared->audio_device()->BuiltInNSIsAvailable();
|
|
}
|
|
|
|
int VoEHardwareImpl::EnableBuiltInNS(bool enable) {
|
|
if (!_shared->statistics().Initialized()) {
|
|
_shared->SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
return _shared->audio_device()->EnableBuiltInNS(enable);
|
|
}
|
|
|
|
#endif // WEBRTC_VOICE_ENGINE_HARDWARE_API
|
|
|
|
} // namespace webrtc
|