Integrate the built-in WASAPI AEC DMO to VoE.

Review URL: http://webrtc-codereview.appspot.com/108006

git-svn-id: http://webrtc.googlecode.com/svn/trunk@592 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
andrew@webrtc.org 2011-09-13 17:17:49 +00:00
parent b1b3e67c97
commit a3c6d61c44
16 changed files with 1250 additions and 181 deletions

View File

@ -17,6 +17,7 @@ modules/audio_device/main/source/Mac/portaudio/pa_ringbuffer.c
modules/audio_processing/utility/fft4g.c
modules/audio_processing/aec/main/source/aec_core_rdft.c
system_wrappers/interface/fix_interlocked_exchange_pointer_windows.h
system_wrappers/interface/scoped_refptr.h
system_wrappers/source/condition_variable_windows.cc
system_wrappers/source/spreadsortlib/constants.hpp
system_wrappers/source/spreadsortlib/spreadsort.hpp

View File

@ -184,6 +184,17 @@ class AudioDeviceModule : public RefCountedModule {
virtual int32_t SetLoudspeakerStatus(bool enable) = 0;
virtual int32_t GetLoudspeakerStatus(bool* enabled) const = 0;
// *Experimental - not recommended for use.*
// Enables the Windows Core Audio built-in AEC. Fails on other platforms.
//
// Must be called before InitRecording(). When enabled:
// 1. StartPlayout() must be called before StartRecording().
// 2. StopRecording() should be called before StopPlayout().
// The reverse order may cause garbage audio to be rendered or the
// capture side to halt util StopRecording() is called.
virtual int32_t EnableBuiltInAEC(bool enable) { return -1; }
virtual bool BuiltInAECIsEnabled() const { return false; }
protected:
virtual ~AudioDeviceModule() {};
};

View File

@ -139,6 +139,17 @@
],
},
}],
['OS=="win"', {
'link_settings': {
'libraries': [
# Required for the built-in WASAPI AEC.
'-ldmoguids.lib',
'-lwmcodecdspuuid.lib',
'-lamstrmid.lib',
'-lmsdmo.lib',
],
},
}],
], # conditions
}], # include_internal_audio_device==1
], # conditions

View File

@ -54,9 +54,23 @@ WebRtc_Word32 AudioDeviceGeneric::SoundDeviceControl(unsigned int par1,
unsigned int par2, unsigned int par3, unsigned int par4)
{
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
"Reset audio device not supported on this platform");
"Sound device control not supported on this platform");
return -1;
}
int32_t AudioDeviceGeneric::EnableBuiltInAEC(bool enable)
{
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
"Windows AEC not supported on this platform");
return -1;
}
bool AudioDeviceGeneric::BuiltInAECIsEnabled() const
{
WEBRTC_TRACE(kTraceError, kTraceAudioDevice, -1,
"Windows AEC not supported on this platform");
return false;
}
} // namespace webrtc

View File

@ -157,6 +157,10 @@ class AudioDeviceGeneric
unsigned int par3 = 0,
unsigned int par4 = 0);
// Windows Core Audio only.
virtual int32_t EnableBuiltInAEC(bool enable);
virtual bool BuiltInAECIsEnabled() const;
public:
virtual bool PlayoutWarning() const = 0;
virtual bool PlayoutError() const = 0;

View File

@ -12,7 +12,7 @@
#include "audio_device_config.h"
#include "system_wrappers/interface/ref_count.h"
#include <cassert>
#include <assert.h>
#include <string.h>
#if defined(_WIN32)
@ -2106,6 +2106,22 @@ WebRtc_Word32 AudioDeviceModuleImpl::GetLoudspeakerStatus(bool* enabled) const
return 0;
}
int32_t AudioDeviceModuleImpl::EnableBuiltInAEC(bool enable)
{
WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);
CHECK_INITIALIZED();
return _ptrAudioDevice->EnableBuiltInAEC(enable);
}
bool AudioDeviceModuleImpl::BuiltInAECIsEnabled() const
{
WEBRTC_TRACE(kTraceModuleCall, kTraceAudioDevice, _id, "%s", __FUNCTION__);
CHECK_INITIALIZED_BOOL();
return _ptrAudioDevice->BuiltInAECIsEnabled();
}
// ============================================================================
// Private Methods
// ============================================================================

View File

@ -202,6 +202,9 @@ public:
virtual WebRtc_Word32 SetLoudspeakerStatus(bool enable);
virtual WebRtc_Word32 GetLoudspeakerStatus(bool* enabled) const;
virtual int32_t EnableBuiltInAEC(bool enable);
virtual bool BuiltInAECIsEnabled() const;
public:
WebRtc_Word32 Id() {return _id;}

