diff --git a/webrtc/voice_engine/include/voe_hardware.h b/webrtc/voice_engine/include/voe_hardware.h index caea2089f0..5c247d7436 100644 --- a/webrtc/voice_engine/include/voe_hardware.h +++ b/webrtc/voice_engine/include/voe_hardware.h @@ -70,6 +70,12 @@ public: virtual int GetPlayoutDeviceName(int index, char strNameUTF8[128], char strGuidUTF8[128]) = 0; + // Checks if the sound card is available to be opened for recording. + virtual int GetRecordingDeviceStatus(bool& isAvailable) = 0; + + // Checks if the sound card is available to be opened for playout. + virtual int GetPlayoutDeviceStatus(bool& isAvailable) = 0; + // Sets the audio device used for recording. virtual int SetRecordingDevice( int index, StereoChannel recordingChannel = kStereoBoth) = 0; @@ -83,24 +89,47 @@ public: // Gets the currently used (active) audio device layer. virtual int GetAudioDeviceLayer(AudioLayers& audioLayer) = 0; + // Gets the VoiceEngine's current CPU consumption in terms of the percent + // of total CPU availability. [Windows only] + virtual int GetCPULoad(int& loadPercent) = 0; + + // Not supported + virtual int ResetAudioDevice() = 0; + + // Not supported + virtual int AudioDeviceControl( + unsigned int par1, unsigned int par2, unsigned int par3) = 0; + + // Not supported + virtual int SetLoudspeakerStatus(bool enable) = 0; + + // Not supported + virtual int GetLoudspeakerStatus(bool& enabled) = 0; + // Native sample rate controls (samples/sec) virtual int SetRecordingSampleRate(unsigned int samples_per_sec) = 0; virtual int RecordingSampleRate(unsigned int* samples_per_sec) const = 0; virtual int SetPlayoutSampleRate(unsigned int samples_per_sec) = 0; virtual int PlayoutSampleRate(unsigned int* samples_per_sec) const = 0; - // To be removed. Don't use. - virtual int EnableBuiltInAEC(bool enable) { return -1; } - virtual bool BuiltInAECIsEnabled() const { return -1; } - virtual int GetRecordingDeviceStatus(bool& isAvailable) { return -1; } - virtual int GetPlayoutDeviceStatus(bool& isAvailable) { return -1; } - virtual int ResetAudioDevice() { return -1; } - virtual int AudioDeviceControl(unsigned int par1, unsigned int par2, - unsigned int par3) { return -1; } - virtual int SetLoudspeakerStatus(bool enable) { return -1; } - virtual int GetLoudspeakerStatus(bool& enabled) { return -1; } - virtual int GetCPULoad(int& loadPercent) { return -1; } - + // *Experimental - not recommended for use.* + // Enables the Windows Core Audio built-in AEC. Fails on other platforms. + // + // Currently incompatible with the standard VoE AEC and AGC; don't attempt + // to enable them while this is active. + // + // Must be called before VoEBase::StartSend(). When enabled: + // 1. VoEBase::StartPlayout() must be called before VoEBase::StartSend(). + // 2. VoEBase::StopSend() should be called before VoEBase::StopPlayout(). + // The reverse order may cause garbage audio to be rendered or the + // capture side to halt until StopSend() is called. + // + // As a consequence, SetPlayoutDevice() should be used with caution + // during a call. It will function, but may cause the above issues for + // the duration it takes to complete. (In practice, it should complete + // fast enough to avoid audible degradation). + virtual int EnableBuiltInAEC(bool enable) = 0; + virtual bool BuiltInAECIsEnabled() const = 0; protected: VoEHardware() {} diff --git a/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc b/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc index 0887fd50b7..e8863c57c4 100644 --- a/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc +++ b/webrtc/voice_engine/test/auto_test/standard/hardware_before_streaming_test.cc @@ -51,6 +51,20 @@ static const char* kNoDevicesErrorMessage = "Either you have no recording / playout device " "on your system, or the method failed."; +TEST_F(HardwareBeforeStreamingTest, GetPlayoutDeviceStatusReturnsTrue) { + bool play_available = false; + EXPECT_EQ(0, voe_hardware_->GetPlayoutDeviceStatus(play_available)); + ASSERT_TRUE(play_available) << + "Ensures that the method works and that hardware is in the right state."; +} + +TEST_F(HardwareBeforeStreamingTest, GetRecordingDeviceStatusReturnsTrue) { + bool recording_available = false; + EXPECT_EQ(0, voe_hardware_->GetRecordingDeviceStatus(recording_available)); + EXPECT_TRUE(recording_available) << + "Ensures that the method works and that hardware is in the right state."; +} + // Win, Mac and Linux sound device tests. TEST_F(HardwareBeforeStreamingTest, GetRecordingDeviceNameRetrievesDeviceNames) { diff --git a/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc b/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc index eceef54d2a..fabdf0c2e6 100644 --- a/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc +++ b/webrtc/voice_engine/test/auto_test/standard/hardware_test.cc @@ -67,3 +67,89 @@ TEST_F(HardwareTest, GetCpuLoadReturnsErrorOnNonWindowsPlatform) { EXPECT_EQ(-1, voe_hardware_->GetCPULoad(load)); } #endif + +// Flakily hangs on Windows: code.google.com/p/webrtc/issues/detail?id=2179. +TEST_F(HardwareTest, + DISABLED_ON_WIN(BuiltInWasapiAECWorksForAudioWindowsCoreAudioLayer)) { +#ifdef WEBRTC_IOS + // Ensure the sound device is reset on iPhone. + EXPECT_EQ(0, voe_hardware_->ResetAudioDevice()); + Sleep(2000); +#endif + EXPECT_EQ(0, voe_base_->StopSend(channel_)); + EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); + + webrtc::AudioLayers given_layer; + EXPECT_EQ(0, voe_hardware_->GetAudioDeviceLayer(given_layer)); + if (given_layer != webrtc::kAudioWindowsCore) { + // Not Windows Audio Core - then it shouldn't work. + EXPECT_EQ(-1, voe_hardware_->EnableBuiltInAEC(true)); + EXPECT_EQ(-1, voe_hardware_->EnableBuiltInAEC(false)); + return; + } + + TEST_LOG("Testing AEC for Audio Windows Core.\n"); + EXPECT_EQ(0, voe_base_->StartSend(channel_)); + + // Can't be set after StartSend(). + EXPECT_EQ(-1, voe_hardware_->EnableBuiltInAEC(true)); + EXPECT_EQ(-1, voe_hardware_->EnableBuiltInAEC(false)); + + EXPECT_EQ(0, voe_base_->StopSend(channel_)); + EXPECT_EQ(0, voe_hardware_->EnableBuiltInAEC(true)); + + // Can't be called before StartPlayout(). + EXPECT_EQ(-1, voe_base_->StartSend(channel_)); + + EXPECT_EQ(0, voe_base_->StartPlayout(channel_)); + EXPECT_EQ(0, voe_base_->StartSend(channel_)); + TEST_LOG("Processing capture data with built-in AEC...\n"); + Sleep(2000); + + TEST_LOG("Looping through capture devices...\n"); + int num_devs = 0; + char dev_name[128] = { 0 }; + char guid_name[128] = { 0 }; + EXPECT_EQ(0, voe_hardware_->GetNumOfRecordingDevices(num_devs)); + for (int dev_index = 0; dev_index < num_devs; ++dev_index) { + EXPECT_EQ(0, voe_hardware_->GetRecordingDeviceName(dev_index, + dev_name, + guid_name)); + TEST_LOG("%d: %s\n", dev_index, dev_name); + EXPECT_EQ(0, voe_hardware_->SetRecordingDevice(dev_index)); + Sleep(2000); + } + + EXPECT_EQ(0, voe_hardware_->SetPlayoutDevice(-1)); + EXPECT_EQ(0, voe_hardware_->SetRecordingDevice(-1)); + + TEST_LOG("Looping through render devices, restarting for each " + "device...\n"); + EXPECT_EQ(0, voe_hardware_->GetNumOfPlayoutDevices(num_devs)); + for (int dev_index = 0; dev_index < num_devs; ++dev_index) { + EXPECT_EQ(0, voe_hardware_->GetPlayoutDeviceName(dev_index, + dev_name, + guid_name)); + TEST_LOG("%d: %s\n", dev_index, dev_name); + EXPECT_EQ(0, voe_hardware_->SetPlayoutDevice(dev_index)); + Sleep(2000); + } + + TEST_LOG("Using default devices...\n"); + EXPECT_EQ(0, voe_hardware_->SetRecordingDevice(-1)); + EXPECT_EQ(0, voe_hardware_->SetPlayoutDevice(-1)); + Sleep(2000); + + // Possible, but not recommended before StopSend(). + EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); + + EXPECT_EQ(0, voe_base_->StopSend(channel_)); + EXPECT_EQ(0, voe_base_->StopPlayout(channel_)); + Sleep(2000); // To verify that there is no garbage audio. + + TEST_LOG("Disabling built-in AEC.\n"); + EXPECT_EQ(0, voe_hardware_->EnableBuiltInAEC(false)); + + EXPECT_EQ(0, voe_base_->StartSend(channel_)); + EXPECT_EQ(0, voe_base_->StartPlayout(channel_)); +} diff --git a/webrtc/voice_engine/voe_hardware_impl.cc b/webrtc/voice_engine/voe_hardware_impl.cc index eaf2a28a38..896572f108 100644 --- a/webrtc/voice_engine/voe_hardware_impl.cc +++ b/webrtc/voice_engine/voe_hardware_impl.cc @@ -528,6 +528,219 @@ int VoEHardwareImpl::SetPlayoutDevice(int index) return 0; } +int VoEHardwareImpl::GetRecordingDeviceStatus(bool& isAvailable) +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "GetRecordingDeviceStatus()"); + + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + + // We let the module do isRecording sanity + + bool available(false); + + // Check availability + if (_shared->audio_device()->RecordingIsAvailable(&available) != 0) + { + _shared->SetLastError(VE_UNDEFINED_SC_REC_ERR, kTraceError, + " Audio Device error"); + return -1; + } + + isAvailable = available; + + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, + VoEId(_shared->instance_id(), -1), + " Output: isAvailable = %d)", (int) isAvailable); + + return 0; +} + +int VoEHardwareImpl::GetPlayoutDeviceStatus(bool& isAvailable) +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "GetPlayoutDeviceStatus()"); + + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + + // We let the module do isPlaying sanity + + bool available(false); + + // Check availability + if (_shared->audio_device()->PlayoutIsAvailable(&available) != 0) + { + _shared->SetLastError(VE_PLAY_UNDEFINED_SC_ERR, kTraceError, + " Audio Device error"); + return -1; + } + + isAvailable = available; + + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, + VoEId(_shared->instance_id(), -1), + " Output: isAvailable = %d)", (int) isAvailable); + + return 0; +} + +int VoEHardwareImpl::ResetAudioDevice() +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "ResetAudioDevice()"); + + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + +#if defined(WEBRTC_IOS) + if (_shared->audio_device()->ResetAudioDevice() < 0) + { + _shared->SetLastError(VE_SOUNDCARD_ERROR, kTraceError, + " Failed to reset sound device"); + return -1; + } + return 0; +#else + _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, + " no support for resetting sound device"); + return -1; +#endif +} + +int VoEHardwareImpl::AudioDeviceControl(unsigned int par1, unsigned int par2, + unsigned int par3) +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "AudioDeviceControl(%i, %i, %i)", par1, par2, par3); + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, + " no support for resetting sound device"); + return -1; +} + +int VoEHardwareImpl::SetLoudspeakerStatus(bool enable) +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetLoudspeakerStatus(enable=%i)", (int) enable); + + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } +#if defined(WEBRTC_ANDROID) + if (_shared->audio_device()->SetLoudspeakerStatus(enable) < 0) + { + _shared->SetLastError(VE_IGNORED_FUNCTION, kTraceError, + " Failed to set loudspeaker status"); + return -1; + } + + return 0; +#else + _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, + " no support for setting loudspeaker status"); + return -1; +#endif +} + +int VoEHardwareImpl::GetLoudspeakerStatus(bool& enabled) +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "GetLoudspeakerStatus()"); + +#if defined(WEBRTC_ANDROID) + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + + if (_shared->audio_device()->GetLoudspeakerStatus(&enabled) < 0) + { + _shared->SetLastError(VE_IGNORED_FUNCTION, kTraceError, + " Failed to get loudspeaker status"); + return -1; + } + + return 0; +#else + _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError, + " no support for setting loudspeaker status"); + return -1; +#endif +} + +int VoEHardwareImpl::GetCPULoad(int& loadPercent) +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "GetCPULoad()"); + + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + + // Get CPU load from ADM + uint16_t load(0); + if (_shared->audio_device()->CPULoad(&load) != 0) + { + _shared->SetLastError(VE_CPU_INFO_ERROR, kTraceError, + " error getting system CPU load"); + return -1; + } + + loadPercent = static_cast (load); + + WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, + VoEId(_shared->instance_id(), -1), + " Output: loadPercent = %d", loadPercent); + + return 0; +} + +int VoEHardwareImpl::EnableBuiltInAEC(bool enable) +{ + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "%s", __FUNCTION__); + if (!_shared->statistics().Initialized()) + { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + + return _shared->audio_device()->EnableBuiltInAEC(enable); +} + +bool VoEHardwareImpl::BuiltInAECIsEnabled() const +{ + 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()->BuiltInAECIsEnabled(); +} + int VoEHardwareImpl::SetRecordingSampleRate(unsigned int samples_per_sec) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), "%s", __FUNCTION__); diff --git a/webrtc/voice_engine/voe_hardware_impl.h b/webrtc/voice_engine/voe_hardware_impl.h index f3537efb03..4e06f978d9 100644 --- a/webrtc/voice_engine/voe_hardware_impl.h +++ b/webrtc/voice_engine/voe_hardware_impl.h @@ -33,6 +33,10 @@ public: char strNameUTF8[128], char strGuidUTF8[128]); + virtual int GetRecordingDeviceStatus(bool& isAvailable); + + virtual int GetPlayoutDeviceStatus(bool& isAvailable); + virtual int SetRecordingDevice( int index, StereoChannel recordingChannel = kStereoBoth); @@ -43,6 +47,21 @@ public: virtual int GetAudioDeviceLayer(AudioLayers& audioLayer); + virtual int GetCPULoad(int& loadPercent); + + virtual int ResetAudioDevice(); + + virtual int AudioDeviceControl(unsigned int par1, + unsigned int par2, + unsigned int par3); + + virtual int SetLoudspeakerStatus(bool enable); + + virtual int GetLoudspeakerStatus(bool& enabled); + + virtual int EnableBuiltInAEC(bool enable); + virtual bool BuiltInAECIsEnabled() const; + virtual int SetRecordingSampleRate(unsigned int samples_per_sec); virtual int RecordingSampleRate(unsigned int* samples_per_sec) const; virtual int SetPlayoutSampleRate(unsigned int samples_per_sec);