View File

@ -16,13 +16,17 @@
#include "audio_device_generic.h"
#pragma once
#include <Mmdeviceapi.h> // MMDevice
#include <Audioclient.h> // WASAPI
#include <avrt.h> // Avrt
#include <endpointvolume.h>
#include <wmcodecdsp.h> // CLSID_CWMAudioAEC
// (must be before audioclient.h)
#include <Audioclient.h> // WASAPI
#include <Audiopolicy.h>
#include <avrt.h> // Avrt
#include <endpointvolume.h>
#include <mediaobj.h> // IMediaObject
#include <Mmdeviceapi.h> // MMDevice
#include "critical_section_wrapper.h"
#include "scoped_refptr.h"
// Use Multimedia Class Scheduler Service (MMCSS) to boost the thread priority
#pragma comment( lib, "avrt.lib" )
@ -148,6 +152,9 @@ public:
// CPU load
virtual WebRtc_Word32 CPULoad(WebRtc_UWord16& load) const;
virtual int32_t EnableBuiltInAEC(bool enable);
virtual bool BuiltInAECIsEnabled() const;
public:
virtual bool PlayoutWarning() const;
virtual bool PlayoutError() const;
@ -169,8 +176,13 @@ private: // avrt function pointers
bool _winSupportAvrt;
private: // thread functions
DWORD InitCaptureThreadPriority();
void RevertCaptureThreadPriority();
static DWORD WINAPI WSAPICaptureThread(LPVOID context);
DWORD DoCaptureThread();
static DWORD WINAPI WSAPICaptureThreadPollDMO(LPVOID context);
DWORD DoCaptureThreadPollDMO();
static DWORD WINAPI WSAPIRenderThread(LPVOID context);
DWORD DoRenderThread();
@ -189,6 +201,16 @@ private:
WebRtc_Word32 Id() {return _id;}
private:
int SetDMOProperties();
int SetBoolProperty(IPropertyStore* ptrPS,
REFPROPERTYKEY key,
VARIANT_BOOL value);
int SetVtI4Property(IPropertyStore* ptrPS,
REFPROPERTYKEY key,
LONG value);
WebRtc_Word32 _EnumerateEndpointDevicesAll(EDataFlow dataFlow) const;
void _TraceCOMError(HRESULT hr) const;
@ -199,6 +221,7 @@ private:
WebRtc_Word32 _GetDeviceName(IMMDevice* pDevice, LPWSTR pszBuffer, int bufferLen);
WebRtc_Word32 _GetListDeviceID(EDataFlow dir, int index, LPWSTR szBuffer, int bufferLen);
WebRtc_Word32 _GetDefaultDeviceID(EDataFlow dir, ERole role, LPWSTR szBuffer, int bufferLen);
WebRtc_Word32 _GetDefaultDeviceIndex(EDataFlow dir, ERole role, int* index);
WebRtc_Word32 _GetDeviceID(IMMDevice* pDevice, LPWSTR pszBuffer, int bufferLen);
WebRtc_Word32 _GetDefaultDevice(EDataFlow dir, ERole role, IMMDevice** ppDevice);
WebRtc_Word32 _GetListDevice(EDataFlow dir, int index, IMMDevice** ppDevice);
@ -209,6 +232,8 @@ private:
// Does nothing if UNICODE is undefined.
char* WideToUTF8(const TCHAR* src) const;
WebRtc_Word32 InitRecordingDMO();
private:
AudioDeviceBuffer* _ptrAudioBuffer;
CriticalSectionWrapper& _critSect;
@ -231,6 +256,11 @@ private: // WASAPI
ISimpleAudioVolume* _ptrRenderSimpleVolume;
IAudioEndpointVolume* _ptrRenderEndpointVolume;
// DirectX Media Object (DMO) for the built-in AEC.
scoped_refptr<IMediaObject> _dmo;
scoped_refptr<IMediaBuffer> _mediaBuffer;
bool _builtInAecEnabled;
HANDLE _hRenderSamplesReadyEvent;
HANDLE _hPlayThread;
HANDLE _hRenderStartedEvent;
@ -245,6 +275,8 @@ private: // WASAPI
HANDLE _hSetCaptureVolumeThread;
HANDLE _hSetCaptureVolumeEvent;
HANDLE _hMmTask;
UINT _playAudioFrameSize;
WebRtc_UWord32 _playSampleRate;
WebRtc_UWord32 _devicePlaySampleRate;

View File

@ -0,0 +1,137 @@
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file under third_party_mods/chromium or at:
// http://src.chromium.org/svn/trunk/src/LICENSE
#ifndef SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_
#define SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_
namespace webrtc {
// Extracted from Chromium's src/base/memory/ref_counted.h.
//
// A smart pointer class for reference counted objects. Use this class instead
// of calling AddRef and Release manually on a reference counted object to
// avoid common memory leaks caused by forgetting to Release an object
// reference. Sample usage:
//
// class MyFoo : public RefCounted<MyFoo> {
// ...
// };
//
// void some_function() {
// scoped_refptr<MyFoo> foo = new MyFoo();
// foo->Method(param);
// // |foo| is released when this function returns
// }
//
// void some_other_function() {
// scoped_refptr<MyFoo> foo = new MyFoo();
// ...
// foo = NULL; // explicitly releases |foo|
// ...
// if (foo)
// foo->Method(param);
// }
//
// The above examples show how scoped_refptr<T> acts like a pointer to T.
// Given two scoped_refptr<T> classes, it is also possible to exchange
// references between the two objects, like so:
//
// {
// scoped_refptr<MyFoo> a = new MyFoo();
// scoped_refptr<MyFoo> b;
//
// b.swap(a);
// // now, |b| references the MyFoo object, and |a| references NULL.
// }
//
// To make both |a| and |b| in the above example reference the same MyFoo
// object, simply use the assignment operator:
//
// {
// scoped_refptr<MyFoo> a = new MyFoo();
// scoped_refptr<MyFoo> b;
//
// b = a;
// // now, |a| and |b| each own a reference to the same MyFoo object.
// }
//
template <class T>
class scoped_refptr {
public:
scoped_refptr() : ptr_(NULL) {
}
scoped_refptr(T* p) : ptr_(p) {
if (ptr_)
ptr_->AddRef();
}
scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
if (ptr_)
ptr_->AddRef();
}
template <typename U>
scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
if (ptr_)
ptr_->AddRef();
}
~scoped_refptr() {
if (ptr_)
ptr_->Release();
}
T* get() const { return ptr_; }
operator T*() const { return ptr_; }
T* operator->() const { return ptr_; }
// Release a pointer.
// The return value is the current pointer held by this object.
// If this object holds a NULL pointer, the return value is NULL.
// After this operation, this object will hold a NULL pointer,
// and will not own the object any more.
T* release() {
T* retVal = ptr_;
ptr_ = NULL;
return retVal;
}
scoped_refptr<T>& operator=(T* p) {
// AddRef first so that self assignment should work
if (p)
p->AddRef();
if (ptr_ )
ptr_->Release();
ptr_ = p;
return *this;
}
scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
return *this = r.ptr_;
}
template <typename U>
scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
return *this = r.get();
}
void swap(T** pp) {
T* p = ptr_;
ptr_ = *pp;
*pp = p;
}
void swap(scoped_refptr<T>& r) {
swap(&r.ptr_);
}
protected:
T* ptr_;
};
} // namespace webrtc
#endif // SYSTEM_WRAPPERS_INTERFACE_SCOPED_REFPTR_H_

View File

@ -1,6 +1,10 @@
# Copyright (c) 2009 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
# Copyright (c) 2011 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.
# TODO: Rename files to use *_linux.cpp etc. names, to automatically include relevant files. Remove conditions section.
@ -32,11 +36,13 @@
'../interface/data_log_impl.h',
'../interface/event_wrapper.h',
'../interface/file_wrapper.h',
'../interface/fix_interlocked_exchange_pointer_windows.h',
'../interface/list_wrapper.h',
'../interface/map_wrapper.h',
'../interface/ref_count.h',
'../interface/rw_lock_wrapper.h',
'../interface/scoped_ptr.h',
'../interface/scoped_refptr.h',
'../interface/sort.h',
'../interface/thread_wrapper.h',
'../interface/tick_util.h',
@ -71,6 +77,7 @@
'thread.cc',
'thread_posix.h',
'thread_windows.h',
'thread_windows_set_name.h',
'trace_impl.cc',
'trace_impl.h',
'trace_posix.h',

View File

@ -110,6 +110,25 @@ public:
// Not supported
virtual int GetLoudspeakerStatus(bool& enabled) = 0;
// *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 util 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() {}
virtual ~VoEHardware() {}

View File

@ -815,6 +815,32 @@ int VoEHardwareImpl::GetSystemCPULoad(int& loadPercent)
return 0;
}
int VoEHardwareImpl::EnableBuiltInAEC(bool enable)
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"%s", __FUNCTION__);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
return _audioDevicePtr->EnableBuiltInAEC(enable);
}
bool VoEHardwareImpl::BuiltInAECIsEnabled() const
{
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId, -1),
"%s", __FUNCTION__);
if (!_engineStatistics.Initialized())
{
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
return false;
}
return _audioDevicePtr->BuiltInAECIsEnabled();
}
#endif // WEBRTC_VOICE_ENGINE_HARDWARE_API
} // namespace webrtc

View File

@ -67,6 +67,9 @@ public:
virtual int GetLoudspeakerStatus(bool& enabled);
virtual int EnableBuiltInAEC(bool enable);
virtual bool BuiltInAECIsEnabled() const;
protected:
VoEHardwareImpl();
virtual ~VoEHardwareImpl();

View File

@ -2004,6 +2004,80 @@ int VoETestManager::DoStandardTest()
SLEEP(2000);
#endif // #ifdef MAC_IPHONE
TEST_LOG("\nBuilt-in WASAPI AEC tests\n");
TEST_MUSTPASS(base->StopSend(0));
TEST_MUSTPASS(base->StopPlayout(0));
TEST_MUSTPASS(hardware->GetAudioDeviceLayer(givenLayer));
if (givenLayer != kAudioWindowsCore)
{
TEST_MUSTFAIL(hardware->EnableBuiltInAEC(true));
TEST_MUSTFAIL(hardware->EnableBuiltInAEC(false));
}
else
{
TEST_MUSTPASS(base->StartSend(0));
// Can't be set after StartSend().
TEST_MUSTFAIL(hardware->EnableBuiltInAEC(true));
TEST_MUSTFAIL(hardware->EnableBuiltInAEC(false));
TEST_MUSTPASS(base->StopSend(0));
TEST_MUSTPASS(hardware->EnableBuiltInAEC(true));
// Can't be called before StartPlayout().
TEST_MUSTFAIL(base->StartSend(0));
TEST_MUSTPASS(base->StartPlayout(0));
TEST_MUSTPASS(base->StartSend(0));
TEST_LOG("Processing capture data with built-in AEC...\n");
SLEEP(2000);
TEST_LOG("Looping through capture devices...\n");
TEST_MUSTPASS(hardware->GetNumOfRecordingDevices(nRec));
for (idx = 0; idx < nRec; idx++)
{
TEST_MUSTPASS(hardware->GetRecordingDeviceName(idx,
devName,
guidName));
TEST_LOG("%d: %s\n", idx, devName);
TEST_MUSTPASS(hardware->SetRecordingDevice(idx));
SLEEP(2000);
}
TEST_MUSTPASS(hardware->SetPlayoutDevice(-1));
TEST_MUSTPASS(hardware->SetRecordingDevice(-1));
TEST_LOG("Looping through render devices, restarting for each "
"device...\n");
TEST_MUSTPASS(hardware->GetNumOfPlayoutDevices(nPlay));
for (idx = 0; idx < nPlay; idx++)
{
TEST_MUSTPASS(hardware->GetPlayoutDeviceName(idx,
devName,
guidName));
TEST_LOG("%d: %s\n", idx, devName);
TEST_MUSTPASS(hardware->SetPlayoutDevice(idx));
SLEEP(2000);
}
TEST_LOG("Using default devices...\n");
TEST_MUSTPASS(hardware->SetRecordingDevice(-1));
TEST_MUSTPASS(hardware->SetPlayoutDevice(-1));
SLEEP(2000);
// Possible, but not recommended before StopSend().
TEST_MUSTPASS(base->StopPlayout(0));
TEST_MUSTPASS(base->StopSend(0));
TEST_MUSTPASS(base->StopPlayout(0));
SLEEP(2000); // To verify that there is no garbage audio.
TEST_LOG("Disabling built-in AEC.\n");
TEST_MUSTPASS(hardware->EnableBuiltInAEC(false));
TEST_MUSTPASS(base->StartSend(0));
TEST_MUSTPASS(base->StartPlayout(0));
}
#else
TEST_LOG("\n\n+++ More hardware tests NOT ENABLED +++\n");
#endif

View File

@ -27,7 +27,7 @@
#endif
// Select the tests to execute, list order below is same as they will be
// executed. Note that, all settings below will be overrided by sub-API
// executed. Note that, all settings below will be overriden by sub-API
// settings in engine_configurations.h.
#define _TEST_BASE_
#define _TEST_RTP_RTCP_