xref: /aosp_15_r20/external/webrtc/modules/audio_device/win/audio_device_core_win.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
1 /*
2  *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #pragma warning(disable : 4995)  // name was marked as #pragma deprecated
12 
13 #if (_MSC_VER >= 1310) && (_MSC_VER < 1400)
14 // Reports the major and minor versions of the compiler.
15 // For example, 1310 for Microsoft Visual C++ .NET 2003. 1310 represents version
16 // 13 and a 1.0 point release. The Visual C++ 2005 compiler version is 1400.
17 // Type cl /? at the command line to see the major and minor versions of your
18 // compiler along with the build number.
19 #pragma message(">> INFO: Windows Core Audio is not supported in VS 2003")
20 #endif
21 
22 #include "modules/audio_device/audio_device_config.h"
23 
24 #ifdef WEBRTC_WINDOWS_CORE_AUDIO_BUILD
25 
26 // clang-format off
27 // To get Windows includes in the right order, this must come before the Windows
28 // includes below.
29 #include "modules/audio_device/win/audio_device_core_win.h"
30 // clang-format on
31 
32 #include <string.h>
33 
34 #include <comdef.h>
35 #include <dmo.h>
36 #include <functiondiscoverykeys_devpkey.h>
37 #include <mmsystem.h>
38 #include <strsafe.h>
39 #include <uuids.h>
40 #include <windows.h>
41 
42 #include <iomanip>
43 
44 #include "api/make_ref_counted.h"
45 #include "rtc_base/checks.h"
46 #include "rtc_base/logging.h"
47 #include "rtc_base/platform_thread.h"
48 #include "rtc_base/string_utils.h"
49 #include "rtc_base/thread_annotations.h"
50 #include "system_wrappers/include/sleep.h"
51 
52 // Macro that calls a COM method returning HRESULT value.
53 #define EXIT_ON_ERROR(hres) \
54   do {                      \
55     if (FAILED(hres))       \
56       goto Exit;            \
57   } while (0)
58 
59 // Macro that continues to a COM error.
60 #define CONTINUE_ON_ERROR(hres) \
61   do {                          \
62     if (FAILED(hres))           \
63       goto Next;                \
64   } while (0)
65 
66 // Macro that releases a COM object if not NULL.
67 #define SAFE_RELEASE(p) \
68   do {                  \
69     if ((p)) {          \
70       (p)->Release();   \
71       (p) = NULL;       \
72     }                   \
73   } while (0)
74 
75 #define ROUND(x) ((x) >= 0 ? (int)((x) + 0.5) : (int)((x)-0.5))
76 
77 // REFERENCE_TIME time units per millisecond
78 #define REFTIMES_PER_MILLISEC 10000
79 
80 typedef struct tagTHREADNAME_INFO {
81   DWORD dwType;      // must be 0x1000
82   LPCSTR szName;     // pointer to name (in user addr space)
83   DWORD dwThreadID;  // thread ID (-1=caller thread)
84   DWORD dwFlags;     // reserved for future use, must be zero
85 } THREADNAME_INFO;
86 
87 namespace webrtc {
88 namespace {
89 
90 enum { COM_THREADING_MODEL = COINIT_MULTITHREADED };
91 
92 enum { kAecCaptureStreamIndex = 0, kAecRenderStreamIndex = 1 };
93 
94 // An implementation of IMediaBuffer, as required for
95 // IMediaObject::ProcessOutput(). After consuming data provided by
96 // ProcessOutput(), call SetLength() to update the buffer availability.
97 //
98 // Example implementation:
99 // http://msdn.microsoft.com/en-us/library/dd376684(v=vs.85).aspx
100 class MediaBufferImpl final : public IMediaBuffer {
101  public:
MediaBufferImpl(DWORD maxLength)102   explicit MediaBufferImpl(DWORD maxLength)
103       : _data(new BYTE[maxLength]),
104         _length(0),
105         _maxLength(maxLength),
106         _refCount(0) {}
107 
108   // IMediaBuffer methods.
STDMETHOD(GetBufferAndLength (BYTE ** ppBuffer,DWORD * pcbLength))109   STDMETHOD(GetBufferAndLength(BYTE** ppBuffer, DWORD* pcbLength)) {
110     if (!ppBuffer || !pcbLength) {
111       return E_POINTER;
112     }
113 
114     *ppBuffer = _data;
115     *pcbLength = _length;
116 
117     return S_OK;
118   }
119 
STDMETHOD(GetMaxLength (DWORD * pcbMaxLength))120   STDMETHOD(GetMaxLength(DWORD* pcbMaxLength)) {
121     if (!pcbMaxLength) {
122       return E_POINTER;
123     }
124 
125     *pcbMaxLength = _maxLength;
126     return S_OK;
127   }
128 
STDMETHOD(SetLength (DWORD cbLength))129   STDMETHOD(SetLength(DWORD cbLength)) {
130     if (cbLength > _maxLength) {
131       return E_INVALIDARG;
132     }
133 
134     _length = cbLength;
135     return S_OK;
136   }
137 
138   // IUnknown methods.
STDMETHOD_(ULONG,AddRef ())139   STDMETHOD_(ULONG, AddRef()) { return InterlockedIncrement(&_refCount); }
140 
STDMETHOD(QueryInterface (REFIID riid,void ** ppv))141   STDMETHOD(QueryInterface(REFIID riid, void** ppv)) {
142     if (!ppv) {
143       return E_POINTER;
144     } else if (riid != IID_IMediaBuffer && riid != IID_IUnknown) {
145       return E_NOINTERFACE;
146     }
147 
148     *ppv = static_cast<IMediaBuffer*>(this);
149     AddRef();
150     return S_OK;
151   }
152 
STDMETHOD_(ULONG,Release ())153   STDMETHOD_(ULONG, Release()) {
154     LONG refCount = InterlockedDecrement(&_refCount);
155     if (refCount == 0) {
156       delete this;
157     }
158 
159     return refCount;
160   }
161 
162  private:
~MediaBufferImpl()163   ~MediaBufferImpl() { delete[] _data; }
164 
165   BYTE* _data;
166   DWORD _length;
167   const DWORD _maxLength;
168   LONG _refCount;
169 };
170 }  // namespace
171 
172 // ============================================================================
173 //                              Static Methods
174 // ============================================================================
175 
176 // ----------------------------------------------------------------------------
177 //  CoreAudioIsSupported
178 // ----------------------------------------------------------------------------
179 
CoreAudioIsSupported()180 bool AudioDeviceWindowsCore::CoreAudioIsSupported() {
181   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
182 
183   bool MMDeviceIsAvailable(false);
184   bool coreAudioIsSupported(false);
185 
186   HRESULT hr(S_OK);
187   wchar_t buf[MAXERRORLENGTH];
188   wchar_t errorText[MAXERRORLENGTH];
189 
190   // 1) Check if Windows version is Vista SP1 or later.
191   //
192   // CoreAudio is only available on Vista SP1 and later.
193   //
194   OSVERSIONINFOEX osvi;
195   DWORDLONG dwlConditionMask = 0;
196   int op = VER_LESS_EQUAL;
197 
198   // Initialize the OSVERSIONINFOEX structure.
199   ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
200   osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
201   osvi.dwMajorVersion = 6;
202   osvi.dwMinorVersion = 0;
203   osvi.wServicePackMajor = 0;
204   osvi.wServicePackMinor = 0;
205   osvi.wProductType = VER_NT_WORKSTATION;
206 
207   // Initialize the condition mask.
208   VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op);
209   VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op);
210   VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMAJOR, op);
211   VER_SET_CONDITION(dwlConditionMask, VER_SERVICEPACKMINOR, op);
212   VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
213 
214   DWORD dwTypeMask = VER_MAJORVERSION | VER_MINORVERSION |
215                      VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR |
216                      VER_PRODUCT_TYPE;
217 
218   // Perform the test.
219   BOOL isVistaRTMorXP = VerifyVersionInfo(&osvi, dwTypeMask, dwlConditionMask);
220   if (isVistaRTMorXP != 0) {
221     RTC_LOG(LS_VERBOSE)
222         << "*** Windows Core Audio is only supported on Vista SP1 or later";
223     return false;
224   }
225 
226   // 2) Initializes the COM library for use by the calling thread.
227 
228   // The COM init wrapper sets the thread's concurrency model to MTA,
229   // and creates a new apartment for the thread if one is required. The
230   // wrapper also ensures that each call to CoInitializeEx is balanced
231   // by a corresponding call to CoUninitialize.
232   //
233   ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA);
234   if (!comInit.Succeeded()) {
235     // Things will work even if an STA thread is calling this method but we
236     // want to ensure that MTA is used and therefore return false here.
237     return false;
238   }
239 
240   // 3) Check if the MMDevice API is available.
241   //
242   // The Windows Multimedia Device (MMDevice) API enables audio clients to
243   // discover audio endpoint devices, determine their capabilities, and create
244   // driver instances for those devices.
245   // Header file Mmdeviceapi.h defines the interfaces in the MMDevice API.
246   // The MMDevice API consists of several interfaces. The first of these is the
247   // IMMDeviceEnumerator interface. To access the interfaces in the MMDevice
248   // API, a client obtains a reference to the IMMDeviceEnumerator interface of a
249   // device-enumerator object by calling the CoCreateInstance function.
250   //
251   // Through the IMMDeviceEnumerator interface, the client can obtain references
252   // to the other interfaces in the MMDevice API. The MMDevice API implements
253   // the following interfaces:
254   //
255   // IMMDevice            Represents an audio device.
256   // IMMDeviceCollection  Represents a collection of audio devices.
257   // IMMDeviceEnumerator  Provides methods for enumerating audio devices.
258   // IMMEndpoint          Represents an audio endpoint device.
259   //
260   IMMDeviceEnumerator* pIMMD(NULL);
261   const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator);
262   const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator);
263 
264   hr = CoCreateInstance(
265       CLSID_MMDeviceEnumerator,  // GUID value of MMDeviceEnumerator coclass
266       NULL, CLSCTX_ALL,
267       IID_IMMDeviceEnumerator,  // GUID value of the IMMDeviceEnumerator
268                                 // interface
269       (void**)&pIMMD);
270 
271   if (FAILED(hr)) {
272     RTC_LOG(LS_ERROR) << "AudioDeviceWindowsCore::CoreAudioIsSupported()"
273                          " Failed to create the required COM object (hr="
274                       << hr << ")";
275     RTC_LOG(LS_VERBOSE) << "AudioDeviceWindowsCore::CoreAudioIsSupported()"
276                            " CoCreateInstance(MMDeviceEnumerator) failed (hr="
277                         << hr << ")";
278 
279     const DWORD dwFlags =
280         FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
281     const DWORD dwLangID = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
282 
283     // Gets the system's human readable message string for this HRESULT.
284     // All error message in English by default.
285     DWORD messageLength = ::FormatMessageW(dwFlags, 0, hr, dwLangID, errorText,
286                                            MAXERRORLENGTH, NULL);
287 
288     RTC_DCHECK_LE(messageLength, MAXERRORLENGTH);
289 
290     // Trims tailing white space (FormatMessage() leaves a trailing cr-lf.).
291     for (; messageLength && ::isspace(errorText[messageLength - 1]);
292          --messageLength) {
293       errorText[messageLength - 1] = '\0';
294     }
295 
296     StringCchPrintfW(buf, MAXERRORLENGTH, L"Error details: ");
297     StringCchCatW(buf, MAXERRORLENGTH, errorText);
298     RTC_LOG(LS_VERBOSE) << buf;
299   } else {
300     MMDeviceIsAvailable = true;
301     RTC_LOG(LS_VERBOSE)
302         << "AudioDeviceWindowsCore::CoreAudioIsSupported()"
303            " CoCreateInstance(MMDeviceEnumerator) succeeded (hr="
304         << hr << ")";
305     SAFE_RELEASE(pIMMD);
306   }
307 
308   // 4) Verify that we can create and initialize our Core Audio class.
309   //
310   if (MMDeviceIsAvailable) {
311     coreAudioIsSupported = false;
312 
313     AudioDeviceWindowsCore* p = new (std::nothrow) AudioDeviceWindowsCore();
314     if (p == NULL) {
315       return false;
316     }
317 
318     int ok(0);
319 
320     if (p->Init() != InitStatus::OK) {
321       ok |= -1;
322     }
323 
324     ok |= p->Terminate();
325 
326     if (ok == 0) {
327       coreAudioIsSupported = true;
328     }
329 
330     delete p;
331   }
332 
333   if (coreAudioIsSupported) {
334     RTC_LOG(LS_VERBOSE) << "*** Windows Core Audio is supported ***";
335   } else {
336     RTC_LOG(LS_VERBOSE) << "*** Windows Core Audio is NOT supported";
337   }
338 
339   return (coreAudioIsSupported);
340 }
341 
342 // ============================================================================
343 //                            Construction & Destruction
344 // ============================================================================
345 
346 // ----------------------------------------------------------------------------
347 //  AudioDeviceWindowsCore() - ctor
348 // ----------------------------------------------------------------------------
349 
AudioDeviceWindowsCore()350 AudioDeviceWindowsCore::AudioDeviceWindowsCore()
351     : _avrtLibrary(nullptr),
352       _winSupportAvrt(false),
353       _comInit(ScopedCOMInitializer::kMTA),
354       _ptrAudioBuffer(nullptr),
355       _ptrEnumerator(nullptr),
356       _ptrRenderCollection(nullptr),
357       _ptrCaptureCollection(nullptr),
358       _ptrDeviceOut(nullptr),
359       _ptrDeviceIn(nullptr),
360       _ptrClientOut(nullptr),
361       _ptrClientIn(nullptr),
362       _ptrRenderClient(nullptr),
363       _ptrCaptureClient(nullptr),
364       _ptrCaptureVolume(nullptr),
365       _ptrRenderSimpleVolume(nullptr),
366       _dmo(nullptr),
367       _mediaBuffer(nullptr),
368       _builtInAecEnabled(false),
369       _hRenderSamplesReadyEvent(nullptr),
370       _hPlayThread(nullptr),
371       _hRenderStartedEvent(nullptr),
372       _hShutdownRenderEvent(nullptr),
373       _hCaptureSamplesReadyEvent(nullptr),
374       _hRecThread(nullptr),
375       _hCaptureStartedEvent(nullptr),
376       _hShutdownCaptureEvent(nullptr),
377       _hMmTask(nullptr),
378       _playAudioFrameSize(0),
379       _playSampleRate(0),
380       _playBlockSize(0),
381       _playChannels(2),
382       _sndCardPlayDelay(0),
383       _writtenSamples(0),
384       _readSamples(0),
385       _recAudioFrameSize(0),
386       _recSampleRate(0),
387       _recBlockSize(0),
388       _recChannels(2),
389       _initialized(false),
390       _recording(false),
391       _playing(false),
392       _recIsInitialized(false),
393       _playIsInitialized(false),
394       _speakerIsInitialized(false),
395       _microphoneIsInitialized(false),
396       _usingInputDeviceIndex(false),
397       _usingOutputDeviceIndex(false),
398       _inputDevice(AudioDeviceModule::kDefaultCommunicationDevice),
399       _outputDevice(AudioDeviceModule::kDefaultCommunicationDevice),
400       _inputDeviceIndex(0),
401       _outputDeviceIndex(0) {
402   RTC_DLOG(LS_INFO) << __FUNCTION__ << " created";
403   RTC_DCHECK(_comInit.Succeeded());
404 
405   // Try to load the Avrt DLL
406   if (!_avrtLibrary) {
407     // Get handle to the Avrt DLL module.
408     _avrtLibrary = LoadLibrary(TEXT("Avrt.dll"));
409     if (_avrtLibrary) {
410       // Handle is valid (should only happen if OS larger than vista & win7).
411       // Try to get the function addresses.
412       RTC_LOG(LS_VERBOSE) << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
413                              " The Avrt DLL module is now loaded";
414 
415       _PAvRevertMmThreadCharacteristics =
416           (PAvRevertMmThreadCharacteristics)GetProcAddress(
417               _avrtLibrary, "AvRevertMmThreadCharacteristics");
418       _PAvSetMmThreadCharacteristicsA =
419           (PAvSetMmThreadCharacteristicsA)GetProcAddress(
420               _avrtLibrary, "AvSetMmThreadCharacteristicsA");
421       _PAvSetMmThreadPriority = (PAvSetMmThreadPriority)GetProcAddress(
422           _avrtLibrary, "AvSetMmThreadPriority");
423 
424       if (_PAvRevertMmThreadCharacteristics &&
425           _PAvSetMmThreadCharacteristicsA && _PAvSetMmThreadPriority) {
426         RTC_LOG(LS_VERBOSE)
427             << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
428                " AvRevertMmThreadCharacteristics() is OK";
429         RTC_LOG(LS_VERBOSE)
430             << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
431                " AvSetMmThreadCharacteristicsA() is OK";
432         RTC_LOG(LS_VERBOSE)
433             << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
434                " AvSetMmThreadPriority() is OK";
435         _winSupportAvrt = true;
436       }
437     }
438   }
439 
440   // Create our samples ready events - we want auto reset events that start in
441   // the not-signaled state. The state of an auto-reset event object remains
442   // signaled until a single waiting thread is released, at which time the
443   // system automatically sets the state to nonsignaled. If no threads are
444   // waiting, the event object's state remains signaled. (Except for
445   // _hShutdownCaptureEvent, which is used to shutdown multiple threads).
446   _hRenderSamplesReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
447   _hCaptureSamplesReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
448   _hShutdownRenderEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
449   _hShutdownCaptureEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
450   _hRenderStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
451   _hCaptureStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
452 
453   _perfCounterFreq.QuadPart = 1;
454   _perfCounterFactor = 0.0;
455 
456   // list of number of channels to use on recording side
457   _recChannelsPrioList[0] = 2;  // stereo is prio 1
458   _recChannelsPrioList[1] = 1;  // mono is prio 2
459   _recChannelsPrioList[2] = 4;  // quad is prio 3
460 
461   // list of number of channels to use on playout side
462   _playChannelsPrioList[0] = 2;  // stereo is prio 1
463   _playChannelsPrioList[1] = 1;  // mono is prio 2
464 
465   HRESULT hr;
466 
467   // We know that this API will work since it has already been verified in
468   // CoreAudioIsSupported, hence no need to check for errors here as well.
469 
470   // Retrive the IMMDeviceEnumerator API (should load the MMDevAPI.dll)
471   // TODO(henrika): we should probably move this allocation to Init() instead
472   // and deallocate in Terminate() to make the implementation more symmetric.
473   CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
474                    __uuidof(IMMDeviceEnumerator),
475                    reinterpret_cast<void**>(&_ptrEnumerator));
476   RTC_DCHECK(_ptrEnumerator);
477 
478   // DMO initialization for built-in WASAPI AEC.
479   {
480     IMediaObject* ptrDMO = NULL;
481     hr = CoCreateInstance(CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC_SERVER,
482                           IID_IMediaObject, reinterpret_cast<void**>(&ptrDMO));
483     if (FAILED(hr) || ptrDMO == NULL) {
484       // Since we check that _dmo is non-NULL in EnableBuiltInAEC(), the
485       // feature is prevented from being enabled.
486       _builtInAecEnabled = false;
487       _TraceCOMError(hr);
488     }
489     _dmo = ptrDMO;
490     SAFE_RELEASE(ptrDMO);
491   }
492 }
493 
494 // ----------------------------------------------------------------------------
495 //  AudioDeviceWindowsCore() - dtor
496 // ----------------------------------------------------------------------------
497 
~AudioDeviceWindowsCore()498 AudioDeviceWindowsCore::~AudioDeviceWindowsCore() {
499   RTC_DLOG(LS_INFO) << __FUNCTION__ << " destroyed";
500 
501   Terminate();
502 
503   // The IMMDeviceEnumerator is created during construction. Must release
504   // it here and not in Terminate() since we don't recreate it in Init().
505   SAFE_RELEASE(_ptrEnumerator);
506 
507   _ptrAudioBuffer = NULL;
508 
509   if (NULL != _hRenderSamplesReadyEvent) {
510     CloseHandle(_hRenderSamplesReadyEvent);
511     _hRenderSamplesReadyEvent = NULL;
512   }
513 
514   if (NULL != _hCaptureSamplesReadyEvent) {
515     CloseHandle(_hCaptureSamplesReadyEvent);
516     _hCaptureSamplesReadyEvent = NULL;
517   }
518 
519   if (NULL != _hRenderStartedEvent) {
520     CloseHandle(_hRenderStartedEvent);
521     _hRenderStartedEvent = NULL;
522   }
523 
524   if (NULL != _hCaptureStartedEvent) {
525     CloseHandle(_hCaptureStartedEvent);
526     _hCaptureStartedEvent = NULL;
527   }
528 
529   if (NULL != _hShutdownRenderEvent) {
530     CloseHandle(_hShutdownRenderEvent);
531     _hShutdownRenderEvent = NULL;
532   }
533 
534   if (NULL != _hShutdownCaptureEvent) {
535     CloseHandle(_hShutdownCaptureEvent);
536     _hShutdownCaptureEvent = NULL;
537   }
538 
539   if (_avrtLibrary) {
540     BOOL freeOK = FreeLibrary(_avrtLibrary);
541     if (!freeOK) {
542       RTC_LOG(LS_WARNING)
543           << "AudioDeviceWindowsCore::~AudioDeviceWindowsCore()"
544              " failed to free the loaded Avrt DLL module correctly";
545     } else {
546       RTC_LOG(LS_WARNING) << "AudioDeviceWindowsCore::~AudioDeviceWindowsCore()"
547                              " the Avrt DLL module is now unloaded";
548     }
549   }
550 }
551 
552 // ============================================================================
553 //                                     API
554 // ============================================================================
555 
556 // ----------------------------------------------------------------------------
557 //  AttachAudioBuffer
558 // ----------------------------------------------------------------------------
559 
AttachAudioBuffer(AudioDeviceBuffer * audioBuffer)560 void AudioDeviceWindowsCore::AttachAudioBuffer(AudioDeviceBuffer* audioBuffer) {
561   _ptrAudioBuffer = audioBuffer;
562 
563   // Inform the AudioBuffer about default settings for this implementation.
564   // Set all values to zero here since the actual settings will be done by
565   // InitPlayout and InitRecording later.
566   _ptrAudioBuffer->SetRecordingSampleRate(0);
567   _ptrAudioBuffer->SetPlayoutSampleRate(0);
568   _ptrAudioBuffer->SetRecordingChannels(0);
569   _ptrAudioBuffer->SetPlayoutChannels(0);
570 }
571 
572 // ----------------------------------------------------------------------------
573 //  ActiveAudioLayer
574 // ----------------------------------------------------------------------------
575 
ActiveAudioLayer(AudioDeviceModule::AudioLayer & audioLayer) const576 int32_t AudioDeviceWindowsCore::ActiveAudioLayer(
577     AudioDeviceModule::AudioLayer& audioLayer) const {
578   audioLayer = AudioDeviceModule::kWindowsCoreAudio;
579   return 0;
580 }
581 
582 // ----------------------------------------------------------------------------
583 //  Init
584 // ----------------------------------------------------------------------------
585 
Init()586 AudioDeviceGeneric::InitStatus AudioDeviceWindowsCore::Init() {
587   MutexLock lock(&mutex_);
588 
589   if (_initialized) {
590     return InitStatus::OK;
591   }
592 
593   // Enumerate all audio rendering and capturing endpoint devices.
594   // Note that, some of these will not be able to select by the user.
595   // The complete collection is for internal use only.
596   _EnumerateEndpointDevicesAll(eRender);
597   _EnumerateEndpointDevicesAll(eCapture);
598 
599   _initialized = true;
600 
601   return InitStatus::OK;
602 }
603 
604 // ----------------------------------------------------------------------------
605 //  Terminate
606 // ----------------------------------------------------------------------------
607 
Terminate()608 int32_t AudioDeviceWindowsCore::Terminate() {
609   MutexLock lock(&mutex_);
610 
611   if (!_initialized) {
612     return 0;
613   }
614 
615   _initialized = false;
616   _speakerIsInitialized = false;
617   _microphoneIsInitialized = false;
618   _playing = false;
619   _recording = false;
620 
621   SAFE_RELEASE(_ptrRenderCollection);
622   SAFE_RELEASE(_ptrCaptureCollection);
623   SAFE_RELEASE(_ptrDeviceOut);
624   SAFE_RELEASE(_ptrDeviceIn);
625   SAFE_RELEASE(_ptrClientOut);
626   SAFE_RELEASE(_ptrClientIn);
627   SAFE_RELEASE(_ptrRenderClient);
628   SAFE_RELEASE(_ptrCaptureClient);
629   SAFE_RELEASE(_ptrCaptureVolume);
630   SAFE_RELEASE(_ptrRenderSimpleVolume);
631 
632   return 0;
633 }
634 
635 // ----------------------------------------------------------------------------
636 //  Initialized
637 // ----------------------------------------------------------------------------
638 
Initialized() const639 bool AudioDeviceWindowsCore::Initialized() const {
640   return (_initialized);
641 }
642 
643 // ----------------------------------------------------------------------------
644 //  InitSpeaker
645 // ----------------------------------------------------------------------------
646 
InitSpeaker()647 int32_t AudioDeviceWindowsCore::InitSpeaker() {
648   MutexLock lock(&mutex_);
649   return InitSpeakerLocked();
650 }
651 
InitSpeakerLocked()652 int32_t AudioDeviceWindowsCore::InitSpeakerLocked() {
653   if (_playing) {
654     return -1;
655   }
656 
657   if (_ptrDeviceOut == NULL) {
658     return -1;
659   }
660 
661   if (_usingOutputDeviceIndex) {
662     int16_t nDevices = PlayoutDevicesLocked();
663     if (_outputDeviceIndex > (nDevices - 1)) {
664       RTC_LOG(LS_ERROR) << "current device selection is invalid => unable to"
665                            " initialize";
666       return -1;
667     }
668   }
669 
670   int32_t ret(0);
671 
672   SAFE_RELEASE(_ptrDeviceOut);
673   if (_usingOutputDeviceIndex) {
674     // Refresh the selected rendering endpoint device using current index
675     ret = _GetListDevice(eRender, _outputDeviceIndex, &_ptrDeviceOut);
676   } else {
677     ERole role;
678     (_outputDevice == AudioDeviceModule::kDefaultDevice)
679         ? role = eConsole
680         : role = eCommunications;
681     // Refresh the selected rendering endpoint device using role
682     ret = _GetDefaultDevice(eRender, role, &_ptrDeviceOut);
683   }
684 
685   if (ret != 0 || (_ptrDeviceOut == NULL)) {
686     RTC_LOG(LS_ERROR) << "failed to initialize the rendering enpoint device";
687     SAFE_RELEASE(_ptrDeviceOut);
688     return -1;
689   }
690 
691   IAudioSessionManager* pManager = NULL;
692   ret = _ptrDeviceOut->Activate(__uuidof(IAudioSessionManager), CLSCTX_ALL,
693                                 NULL, (void**)&pManager);
694   if (ret != 0 || pManager == NULL) {
695     RTC_LOG(LS_ERROR) << "failed to initialize the render manager";
696     SAFE_RELEASE(pManager);
697     return -1;
698   }
699 
700   SAFE_RELEASE(_ptrRenderSimpleVolume);
701   ret = pManager->GetSimpleAudioVolume(NULL, FALSE, &_ptrRenderSimpleVolume);
702   if (ret != 0 || _ptrRenderSimpleVolume == NULL) {
703     RTC_LOG(LS_ERROR) << "failed to initialize the render simple volume";
704     SAFE_RELEASE(pManager);
705     SAFE_RELEASE(_ptrRenderSimpleVolume);
706     return -1;
707   }
708   SAFE_RELEASE(pManager);
709 
710   _speakerIsInitialized = true;
711 
712   return 0;
713 }
714 
715 // ----------------------------------------------------------------------------
716 //  InitMicrophone
717 // ----------------------------------------------------------------------------
718 
InitMicrophone()719 int32_t AudioDeviceWindowsCore::InitMicrophone() {
720   MutexLock lock(&mutex_);
721   return InitMicrophoneLocked();
722 }
723 
InitMicrophoneLocked()724 int32_t AudioDeviceWindowsCore::InitMicrophoneLocked() {
725   if (_recording) {
726     return -1;
727   }
728 
729   if (_ptrDeviceIn == NULL) {
730     return -1;
731   }
732 
733   if (_usingInputDeviceIndex) {
734     int16_t nDevices = RecordingDevicesLocked();
735     if (_inputDeviceIndex > (nDevices - 1)) {
736       RTC_LOG(LS_ERROR) << "current device selection is invalid => unable to"
737                            " initialize";
738       return -1;
739     }
740   }
741 
742   int32_t ret(0);
743 
744   SAFE_RELEASE(_ptrDeviceIn);
745   if (_usingInputDeviceIndex) {
746     // Refresh the selected capture endpoint device using current index
747     ret = _GetListDevice(eCapture, _inputDeviceIndex, &_ptrDeviceIn);
748   } else {
749     ERole role;
750     (_inputDevice == AudioDeviceModule::kDefaultDevice)
751         ? role = eConsole
752         : role = eCommunications;
753     // Refresh the selected capture endpoint device using role
754     ret = _GetDefaultDevice(eCapture, role, &_ptrDeviceIn);
755   }
756 
757   if (ret != 0 || (_ptrDeviceIn == NULL)) {
758     RTC_LOG(LS_ERROR) << "failed to initialize the capturing enpoint device";
759     SAFE_RELEASE(_ptrDeviceIn);
760     return -1;
761   }
762 
763   SAFE_RELEASE(_ptrCaptureVolume);
764   ret = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
765                                reinterpret_cast<void**>(&_ptrCaptureVolume));
766   if (ret != 0 || _ptrCaptureVolume == NULL) {
767     RTC_LOG(LS_ERROR) << "failed to initialize the capture volume";
768     SAFE_RELEASE(_ptrCaptureVolume);
769     return -1;
770   }
771 
772   _microphoneIsInitialized = true;
773 
774   return 0;
775 }
776 
777 // ----------------------------------------------------------------------------
778 //  SpeakerIsInitialized
779 // ----------------------------------------------------------------------------
780 
SpeakerIsInitialized() const781 bool AudioDeviceWindowsCore::SpeakerIsInitialized() const {
782   return (_speakerIsInitialized);
783 }
784 
785 // ----------------------------------------------------------------------------
786 //  MicrophoneIsInitialized
787 // ----------------------------------------------------------------------------
788 
MicrophoneIsInitialized() const789 bool AudioDeviceWindowsCore::MicrophoneIsInitialized() const {
790   return (_microphoneIsInitialized);
791 }
792 
793 // ----------------------------------------------------------------------------
794 //  SpeakerVolumeIsAvailable
795 // ----------------------------------------------------------------------------
796 
SpeakerVolumeIsAvailable(bool & available)797 int32_t AudioDeviceWindowsCore::SpeakerVolumeIsAvailable(bool& available) {
798   MutexLock lock(&mutex_);
799 
800   if (_ptrDeviceOut == NULL) {
801     return -1;
802   }
803 
804   HRESULT hr = S_OK;
805   IAudioSessionManager* pManager = NULL;
806   ISimpleAudioVolume* pVolume = NULL;
807 
808   hr = _ptrDeviceOut->Activate(__uuidof(IAudioSessionManager), CLSCTX_ALL, NULL,
809                                (void**)&pManager);
810   EXIT_ON_ERROR(hr);
811 
812   hr = pManager->GetSimpleAudioVolume(NULL, FALSE, &pVolume);
813   EXIT_ON_ERROR(hr);
814 
815   float volume(0.0f);
816   hr = pVolume->GetMasterVolume(&volume);
817   if (FAILED(hr)) {
818     available = false;
819   }
820   available = true;
821 
822   SAFE_RELEASE(pManager);
823   SAFE_RELEASE(pVolume);
824 
825   return 0;
826 
827 Exit:
828   _TraceCOMError(hr);
829   SAFE_RELEASE(pManager);
830   SAFE_RELEASE(pVolume);
831   return -1;
832 }
833 
834 // ----------------------------------------------------------------------------
835 //  SetSpeakerVolume
836 // ----------------------------------------------------------------------------
837 
SetSpeakerVolume(uint32_t volume)838 int32_t AudioDeviceWindowsCore::SetSpeakerVolume(uint32_t volume) {
839   {
840     MutexLock lock(&mutex_);
841 
842     if (!_speakerIsInitialized) {
843       return -1;
844     }
845 
846     if (_ptrDeviceOut == NULL) {
847       return -1;
848     }
849   }
850 
851   if (volume < (uint32_t)MIN_CORE_SPEAKER_VOLUME ||
852       volume > (uint32_t)MAX_CORE_SPEAKER_VOLUME) {
853     return -1;
854   }
855 
856   HRESULT hr = S_OK;
857 
858   // scale input volume to valid range (0.0 to 1.0)
859   const float fLevel = (float)volume / MAX_CORE_SPEAKER_VOLUME;
860   volume_mutex_.Lock();
861   hr = _ptrRenderSimpleVolume->SetMasterVolume(fLevel, NULL);
862   volume_mutex_.Unlock();
863   EXIT_ON_ERROR(hr);
864 
865   return 0;
866 
867 Exit:
868   _TraceCOMError(hr);
869   return -1;
870 }
871 
872 // ----------------------------------------------------------------------------
873 //  SpeakerVolume
874 // ----------------------------------------------------------------------------
875 
SpeakerVolume(uint32_t & volume) const876 int32_t AudioDeviceWindowsCore::SpeakerVolume(uint32_t& volume) const {
877   {
878     MutexLock lock(&mutex_);
879 
880     if (!_speakerIsInitialized) {
881       return -1;
882     }
883 
884     if (_ptrDeviceOut == NULL) {
885       return -1;
886     }
887   }
888 
889   HRESULT hr = S_OK;
890   float fLevel(0.0f);
891 
892   volume_mutex_.Lock();
893   hr = _ptrRenderSimpleVolume->GetMasterVolume(&fLevel);
894   volume_mutex_.Unlock();
895   EXIT_ON_ERROR(hr);
896 
897   // scale input volume range [0.0,1.0] to valid output range
898   volume = static_cast<uint32_t>(fLevel * MAX_CORE_SPEAKER_VOLUME);
899 
900   return 0;
901 
902 Exit:
903   _TraceCOMError(hr);
904   return -1;
905 }
906 
907 // ----------------------------------------------------------------------------
908 //  MaxSpeakerVolume
909 //
910 //  The internal range for Core Audio is 0.0 to 1.0, where 0.0 indicates
911 //  silence and 1.0 indicates full volume (no attenuation).
912 //  We add our (webrtc-internal) own max level to match the Wave API and
913 //  how it is used today in VoE.
914 // ----------------------------------------------------------------------------
915 
MaxSpeakerVolume(uint32_t & maxVolume) const916 int32_t AudioDeviceWindowsCore::MaxSpeakerVolume(uint32_t& maxVolume) const {
917   if (!_speakerIsInitialized) {
918     return -1;
919   }
920 
921   maxVolume = static_cast<uint32_t>(MAX_CORE_SPEAKER_VOLUME);
922 
923   return 0;
924 }
925 
926 // ----------------------------------------------------------------------------
927 //  MinSpeakerVolume
928 // ----------------------------------------------------------------------------
929 
MinSpeakerVolume(uint32_t & minVolume) const930 int32_t AudioDeviceWindowsCore::MinSpeakerVolume(uint32_t& minVolume) const {
931   if (!_speakerIsInitialized) {
932     return -1;
933   }
934 
935   minVolume = static_cast<uint32_t>(MIN_CORE_SPEAKER_VOLUME);
936 
937   return 0;
938 }
939 
940 // ----------------------------------------------------------------------------
941 //  SpeakerMuteIsAvailable
942 // ----------------------------------------------------------------------------
943 
SpeakerMuteIsAvailable(bool & available)944 int32_t AudioDeviceWindowsCore::SpeakerMuteIsAvailable(bool& available) {
945   MutexLock lock(&mutex_);
946 
947   if (_ptrDeviceOut == NULL) {
948     return -1;
949   }
950 
951   HRESULT hr = S_OK;
952   IAudioEndpointVolume* pVolume = NULL;
953 
954   // Query the speaker system mute state.
955   hr = _ptrDeviceOut->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
956                                reinterpret_cast<void**>(&pVolume));
957   EXIT_ON_ERROR(hr);
958 
959   BOOL mute;
960   hr = pVolume->GetMute(&mute);
961   if (FAILED(hr))
962     available = false;
963   else
964     available = true;
965 
966   SAFE_RELEASE(pVolume);
967 
968   return 0;
969 
970 Exit:
971   _TraceCOMError(hr);
972   SAFE_RELEASE(pVolume);
973   return -1;
974 }
975 
976 // ----------------------------------------------------------------------------
977 //  SetSpeakerMute
978 // ----------------------------------------------------------------------------
979 
SetSpeakerMute(bool enable)980 int32_t AudioDeviceWindowsCore::SetSpeakerMute(bool enable) {
981   MutexLock lock(&mutex_);
982 
983   if (!_speakerIsInitialized) {
984     return -1;
985   }
986 
987   if (_ptrDeviceOut == NULL) {
988     return -1;
989   }
990 
991   HRESULT hr = S_OK;
992   IAudioEndpointVolume* pVolume = NULL;
993 
994   // Set the speaker system mute state.
995   hr = _ptrDeviceOut->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
996                                reinterpret_cast<void**>(&pVolume));
997   EXIT_ON_ERROR(hr);
998 
999   const BOOL mute(enable);
1000   hr = pVolume->SetMute(mute, NULL);
1001   EXIT_ON_ERROR(hr);
1002 
1003   SAFE_RELEASE(pVolume);
1004 
1005   return 0;
1006 
1007 Exit:
1008   _TraceCOMError(hr);
1009   SAFE_RELEASE(pVolume);
1010   return -1;
1011 }
1012 
1013 // ----------------------------------------------------------------------------
1014 //  SpeakerMute
1015 // ----------------------------------------------------------------------------
1016 
SpeakerMute(bool & enabled) const1017 int32_t AudioDeviceWindowsCore::SpeakerMute(bool& enabled) const {
1018   if (!_speakerIsInitialized) {
1019     return -1;
1020   }
1021 
1022   if (_ptrDeviceOut == NULL) {
1023     return -1;
1024   }
1025 
1026   HRESULT hr = S_OK;
1027   IAudioEndpointVolume* pVolume = NULL;
1028 
1029   // Query the speaker system mute state.
1030   hr = _ptrDeviceOut->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
1031                                reinterpret_cast<void**>(&pVolume));
1032   EXIT_ON_ERROR(hr);
1033 
1034   BOOL mute;
1035   hr = pVolume->GetMute(&mute);
1036   EXIT_ON_ERROR(hr);
1037 
1038   enabled = (mute == TRUE) ? true : false;
1039 
1040   SAFE_RELEASE(pVolume);
1041 
1042   return 0;
1043 
1044 Exit:
1045   _TraceCOMError(hr);
1046   SAFE_RELEASE(pVolume);
1047   return -1;
1048 }
1049 
1050 // ----------------------------------------------------------------------------
1051 //  MicrophoneMuteIsAvailable
1052 // ----------------------------------------------------------------------------
1053 
MicrophoneMuteIsAvailable(bool & available)1054 int32_t AudioDeviceWindowsCore::MicrophoneMuteIsAvailable(bool& available) {
1055   MutexLock lock(&mutex_);
1056 
1057   if (_ptrDeviceIn == NULL) {
1058     return -1;
1059   }
1060 
1061   HRESULT hr = S_OK;
1062   IAudioEndpointVolume* pVolume = NULL;
1063 
1064   // Query the microphone system mute state.
1065   hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
1066                               reinterpret_cast<void**>(&pVolume));
1067   EXIT_ON_ERROR(hr);
1068 
1069   BOOL mute;
1070   hr = pVolume->GetMute(&mute);
1071   if (FAILED(hr))
1072     available = false;
1073   else
1074     available = true;
1075 
1076   SAFE_RELEASE(pVolume);
1077   return 0;
1078 
1079 Exit:
1080   _TraceCOMError(hr);
1081   SAFE_RELEASE(pVolume);
1082   return -1;
1083 }
1084 
1085 // ----------------------------------------------------------------------------
1086 //  SetMicrophoneMute
1087 // ----------------------------------------------------------------------------
1088 
SetMicrophoneMute(bool enable)1089 int32_t AudioDeviceWindowsCore::SetMicrophoneMute(bool enable) {
1090   if (!_microphoneIsInitialized) {
1091     return -1;
1092   }
1093 
1094   if (_ptrDeviceIn == NULL) {
1095     return -1;
1096   }
1097 
1098   HRESULT hr = S_OK;
1099   IAudioEndpointVolume* pVolume = NULL;
1100 
1101   // Set the microphone system mute state.
1102   hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
1103                               reinterpret_cast<void**>(&pVolume));
1104   EXIT_ON_ERROR(hr);
1105 
1106   const BOOL mute(enable);
1107   hr = pVolume->SetMute(mute, NULL);
1108   EXIT_ON_ERROR(hr);
1109 
1110   SAFE_RELEASE(pVolume);
1111   return 0;
1112 
1113 Exit:
1114   _TraceCOMError(hr);
1115   SAFE_RELEASE(pVolume);
1116   return -1;
1117 }
1118 
1119 // ----------------------------------------------------------------------------
1120 //  MicrophoneMute
1121 // ----------------------------------------------------------------------------
1122 
MicrophoneMute(bool & enabled) const1123 int32_t AudioDeviceWindowsCore::MicrophoneMute(bool& enabled) const {
1124   if (!_microphoneIsInitialized) {
1125     return -1;
1126   }
1127 
1128   HRESULT hr = S_OK;
1129   IAudioEndpointVolume* pVolume = NULL;
1130 
1131   // Query the microphone system mute state.
1132   hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
1133                               reinterpret_cast<void**>(&pVolume));
1134   EXIT_ON_ERROR(hr);
1135 
1136   BOOL mute;
1137   hr = pVolume->GetMute(&mute);
1138   EXIT_ON_ERROR(hr);
1139 
1140   enabled = (mute == TRUE) ? true : false;
1141 
1142   SAFE_RELEASE(pVolume);
1143   return 0;
1144 
1145 Exit:
1146   _TraceCOMError(hr);
1147   SAFE_RELEASE(pVolume);
1148   return -1;
1149 }
1150 
1151 // ----------------------------------------------------------------------------
1152 //  StereoRecordingIsAvailable
1153 // ----------------------------------------------------------------------------
1154 
StereoRecordingIsAvailable(bool & available)1155 int32_t AudioDeviceWindowsCore::StereoRecordingIsAvailable(bool& available) {
1156   available = true;
1157   return 0;
1158 }
1159 
1160 // ----------------------------------------------------------------------------
1161 //  SetStereoRecording
1162 // ----------------------------------------------------------------------------
1163 
SetStereoRecording(bool enable)1164 int32_t AudioDeviceWindowsCore::SetStereoRecording(bool enable) {
1165   MutexLock lock(&mutex_);
1166 
1167   if (enable) {
1168     _recChannelsPrioList[0] = 2;  // try stereo first
1169     _recChannelsPrioList[1] = 1;
1170     _recChannels = 2;
1171   } else {
1172     _recChannelsPrioList[0] = 1;  // try mono first
1173     _recChannelsPrioList[1] = 2;
1174     _recChannels = 1;
1175   }
1176 
1177   return 0;
1178 }
1179 
1180 // ----------------------------------------------------------------------------
1181 //  StereoRecording
1182 // ----------------------------------------------------------------------------
1183 
StereoRecording(bool & enabled) const1184 int32_t AudioDeviceWindowsCore::StereoRecording(bool& enabled) const {
1185   if (_recChannels == 2)
1186     enabled = true;
1187   else
1188     enabled = false;
1189 
1190   return 0;
1191 }
1192 
1193 // ----------------------------------------------------------------------------
1194 //  StereoPlayoutIsAvailable
1195 // ----------------------------------------------------------------------------
1196 
StereoPlayoutIsAvailable(bool & available)1197 int32_t AudioDeviceWindowsCore::StereoPlayoutIsAvailable(bool& available) {
1198   available = true;
1199   return 0;
1200 }
1201 
1202 // ----------------------------------------------------------------------------
1203 //  SetStereoPlayout
1204 // ----------------------------------------------------------------------------
1205 
SetStereoPlayout(bool enable)1206 int32_t AudioDeviceWindowsCore::SetStereoPlayout(bool enable) {
1207   MutexLock lock(&mutex_);
1208 
1209   if (enable) {
1210     _playChannelsPrioList[0] = 2;  // try stereo first
1211     _playChannelsPrioList[1] = 1;
1212     _playChannels = 2;
1213   } else {
1214     _playChannelsPrioList[0] = 1;  // try mono first
1215     _playChannelsPrioList[1] = 2;
1216     _playChannels = 1;
1217   }
1218 
1219   return 0;
1220 }
1221 
1222 // ----------------------------------------------------------------------------
1223 //  StereoPlayout
1224 // ----------------------------------------------------------------------------
1225 
StereoPlayout(bool & enabled) const1226 int32_t AudioDeviceWindowsCore::StereoPlayout(bool& enabled) const {
1227   if (_playChannels == 2)
1228     enabled = true;
1229   else
1230     enabled = false;
1231 
1232   return 0;
1233 }
1234 
1235 // ----------------------------------------------------------------------------
1236 //  MicrophoneVolumeIsAvailable
1237 // ----------------------------------------------------------------------------
1238 
MicrophoneVolumeIsAvailable(bool & available)1239 int32_t AudioDeviceWindowsCore::MicrophoneVolumeIsAvailable(bool& available) {
1240   MutexLock lock(&mutex_);
1241 
1242   if (_ptrDeviceIn == NULL) {
1243     return -1;
1244   }
1245 
1246   HRESULT hr = S_OK;
1247   IAudioEndpointVolume* pVolume = NULL;
1248 
1249   hr = _ptrDeviceIn->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
1250                               reinterpret_cast<void**>(&pVolume));
1251   EXIT_ON_ERROR(hr);
1252 
1253   float volume(0.0f);
1254   hr = pVolume->GetMasterVolumeLevelScalar(&volume);
1255   if (FAILED(hr)) {
1256     available = false;
1257   }
1258   available = true;
1259 
1260   SAFE_RELEASE(pVolume);
1261   return 0;
1262 
1263 Exit:
1264   _TraceCOMError(hr);
1265   SAFE_RELEASE(pVolume);
1266   return -1;
1267 }
1268 
1269 // ----------------------------------------------------------------------------
1270 //  SetMicrophoneVolume
1271 // ----------------------------------------------------------------------------
1272 
SetMicrophoneVolume(uint32_t volume)1273 int32_t AudioDeviceWindowsCore::SetMicrophoneVolume(uint32_t volume) {
1274   RTC_LOG(LS_VERBOSE) << "AudioDeviceWindowsCore::SetMicrophoneVolume(volume="
1275                       << volume << ")";
1276 
1277   {
1278     MutexLock lock(&mutex_);
1279 
1280     if (!_microphoneIsInitialized) {
1281       return -1;
1282     }
1283 
1284     if (_ptrDeviceIn == NULL) {
1285       return -1;
1286     }
1287   }
1288 
1289   if (volume < static_cast<uint32_t>(MIN_CORE_MICROPHONE_VOLUME) ||
1290       volume > static_cast<uint32_t>(MAX_CORE_MICROPHONE_VOLUME)) {
1291     return -1;
1292   }
1293 
1294   HRESULT hr = S_OK;
1295   // scale input volume to valid range (0.0 to 1.0)
1296   const float fLevel = static_cast<float>(volume) / MAX_CORE_MICROPHONE_VOLUME;
1297   volume_mutex_.Lock();
1298   _ptrCaptureVolume->SetMasterVolumeLevelScalar(fLevel, NULL);
1299   volume_mutex_.Unlock();
1300   EXIT_ON_ERROR(hr);
1301 
1302   return 0;
1303 
1304 Exit:
1305   _TraceCOMError(hr);
1306   return -1;
1307 }
1308 
1309 // ----------------------------------------------------------------------------
1310 //  MicrophoneVolume
1311 // ----------------------------------------------------------------------------
1312 
MicrophoneVolume(uint32_t & volume) const1313 int32_t AudioDeviceWindowsCore::MicrophoneVolume(uint32_t& volume) const {
1314   {
1315     MutexLock lock(&mutex_);
1316 
1317     if (!_microphoneIsInitialized) {
1318       return -1;
1319     }
1320 
1321     if (_ptrDeviceIn == NULL) {
1322       return -1;
1323     }
1324   }
1325 
1326   HRESULT hr = S_OK;
1327   float fLevel(0.0f);
1328   volume = 0;
1329   volume_mutex_.Lock();
1330   hr = _ptrCaptureVolume->GetMasterVolumeLevelScalar(&fLevel);
1331   volume_mutex_.Unlock();
1332   EXIT_ON_ERROR(hr);
1333 
1334   // scale input volume range [0.0,1.0] to valid output range
1335   volume = static_cast<uint32_t>(fLevel * MAX_CORE_MICROPHONE_VOLUME);
1336 
1337   return 0;
1338 
1339 Exit:
1340   _TraceCOMError(hr);
1341   return -1;
1342 }
1343 
1344 // ----------------------------------------------------------------------------
1345 //  MaxMicrophoneVolume
1346 //
1347 //  The internal range for Core Audio is 0.0 to 1.0, where 0.0 indicates
1348 //  silence and 1.0 indicates full volume (no attenuation).
1349 //  We add our (webrtc-internal) own max level to match the Wave API and
1350 //  how it is used today in VoE.
1351 // ----------------------------------------------------------------------------
1352 
MaxMicrophoneVolume(uint32_t & maxVolume) const1353 int32_t AudioDeviceWindowsCore::MaxMicrophoneVolume(uint32_t& maxVolume) const {
1354   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
1355 
1356   if (!_microphoneIsInitialized) {
1357     return -1;
1358   }
1359 
1360   maxVolume = static_cast<uint32_t>(MAX_CORE_MICROPHONE_VOLUME);
1361 
1362   return 0;
1363 }
1364 
1365 // ----------------------------------------------------------------------------
1366 //  MinMicrophoneVolume
1367 // ----------------------------------------------------------------------------
1368 
MinMicrophoneVolume(uint32_t & minVolume) const1369 int32_t AudioDeviceWindowsCore::MinMicrophoneVolume(uint32_t& minVolume) const {
1370   if (!_microphoneIsInitialized) {
1371     return -1;
1372   }
1373 
1374   minVolume = static_cast<uint32_t>(MIN_CORE_MICROPHONE_VOLUME);
1375 
1376   return 0;
1377 }
1378 
1379 // ----------------------------------------------------------------------------
1380 //  PlayoutDevices
1381 // ----------------------------------------------------------------------------
PlayoutDevices()1382 int16_t AudioDeviceWindowsCore::PlayoutDevices() {
1383   MutexLock lock(&mutex_);
1384   return PlayoutDevicesLocked();
1385 }
1386 
PlayoutDevicesLocked()1387 int16_t AudioDeviceWindowsCore::PlayoutDevicesLocked() {
1388   if (_RefreshDeviceList(eRender) != -1) {
1389     return (_DeviceListCount(eRender));
1390   }
1391 
1392   return -1;
1393 }
1394 
1395 // ----------------------------------------------------------------------------
1396 //  SetPlayoutDevice I (II)
1397 // ----------------------------------------------------------------------------
1398 
SetPlayoutDevice(uint16_t index)1399 int32_t AudioDeviceWindowsCore::SetPlayoutDevice(uint16_t index) {
1400   if (_playIsInitialized) {
1401     return -1;
1402   }
1403 
1404   // Get current number of available rendering endpoint devices and refresh the
1405   // rendering collection.
1406   UINT nDevices = PlayoutDevices();
1407 
1408   if (index < 0 || index > (nDevices - 1)) {
1409     RTC_LOG(LS_ERROR) << "device index is out of range [0," << (nDevices - 1)
1410                       << "]";
1411     return -1;
1412   }
1413 
1414   MutexLock lock(&mutex_);
1415 
1416   HRESULT hr(S_OK);
1417 
1418   RTC_DCHECK(_ptrRenderCollection);
1419 
1420   //  Select an endpoint rendering device given the specified index
1421   SAFE_RELEASE(_ptrDeviceOut);
1422   hr = _ptrRenderCollection->Item(index, &_ptrDeviceOut);
1423   if (FAILED(hr)) {
1424     _TraceCOMError(hr);
1425     SAFE_RELEASE(_ptrDeviceOut);
1426     return -1;
1427   }
1428 
1429   WCHAR szDeviceName[MAX_PATH];
1430   const int bufferLen = sizeof(szDeviceName) / sizeof(szDeviceName)[0];
1431 
1432   // Get the endpoint device's friendly-name
1433   if (_GetDeviceName(_ptrDeviceOut, szDeviceName, bufferLen) == 0) {
1434     RTC_LOG(LS_VERBOSE) << "friendly name: \"" << szDeviceName << "\"";
1435   }
1436 
1437   _usingOutputDeviceIndex = true;
1438   _outputDeviceIndex = index;
1439 
1440   return 0;
1441 }
1442 
1443 // ----------------------------------------------------------------------------
1444 //  SetPlayoutDevice II (II)
1445 // ----------------------------------------------------------------------------
1446 
SetPlayoutDevice(AudioDeviceModule::WindowsDeviceType device)1447 int32_t AudioDeviceWindowsCore::SetPlayoutDevice(
1448     AudioDeviceModule::WindowsDeviceType device) {
1449   if (_playIsInitialized) {
1450     return -1;
1451   }
1452 
1453   ERole role(eCommunications);
1454 
1455   if (device == AudioDeviceModule::kDefaultDevice) {
1456     role = eConsole;
1457   } else if (device == AudioDeviceModule::kDefaultCommunicationDevice) {
1458     role = eCommunications;
1459   }
1460 
1461   MutexLock lock(&mutex_);
1462 
1463   // Refresh the list of rendering endpoint devices
1464   _RefreshDeviceList(eRender);
1465 
1466   HRESULT hr(S_OK);
1467 
1468   RTC_DCHECK(_ptrEnumerator);
1469 
1470   //  Select an endpoint rendering device given the specified role
1471   SAFE_RELEASE(_ptrDeviceOut);
1472   hr = _ptrEnumerator->GetDefaultAudioEndpoint(eRender, role, &_ptrDeviceOut);
1473   if (FAILED(hr)) {
1474     _TraceCOMError(hr);
1475     SAFE_RELEASE(_ptrDeviceOut);
1476     return -1;
1477   }
1478 
1479   WCHAR szDeviceName[MAX_PATH];
1480   const int bufferLen = sizeof(szDeviceName) / sizeof(szDeviceName)[0];
1481 
1482   // Get the endpoint device's friendly-name
1483   if (_GetDeviceName(_ptrDeviceOut, szDeviceName, bufferLen) == 0) {
1484     RTC_LOG(LS_VERBOSE) << "friendly name: \"" << szDeviceName << "\"";
1485   }
1486 
1487   _usingOutputDeviceIndex = false;
1488   _outputDevice = device;
1489 
1490   return 0;
1491 }
1492 
1493 // ----------------------------------------------------------------------------
1494 //  PlayoutDeviceName
1495 // ----------------------------------------------------------------------------
1496 
PlayoutDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])1497 int32_t AudioDeviceWindowsCore::PlayoutDeviceName(
1498     uint16_t index,
1499     char name[kAdmMaxDeviceNameSize],
1500     char guid[kAdmMaxGuidSize]) {
1501   bool defaultCommunicationDevice(false);
1502   const int16_t nDevices(PlayoutDevices());  // also updates the list of devices
1503 
1504   // Special fix for the case when the user selects '-1' as index (<=> Default
1505   // Communication Device)
1506   if (index == (uint16_t)(-1)) {
1507     defaultCommunicationDevice = true;
1508     index = 0;
1509     RTC_LOG(LS_VERBOSE) << "Default Communication endpoint device will be used";
1510   }
1511 
1512   if ((index > (nDevices - 1)) || (name == NULL)) {
1513     return -1;
1514   }
1515 
1516   memset(name, 0, kAdmMaxDeviceNameSize);
1517 
1518   if (guid != NULL) {
1519     memset(guid, 0, kAdmMaxGuidSize);
1520   }
1521 
1522   MutexLock lock(&mutex_);
1523 
1524   int32_t ret(-1);
1525   WCHAR szDeviceName[MAX_PATH];
1526   const int bufferLen = sizeof(szDeviceName) / sizeof(szDeviceName)[0];
1527 
1528   // Get the endpoint device's friendly-name
1529   if (defaultCommunicationDevice) {
1530     ret = _GetDefaultDeviceName(eRender, eCommunications, szDeviceName,
1531                                 bufferLen);
1532   } else {
1533     ret = _GetListDeviceName(eRender, index, szDeviceName, bufferLen);
1534   }
1535 
1536   if (ret == 0) {
1537     // Convert the endpoint device's friendly-name to UTF-8
1538     if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, name,
1539                             kAdmMaxDeviceNameSize, NULL, NULL) == 0) {
1540       RTC_LOG(LS_ERROR)
1541           << "WideCharToMultiByte(CP_UTF8) failed with error code "
1542           << GetLastError();
1543     }
1544   }
1545 
1546   // Get the endpoint ID string (uniquely identifies the device among all audio
1547   // endpoint devices)
1548   if (defaultCommunicationDevice) {
1549     ret =
1550         _GetDefaultDeviceID(eRender, eCommunications, szDeviceName, bufferLen);
1551   } else {
1552     ret = _GetListDeviceID(eRender, index, szDeviceName, bufferLen);
1553   }
1554 
1555   if (guid != NULL && ret == 0) {
1556     // Convert the endpoint device's ID string to UTF-8
1557     if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, guid, kAdmMaxGuidSize,
1558                             NULL, NULL) == 0) {
1559       RTC_LOG(LS_ERROR)
1560           << "WideCharToMultiByte(CP_UTF8) failed with error code "
1561           << GetLastError();
1562     }
1563   }
1564 
1565   return ret;
1566 }
1567 
1568 // ----------------------------------------------------------------------------
1569 //  RecordingDeviceName
1570 // ----------------------------------------------------------------------------
1571 
RecordingDeviceName(uint16_t index,char name[kAdmMaxDeviceNameSize],char guid[kAdmMaxGuidSize])1572 int32_t AudioDeviceWindowsCore::RecordingDeviceName(
1573     uint16_t index,
1574     char name[kAdmMaxDeviceNameSize],
1575     char guid[kAdmMaxGuidSize]) {
1576   bool defaultCommunicationDevice(false);
1577   const int16_t nDevices(
1578       RecordingDevices());  // also updates the list of devices
1579 
1580   // Special fix for the case when the user selects '-1' as index (<=> Default
1581   // Communication Device)
1582   if (index == (uint16_t)(-1)) {
1583     defaultCommunicationDevice = true;
1584     index = 0;
1585     RTC_LOG(LS_VERBOSE) << "Default Communication endpoint device will be used";
1586   }
1587 
1588   if ((index > (nDevices - 1)) || (name == NULL)) {
1589     return -1;
1590   }
1591 
1592   memset(name, 0, kAdmMaxDeviceNameSize);
1593 
1594   if (guid != NULL) {
1595     memset(guid, 0, kAdmMaxGuidSize);
1596   }
1597 
1598   MutexLock lock(&mutex_);
1599 
1600   int32_t ret(-1);
1601   WCHAR szDeviceName[MAX_PATH];
1602   const int bufferLen = sizeof(szDeviceName) / sizeof(szDeviceName)[0];
1603 
1604   // Get the endpoint device's friendly-name
1605   if (defaultCommunicationDevice) {
1606     ret = _GetDefaultDeviceName(eCapture, eCommunications, szDeviceName,
1607                                 bufferLen);
1608   } else {
1609     ret = _GetListDeviceName(eCapture, index, szDeviceName, bufferLen);
1610   }
1611 
1612   if (ret == 0) {
1613     // Convert the endpoint device's friendly-name to UTF-8
1614     if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, name,
1615                             kAdmMaxDeviceNameSize, NULL, NULL) == 0) {
1616       RTC_LOG(LS_ERROR)
1617           << "WideCharToMultiByte(CP_UTF8) failed with error code "
1618           << GetLastError();
1619     }
1620   }
1621 
1622   // Get the endpoint ID string (uniquely identifies the device among all audio
1623   // endpoint devices)
1624   if (defaultCommunicationDevice) {
1625     ret =
1626         _GetDefaultDeviceID(eCapture, eCommunications, szDeviceName, bufferLen);
1627   } else {
1628     ret = _GetListDeviceID(eCapture, index, szDeviceName, bufferLen);
1629   }
1630 
1631   if (guid != NULL && ret == 0) {
1632     // Convert the endpoint device's ID string to UTF-8
1633     if (WideCharToMultiByte(CP_UTF8, 0, szDeviceName, -1, guid, kAdmMaxGuidSize,
1634                             NULL, NULL) == 0) {
1635       RTC_LOG(LS_ERROR)
1636           << "WideCharToMultiByte(CP_UTF8) failed with error code "
1637           << GetLastError();
1638     }
1639   }
1640 
1641   return ret;
1642 }
1643 
1644 // ----------------------------------------------------------------------------
1645 //  RecordingDevices
1646 // ----------------------------------------------------------------------------
1647 
RecordingDevices()1648 int16_t AudioDeviceWindowsCore::RecordingDevices() {
1649   MutexLock lock(&mutex_);
1650   return RecordingDevicesLocked();
1651 }
1652 
RecordingDevicesLocked()1653 int16_t AudioDeviceWindowsCore::RecordingDevicesLocked() {
1654   if (_RefreshDeviceList(eCapture) != -1) {
1655     return (_DeviceListCount(eCapture));
1656   }
1657 
1658   return -1;
1659 }
1660 
1661 // ----------------------------------------------------------------------------
1662 //  SetRecordingDevice I (II)
1663 // ----------------------------------------------------------------------------
1664 
SetRecordingDevice(uint16_t index)1665 int32_t AudioDeviceWindowsCore::SetRecordingDevice(uint16_t index) {
1666   if (_recIsInitialized) {
1667     return -1;
1668   }
1669 
1670   // Get current number of available capture endpoint devices and refresh the
1671   // capture collection.
1672   UINT nDevices = RecordingDevices();
1673 
1674   if (index < 0 || index > (nDevices - 1)) {
1675     RTC_LOG(LS_ERROR) << "device index is out of range [0," << (nDevices - 1)
1676                       << "]";
1677     return -1;
1678   }
1679 
1680   MutexLock lock(&mutex_);
1681 
1682   HRESULT hr(S_OK);
1683 
1684   RTC_DCHECK(_ptrCaptureCollection);
1685 
1686   // Select an endpoint capture device given the specified index
1687   SAFE_RELEASE(_ptrDeviceIn);
1688   hr = _ptrCaptureCollection->Item(index, &_ptrDeviceIn);
1689   if (FAILED(hr)) {
1690     _TraceCOMError(hr);
1691     SAFE_RELEASE(_ptrDeviceIn);
1692     return -1;
1693   }
1694 
1695   WCHAR szDeviceName[MAX_PATH];
1696   const int bufferLen = sizeof(szDeviceName) / sizeof(szDeviceName)[0];
1697 
1698   // Get the endpoint device's friendly-name
1699   if (_GetDeviceName(_ptrDeviceIn, szDeviceName, bufferLen) == 0) {
1700     RTC_LOG(LS_VERBOSE) << "friendly name: \"" << szDeviceName << "\"";
1701   }
1702 
1703   _usingInputDeviceIndex = true;
1704   _inputDeviceIndex = index;
1705 
1706   return 0;
1707 }
1708 
1709 // ----------------------------------------------------------------------------
1710 //  SetRecordingDevice II (II)
1711 // ----------------------------------------------------------------------------
1712 
SetRecordingDevice(AudioDeviceModule::WindowsDeviceType device)1713 int32_t AudioDeviceWindowsCore::SetRecordingDevice(
1714     AudioDeviceModule::WindowsDeviceType device) {
1715   if (_recIsInitialized) {
1716     return -1;
1717   }
1718 
1719   ERole role(eCommunications);
1720 
1721   if (device == AudioDeviceModule::kDefaultDevice) {
1722     role = eConsole;
1723   } else if (device == AudioDeviceModule::kDefaultCommunicationDevice) {
1724     role = eCommunications;
1725   }
1726 
1727   MutexLock lock(&mutex_);
1728 
1729   // Refresh the list of capture endpoint devices
1730   _RefreshDeviceList(eCapture);
1731 
1732   HRESULT hr(S_OK);
1733 
1734   RTC_DCHECK(_ptrEnumerator);
1735 
1736   //  Select an endpoint capture device given the specified role
1737   SAFE_RELEASE(_ptrDeviceIn);
1738   hr = _ptrEnumerator->GetDefaultAudioEndpoint(eCapture, role, &_ptrDeviceIn);
1739   if (FAILED(hr)) {
1740     _TraceCOMError(hr);
1741     SAFE_RELEASE(_ptrDeviceIn);
1742     return -1;
1743   }
1744 
1745   WCHAR szDeviceName[MAX_PATH];
1746   const int bufferLen = sizeof(szDeviceName) / sizeof(szDeviceName)[0];
1747 
1748   // Get the endpoint device's friendly-name
1749   if (_GetDeviceName(_ptrDeviceIn, szDeviceName, bufferLen) == 0) {
1750     RTC_LOG(LS_VERBOSE) << "friendly name: \"" << szDeviceName << "\"";
1751   }
1752 
1753   _usingInputDeviceIndex = false;
1754   _inputDevice = device;
1755 
1756   return 0;
1757 }
1758 
1759 // ----------------------------------------------------------------------------
1760 //  PlayoutIsAvailable
1761 // ----------------------------------------------------------------------------
1762 
PlayoutIsAvailable(bool & available)1763 int32_t AudioDeviceWindowsCore::PlayoutIsAvailable(bool& available) {
1764   available = false;
1765 
1766   // Try to initialize the playout side
1767   int32_t res = InitPlayout();
1768 
1769   // Cancel effect of initialization
1770   StopPlayout();
1771 
1772   if (res != -1) {
1773     available = true;
1774   }
1775 
1776   return 0;
1777 }
1778 
1779 // ----------------------------------------------------------------------------
1780 //  RecordingIsAvailable
1781 // ----------------------------------------------------------------------------
1782 
RecordingIsAvailable(bool & available)1783 int32_t AudioDeviceWindowsCore::RecordingIsAvailable(bool& available) {
1784   available = false;
1785 
1786   // Try to initialize the recording side
1787   int32_t res = InitRecording();
1788 
1789   // Cancel effect of initialization
1790   StopRecording();
1791 
1792   if (res != -1) {
1793     available = true;
1794   }
1795 
1796   return 0;
1797 }
1798 
1799 // ----------------------------------------------------------------------------
1800 //  InitPlayout
1801 // ----------------------------------------------------------------------------
1802 
InitPlayout()1803 int32_t AudioDeviceWindowsCore::InitPlayout() {
1804   MutexLock lock(&mutex_);
1805 
1806   if (_playing) {
1807     return -1;
1808   }
1809 
1810   if (_playIsInitialized) {
1811     return 0;
1812   }
1813 
1814   if (_ptrDeviceOut == NULL) {
1815     return -1;
1816   }
1817 
1818   // Initialize the speaker (devices might have been added or removed)
1819   if (InitSpeakerLocked() == -1) {
1820     RTC_LOG(LS_WARNING) << "InitSpeaker() failed";
1821   }
1822 
1823   // Ensure that the updated rendering endpoint device is valid
1824   if (_ptrDeviceOut == NULL) {
1825     return -1;
1826   }
1827 
1828   if (_builtInAecEnabled && _recIsInitialized) {
1829     // Ensure the correct render device is configured in case
1830     // InitRecording() was called before InitPlayout().
1831     if (SetDMOProperties() == -1) {
1832       return -1;
1833     }
1834   }
1835 
1836   HRESULT hr = S_OK;
1837   WAVEFORMATEX* pWfxOut = NULL;
1838   WAVEFORMATEX Wfx = WAVEFORMATEX();
1839   WAVEFORMATEX* pWfxClosestMatch = NULL;
1840 
1841   // Create COM object with IAudioClient interface.
1842   SAFE_RELEASE(_ptrClientOut);
1843   hr = _ptrDeviceOut->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL,
1844                                (void**)&_ptrClientOut);
1845   EXIT_ON_ERROR(hr);
1846 
1847   // Retrieve the stream format that the audio engine uses for its internal
1848   // processing (mixing) of shared-mode streams.
1849   hr = _ptrClientOut->GetMixFormat(&pWfxOut);
1850   if (SUCCEEDED(hr)) {
1851     RTC_LOG(LS_VERBOSE) << "Audio Engine's current rendering mix format:";
1852     // format type
1853     RTC_LOG(LS_VERBOSE) << "wFormatTag     : 0x"
1854                         << rtc::ToHex(pWfxOut->wFormatTag) << " ("
1855                         << pWfxOut->wFormatTag << ")";
1856     // number of channels (i.e. mono, stereo...)
1857     RTC_LOG(LS_VERBOSE) << "nChannels      : " << pWfxOut->nChannels;
1858     // sample rate
1859     RTC_LOG(LS_VERBOSE) << "nSamplesPerSec : " << pWfxOut->nSamplesPerSec;
1860     // for buffer estimation
1861     RTC_LOG(LS_VERBOSE) << "nAvgBytesPerSec: " << pWfxOut->nAvgBytesPerSec;
1862     // block size of data
1863     RTC_LOG(LS_VERBOSE) << "nBlockAlign    : " << pWfxOut->nBlockAlign;
1864     // number of bits per sample of mono data
1865     RTC_LOG(LS_VERBOSE) << "wBitsPerSample : " << pWfxOut->wBitsPerSample;
1866     RTC_LOG(LS_VERBOSE) << "cbSize         : " << pWfxOut->cbSize;
1867   }
1868 
1869   // Set wave format
1870   Wfx.wFormatTag = WAVE_FORMAT_PCM;
1871   Wfx.wBitsPerSample = 16;
1872   Wfx.cbSize = 0;
1873 
1874   const int freqs[] = {48000, 44100, 16000, 96000, 32000, 8000};
1875   hr = S_FALSE;
1876 
1877   // Iterate over frequencies and channels, in order of priority
1878   for (unsigned int freq = 0; freq < sizeof(freqs) / sizeof(freqs[0]); freq++) {
1879     for (unsigned int chan = 0; chan < sizeof(_playChannelsPrioList) /
1880                                            sizeof(_playChannelsPrioList[0]);
1881          chan++) {
1882       Wfx.nChannels = _playChannelsPrioList[chan];
1883       Wfx.nSamplesPerSec = freqs[freq];
1884       Wfx.nBlockAlign = Wfx.nChannels * Wfx.wBitsPerSample / 8;
1885       Wfx.nAvgBytesPerSec = Wfx.nSamplesPerSec * Wfx.nBlockAlign;
1886       // If the method succeeds and the audio endpoint device supports the
1887       // specified stream format, it returns S_OK. If the method succeeds and
1888       // provides a closest match to the specified format, it returns S_FALSE.
1889       hr = _ptrClientOut->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, &Wfx,
1890                                             &pWfxClosestMatch);
1891       if (hr == S_OK) {
1892         break;
1893       } else {
1894         if (pWfxClosestMatch) {
1895           RTC_LOG(LS_INFO) << "nChannels=" << Wfx.nChannels
1896                            << ", nSamplesPerSec=" << Wfx.nSamplesPerSec
1897                            << " is not supported. Closest match: "
1898                               "nChannels="
1899                            << pWfxClosestMatch->nChannels << ", nSamplesPerSec="
1900                            << pWfxClosestMatch->nSamplesPerSec;
1901           CoTaskMemFree(pWfxClosestMatch);
1902           pWfxClosestMatch = NULL;
1903         } else {
1904           RTC_LOG(LS_INFO) << "nChannels=" << Wfx.nChannels
1905                            << ", nSamplesPerSec=" << Wfx.nSamplesPerSec
1906                            << " is not supported. No closest match.";
1907         }
1908       }
1909     }
1910     if (hr == S_OK)
1911       break;
1912   }
1913 
1914   // TODO(andrew): what happens in the event of failure in the above loop?
1915   //   Is _ptrClientOut->Initialize expected to fail?
1916   //   Same in InitRecording().
1917   if (hr == S_OK) {
1918     _playAudioFrameSize = Wfx.nBlockAlign;
1919     // Block size is the number of samples each channel in 10ms.
1920     _playBlockSize = Wfx.nSamplesPerSec / 100;
1921     _playSampleRate = Wfx.nSamplesPerSec;
1922     _devicePlaySampleRate =
1923         Wfx.nSamplesPerSec;  // The device itself continues to run at 44.1 kHz.
1924     _devicePlayBlockSize = Wfx.nSamplesPerSec / 100;
1925     _playChannels = Wfx.nChannels;
1926 
1927     RTC_LOG(LS_VERBOSE) << "VoE selected this rendering format:";
1928     RTC_LOG(LS_VERBOSE) << "wFormatTag         : 0x"
1929                         << rtc::ToHex(Wfx.wFormatTag) << " (" << Wfx.wFormatTag
1930                         << ")";
1931     RTC_LOG(LS_VERBOSE) << "nChannels          : " << Wfx.nChannels;
1932     RTC_LOG(LS_VERBOSE) << "nSamplesPerSec     : " << Wfx.nSamplesPerSec;
1933     RTC_LOG(LS_VERBOSE) << "nAvgBytesPerSec    : " << Wfx.nAvgBytesPerSec;
1934     RTC_LOG(LS_VERBOSE) << "nBlockAlign        : " << Wfx.nBlockAlign;
1935     RTC_LOG(LS_VERBOSE) << "wBitsPerSample     : " << Wfx.wBitsPerSample;
1936     RTC_LOG(LS_VERBOSE) << "cbSize             : " << Wfx.cbSize;
1937     RTC_LOG(LS_VERBOSE) << "Additional settings:";
1938     RTC_LOG(LS_VERBOSE) << "_playAudioFrameSize: " << _playAudioFrameSize;
1939     RTC_LOG(LS_VERBOSE) << "_playBlockSize     : " << _playBlockSize;
1940     RTC_LOG(LS_VERBOSE) << "_playChannels      : " << _playChannels;
1941   }
1942 
1943   // Create a rendering stream.
1944   //
1945   // ****************************************************************************
1946   // For a shared-mode stream that uses event-driven buffering, the caller must
1947   // set both hnsPeriodicity and hnsBufferDuration to 0. The Initialize method
1948   // determines how large a buffer to allocate based on the scheduling period
1949   // of the audio engine. Although the client's buffer processing thread is
1950   // event driven, the basic buffer management process, as described previously,
1951   // is unaltered.
1952   // Each time the thread awakens, it should call
1953   // IAudioClient::GetCurrentPadding to determine how much data to write to a
1954   // rendering buffer or read from a capture buffer. In contrast to the two
1955   // buffers that the Initialize method allocates for an exclusive-mode stream
1956   // that uses event-driven buffering, a shared-mode stream requires a single
1957   // buffer.
1958   // ****************************************************************************
1959   //
1960   REFERENCE_TIME hnsBufferDuration =
1961       0;  // ask for minimum buffer size (default)
1962   if (_devicePlaySampleRate == 44100) {
1963     // Ask for a larger buffer size (30ms) when using 44.1kHz as render rate.
1964     // There seems to be a larger risk of underruns for 44.1 compared
1965     // with the default rate (48kHz). When using default, we set the requested
1966     // buffer duration to 0, which sets the buffer to the minimum size
1967     // required by the engine thread. The actual buffer size can then be
1968     // read by GetBufferSize() and it is 20ms on most machines.
1969     hnsBufferDuration = 30 * 10000;
1970   }
1971   hr = _ptrClientOut->Initialize(
1972       AUDCLNT_SHAREMODE_SHARED,  // share Audio Engine with other applications
1973       AUDCLNT_STREAMFLAGS_EVENTCALLBACK,  // processing of the audio buffer by
1974                                           // the client will be event driven
1975       hnsBufferDuration,  // requested buffer capacity as a time value (in
1976                           // 100-nanosecond units)
1977       0,                  // periodicity
1978       &Wfx,               // selected wave format
1979       NULL);              // session GUID
1980 
1981   if (FAILED(hr)) {
1982     RTC_LOG(LS_ERROR) << "IAudioClient::Initialize() failed:";
1983   }
1984   EXIT_ON_ERROR(hr);
1985 
1986   if (_ptrAudioBuffer) {
1987     // Update the audio buffer with the selected parameters
1988     _ptrAudioBuffer->SetPlayoutSampleRate(_playSampleRate);
1989     _ptrAudioBuffer->SetPlayoutChannels((uint8_t)_playChannels);
1990   } else {
1991     // We can enter this state during CoreAudioIsSupported() when no
1992     // AudioDeviceImplementation has been created, hence the AudioDeviceBuffer
1993     // does not exist. It is OK to end up here since we don't initiate any media
1994     // in CoreAudioIsSupported().
1995     RTC_LOG(LS_VERBOSE)
1996         << "AudioDeviceBuffer must be attached before streaming can start";
1997   }
1998 
1999   // Get the actual size of the shared (endpoint buffer).
2000   // Typical value is 960 audio frames <=> 20ms @ 48kHz sample rate.
2001   UINT bufferFrameCount(0);
2002   hr = _ptrClientOut->GetBufferSize(&bufferFrameCount);
2003   if (SUCCEEDED(hr)) {
2004     RTC_LOG(LS_VERBOSE) << "IAudioClient::GetBufferSize() => "
2005                         << bufferFrameCount << " (<=> "
2006                         << bufferFrameCount * _playAudioFrameSize << " bytes)";
2007   }
2008 
2009   // Set the event handle that the system signals when an audio buffer is ready
2010   // to be processed by the client.
2011   hr = _ptrClientOut->SetEventHandle(_hRenderSamplesReadyEvent);
2012   EXIT_ON_ERROR(hr);
2013 
2014   // Get an IAudioRenderClient interface.
2015   SAFE_RELEASE(_ptrRenderClient);
2016   hr = _ptrClientOut->GetService(__uuidof(IAudioRenderClient),
2017                                  (void**)&_ptrRenderClient);
2018   EXIT_ON_ERROR(hr);
2019 
2020   // Mark playout side as initialized
2021   _playIsInitialized = true;
2022 
2023   CoTaskMemFree(pWfxOut);
2024   CoTaskMemFree(pWfxClosestMatch);
2025 
2026   RTC_LOG(LS_VERBOSE) << "render side is now initialized";
2027   return 0;
2028 
2029 Exit:
2030   _TraceCOMError(hr);
2031   CoTaskMemFree(pWfxOut);
2032   CoTaskMemFree(pWfxClosestMatch);
2033   SAFE_RELEASE(_ptrClientOut);
2034   SAFE_RELEASE(_ptrRenderClient);
2035   return -1;
2036 }
2037 
2038 // Capture initialization when the built-in AEC DirectX Media Object (DMO) is
2039 // used. Called from InitRecording(), most of which is skipped over. The DMO
2040 // handles device initialization itself.
2041 // Reference: http://msdn.microsoft.com/en-us/library/ff819492(v=vs.85).aspx
InitRecordingDMO()2042 int32_t AudioDeviceWindowsCore::InitRecordingDMO() {
2043   RTC_DCHECK(_builtInAecEnabled);
2044   RTC_DCHECK(_dmo);
2045 
2046   if (SetDMOProperties() == -1) {
2047     return -1;
2048   }
2049 
2050   DMO_MEDIA_TYPE mt = {};
2051   HRESULT hr = MoInitMediaType(&mt, sizeof(WAVEFORMATEX));
2052   if (FAILED(hr)) {
2053     MoFreeMediaType(&mt);
2054     _TraceCOMError(hr);
2055     return -1;
2056   }
2057   mt.majortype = MEDIATYPE_Audio;
2058   mt.subtype = MEDIASUBTYPE_PCM;
2059   mt.formattype = FORMAT_WaveFormatEx;
2060 
2061   // Supported formats
2062   // nChannels: 1 (in AEC-only mode)
2063   // nSamplesPerSec: 8000, 11025, 16000, 22050
2064   // wBitsPerSample: 16
2065   WAVEFORMATEX* ptrWav = reinterpret_cast<WAVEFORMATEX*>(mt.pbFormat);
2066   ptrWav->wFormatTag = WAVE_FORMAT_PCM;
2067   ptrWav->nChannels = 1;
2068   // 16000 is the highest we can support with our resampler.
2069   ptrWav->nSamplesPerSec = 16000;
2070   ptrWav->nAvgBytesPerSec = 32000;
2071   ptrWav->nBlockAlign = 2;
2072   ptrWav->wBitsPerSample = 16;
2073   ptrWav->cbSize = 0;
2074 
2075   // Set the VoE format equal to the AEC output format.
2076   _recAudioFrameSize = ptrWav->nBlockAlign;
2077   _recSampleRate = ptrWav->nSamplesPerSec;
2078   _recBlockSize = ptrWav->nSamplesPerSec / 100;
2079   _recChannels = ptrWav->nChannels;
2080 
2081   // Set the DMO output format parameters.
2082   hr = _dmo->SetOutputType(kAecCaptureStreamIndex, &mt, 0);
2083   MoFreeMediaType(&mt);
2084   if (FAILED(hr)) {
2085     _TraceCOMError(hr);
2086     return -1;
2087   }
2088 
2089   if (_ptrAudioBuffer) {
2090     _ptrAudioBuffer->SetRecordingSampleRate(_recSampleRate);
2091     _ptrAudioBuffer->SetRecordingChannels(_recChannels);
2092   } else {
2093     // Refer to InitRecording() for comments.
2094     RTC_LOG(LS_VERBOSE)
2095         << "AudioDeviceBuffer must be attached before streaming can start";
2096   }
2097 
2098   _mediaBuffer = rtc::make_ref_counted<MediaBufferImpl>(_recBlockSize *
2099                                                         _recAudioFrameSize);
2100 
2101   // Optional, but if called, must be after media types are set.
2102   hr = _dmo->AllocateStreamingResources();
2103   if (FAILED(hr)) {
2104     _TraceCOMError(hr);
2105     return -1;
2106   }
2107 
2108   _recIsInitialized = true;
2109   RTC_LOG(LS_VERBOSE) << "Capture side is now initialized";
2110 
2111   return 0;
2112 }
2113 
2114 // ----------------------------------------------------------------------------
2115 //  InitRecording
2116 // ----------------------------------------------------------------------------
2117 
InitRecording()2118 int32_t AudioDeviceWindowsCore::InitRecording() {
2119   MutexLock lock(&mutex_);
2120 
2121   if (_recording) {
2122     return -1;
2123   }
2124 
2125   if (_recIsInitialized) {
2126     return 0;
2127   }
2128 
2129   if (QueryPerformanceFrequency(&_perfCounterFreq) == 0) {
2130     return -1;
2131   }
2132   _perfCounterFactor = 10000000.0 / (double)_perfCounterFreq.QuadPart;
2133 
2134   if (_ptrDeviceIn == NULL) {
2135     return -1;
2136   }
2137 
2138   // Initialize the microphone (devices might have been added or removed)
2139   if (InitMicrophoneLocked() == -1) {
2140     RTC_LOG(LS_WARNING) << "InitMicrophone() failed";
2141   }
2142 
2143   // Ensure that the updated capturing endpoint device is valid
2144   if (_ptrDeviceIn == NULL) {
2145     return -1;
2146   }
2147 
2148   if (_builtInAecEnabled) {
2149     // The DMO will configure the capture device.
2150     return InitRecordingDMO();
2151   }
2152 
2153   HRESULT hr = S_OK;
2154   WAVEFORMATEX* pWfxIn = NULL;
2155   WAVEFORMATEXTENSIBLE Wfx = WAVEFORMATEXTENSIBLE();
2156   WAVEFORMATEX* pWfxClosestMatch = NULL;
2157 
2158   // Create COM object with IAudioClient interface.
2159   SAFE_RELEASE(_ptrClientIn);
2160   hr = _ptrDeviceIn->Activate(__uuidof(IAudioClient), CLSCTX_ALL, NULL,
2161                               (void**)&_ptrClientIn);
2162   EXIT_ON_ERROR(hr);
2163 
2164   // Retrieve the stream format that the audio engine uses for its internal
2165   // processing (mixing) of shared-mode streams.
2166   hr = _ptrClientIn->GetMixFormat(&pWfxIn);
2167   if (SUCCEEDED(hr)) {
2168     RTC_LOG(LS_VERBOSE) << "Audio Engine's current capturing mix format:";
2169     // format type
2170     RTC_LOG(LS_VERBOSE) << "wFormatTag     : 0x"
2171                         << rtc::ToHex(pWfxIn->wFormatTag) << " ("
2172                         << pWfxIn->wFormatTag << ")";
2173     // number of channels (i.e. mono, stereo...)
2174     RTC_LOG(LS_VERBOSE) << "nChannels      : " << pWfxIn->nChannels;
2175     // sample rate
2176     RTC_LOG(LS_VERBOSE) << "nSamplesPerSec : " << pWfxIn->nSamplesPerSec;
2177     // for buffer estimation
2178     RTC_LOG(LS_VERBOSE) << "nAvgBytesPerSec: " << pWfxIn->nAvgBytesPerSec;
2179     // block size of data
2180     RTC_LOG(LS_VERBOSE) << "nBlockAlign    : " << pWfxIn->nBlockAlign;
2181     // number of bits per sample of mono data
2182     RTC_LOG(LS_VERBOSE) << "wBitsPerSample : " << pWfxIn->wBitsPerSample;
2183     RTC_LOG(LS_VERBOSE) << "cbSize         : " << pWfxIn->cbSize;
2184   }
2185 
2186   // Set wave format
2187   Wfx.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
2188   Wfx.Format.wBitsPerSample = 16;
2189   Wfx.Format.cbSize = 22;
2190   Wfx.dwChannelMask = 0;
2191   Wfx.Samples.wValidBitsPerSample = Wfx.Format.wBitsPerSample;
2192   Wfx.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
2193 
2194   const int freqs[6] = {48000, 44100, 16000, 96000, 32000, 8000};
2195   hr = S_FALSE;
2196 
2197   // Iterate over frequencies and channels, in order of priority
2198   for (unsigned int freq = 0; freq < sizeof(freqs) / sizeof(freqs[0]); freq++) {
2199     for (unsigned int chan = 0;
2200          chan < sizeof(_recChannelsPrioList) / sizeof(_recChannelsPrioList[0]);
2201          chan++) {
2202       Wfx.Format.nChannels = _recChannelsPrioList[chan];
2203       Wfx.Format.nSamplesPerSec = freqs[freq];
2204       Wfx.Format.nBlockAlign =
2205           Wfx.Format.nChannels * Wfx.Format.wBitsPerSample / 8;
2206       Wfx.Format.nAvgBytesPerSec =
2207           Wfx.Format.nSamplesPerSec * Wfx.Format.nBlockAlign;
2208       // If the method succeeds and the audio endpoint device supports the
2209       // specified stream format, it returns S_OK. If the method succeeds and
2210       // provides a closest match to the specified format, it returns S_FALSE.
2211       hr = _ptrClientIn->IsFormatSupported(
2212           AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&Wfx, &pWfxClosestMatch);
2213       if (hr == S_OK) {
2214         break;
2215       } else {
2216         if (pWfxClosestMatch) {
2217           RTC_LOG(LS_INFO) << "nChannels=" << Wfx.Format.nChannels
2218                            << ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec
2219                            << " is not supported. Closest match: "
2220                               "nChannels="
2221                            << pWfxClosestMatch->nChannels << ", nSamplesPerSec="
2222                            << pWfxClosestMatch->nSamplesPerSec;
2223           CoTaskMemFree(pWfxClosestMatch);
2224           pWfxClosestMatch = NULL;
2225         } else {
2226           RTC_LOG(LS_INFO) << "nChannels=" << Wfx.Format.nChannels
2227                            << ", nSamplesPerSec=" << Wfx.Format.nSamplesPerSec
2228                            << " is not supported. No closest match.";
2229         }
2230       }
2231     }
2232     if (hr == S_OK)
2233       break;
2234   }
2235 
2236   if (hr == S_OK) {
2237     _recAudioFrameSize = Wfx.Format.nBlockAlign;
2238     _recSampleRate = Wfx.Format.nSamplesPerSec;
2239     _recBlockSize = Wfx.Format.nSamplesPerSec / 100;
2240     _recChannels = Wfx.Format.nChannels;
2241 
2242     RTC_LOG(LS_VERBOSE) << "VoE selected this capturing format:";
2243     RTC_LOG(LS_VERBOSE) << "wFormatTag        : 0x"
2244                         << rtc::ToHex(Wfx.Format.wFormatTag) << " ("
2245                         << Wfx.Format.wFormatTag << ")";
2246     RTC_LOG(LS_VERBOSE) << "nChannels         : " << Wfx.Format.nChannels;
2247     RTC_LOG(LS_VERBOSE) << "nSamplesPerSec    : " << Wfx.Format.nSamplesPerSec;
2248     RTC_LOG(LS_VERBOSE) << "nAvgBytesPerSec   : " << Wfx.Format.nAvgBytesPerSec;
2249     RTC_LOG(LS_VERBOSE) << "nBlockAlign       : " << Wfx.Format.nBlockAlign;
2250     RTC_LOG(LS_VERBOSE) << "wBitsPerSample    : " << Wfx.Format.wBitsPerSample;
2251     RTC_LOG(LS_VERBOSE) << "cbSize            : " << Wfx.Format.cbSize;
2252     RTC_LOG(LS_VERBOSE) << "Additional settings:";
2253     RTC_LOG(LS_VERBOSE) << "_recAudioFrameSize: " << _recAudioFrameSize;
2254     RTC_LOG(LS_VERBOSE) << "_recBlockSize     : " << _recBlockSize;
2255     RTC_LOG(LS_VERBOSE) << "_recChannels      : " << _recChannels;
2256   }
2257 
2258   // Create a capturing stream.
2259   hr = _ptrClientIn->Initialize(
2260       AUDCLNT_SHAREMODE_SHARED,  // share Audio Engine with other applications
2261       AUDCLNT_STREAMFLAGS_EVENTCALLBACK |  // processing of the audio buffer by
2262                                            // the client will be event driven
2263           AUDCLNT_STREAMFLAGS_NOPERSIST,   // volume and mute settings for an
2264                                            // audio session will not persist
2265                                            // across system restarts
2266       0,                    // required for event-driven shared mode
2267       0,                    // periodicity
2268       (WAVEFORMATEX*)&Wfx,  // selected wave format
2269       NULL);                // session GUID
2270 
2271   if (hr != S_OK) {
2272     RTC_LOG(LS_ERROR) << "IAudioClient::Initialize() failed:";
2273   }
2274   EXIT_ON_ERROR(hr);
2275 
2276   if (_ptrAudioBuffer) {
2277     // Update the audio buffer with the selected parameters
2278     _ptrAudioBuffer->SetRecordingSampleRate(_recSampleRate);
2279     _ptrAudioBuffer->SetRecordingChannels((uint8_t)_recChannels);
2280   } else {
2281     // We can enter this state during CoreAudioIsSupported() when no
2282     // AudioDeviceImplementation has been created, hence the AudioDeviceBuffer
2283     // does not exist. It is OK to end up here since we don't initiate any media
2284     // in CoreAudioIsSupported().
2285     RTC_LOG(LS_VERBOSE)
2286         << "AudioDeviceBuffer must be attached before streaming can start";
2287   }
2288 
2289   // Get the actual size of the shared (endpoint buffer).
2290   // Typical value is 960 audio frames <=> 20ms @ 48kHz sample rate.
2291   UINT bufferFrameCount(0);
2292   hr = _ptrClientIn->GetBufferSize(&bufferFrameCount);
2293   if (SUCCEEDED(hr)) {
2294     RTC_LOG(LS_VERBOSE) << "IAudioClient::GetBufferSize() => "
2295                         << bufferFrameCount << " (<=> "
2296                         << bufferFrameCount * _recAudioFrameSize << " bytes)";
2297   }
2298 
2299   // Set the event handle that the system signals when an audio buffer is ready
2300   // to be processed by the client.
2301   hr = _ptrClientIn->SetEventHandle(_hCaptureSamplesReadyEvent);
2302   EXIT_ON_ERROR(hr);
2303 
2304   // Get an IAudioCaptureClient interface.
2305   SAFE_RELEASE(_ptrCaptureClient);
2306   hr = _ptrClientIn->GetService(__uuidof(IAudioCaptureClient),
2307                                 (void**)&_ptrCaptureClient);
2308   EXIT_ON_ERROR(hr);
2309 
2310   // Mark capture side as initialized
2311   _recIsInitialized = true;
2312 
2313   CoTaskMemFree(pWfxIn);
2314   CoTaskMemFree(pWfxClosestMatch);
2315 
2316   RTC_LOG(LS_VERBOSE) << "capture side is now initialized";
2317   return 0;
2318 
2319 Exit:
2320   _TraceCOMError(hr);
2321   CoTaskMemFree(pWfxIn);
2322   CoTaskMemFree(pWfxClosestMatch);
2323   SAFE_RELEASE(_ptrClientIn);
2324   SAFE_RELEASE(_ptrCaptureClient);
2325   return -1;
2326 }
2327 
2328 // ----------------------------------------------------------------------------
2329 //  StartRecording
2330 // ----------------------------------------------------------------------------
2331 
StartRecording()2332 int32_t AudioDeviceWindowsCore::StartRecording() {
2333   if (!_recIsInitialized) {
2334     return -1;
2335   }
2336 
2337   if (_hRecThread != NULL) {
2338     return 0;
2339   }
2340 
2341   if (_recording) {
2342     return 0;
2343   }
2344 
2345   {
2346     MutexLock lockScoped(&mutex_);
2347 
2348     // Create thread which will drive the capturing
2349     LPTHREAD_START_ROUTINE lpStartAddress = WSAPICaptureThread;
2350     if (_builtInAecEnabled) {
2351       // Redirect to the DMO polling method.
2352       lpStartAddress = WSAPICaptureThreadPollDMO;
2353 
2354       if (!_playing) {
2355         // The DMO won't provide us captured output data unless we
2356         // give it render data to process.
2357         RTC_LOG(LS_ERROR)
2358             << "Playout must be started before recording when using"
2359                " the built-in AEC";
2360         return -1;
2361       }
2362     }
2363 
2364     RTC_DCHECK(_hRecThread == NULL);
2365     _hRecThread = CreateThread(NULL, 0, lpStartAddress, this, 0, NULL);
2366     if (_hRecThread == NULL) {
2367       RTC_LOG(LS_ERROR) << "failed to create the recording thread";
2368       return -1;
2369     }
2370 
2371     // Set thread priority to highest possible
2372     SetThreadPriority(_hRecThread, THREAD_PRIORITY_TIME_CRITICAL);
2373   }  // critScoped
2374 
2375   DWORD ret = WaitForSingleObject(_hCaptureStartedEvent, 1000);
2376   if (ret != WAIT_OBJECT_0) {
2377     RTC_LOG(LS_VERBOSE) << "capturing did not start up properly";
2378     return -1;
2379   }
2380   RTC_LOG(LS_VERBOSE) << "capture audio stream has now started...";
2381 
2382   _recording = true;
2383 
2384   return 0;
2385 }
2386 
2387 // ----------------------------------------------------------------------------
2388 //  StopRecording
2389 // ----------------------------------------------------------------------------
2390 
StopRecording()2391 int32_t AudioDeviceWindowsCore::StopRecording() {
2392   int32_t err = 0;
2393 
2394   if (!_recIsInitialized) {
2395     return 0;
2396   }
2397 
2398   _Lock();
2399 
2400   if (_hRecThread == NULL) {
2401     RTC_LOG(LS_VERBOSE)
2402         << "no capturing stream is active => close down WASAPI only";
2403     SAFE_RELEASE(_ptrClientIn);
2404     SAFE_RELEASE(_ptrCaptureClient);
2405     _recIsInitialized = false;
2406     _recording = false;
2407     _UnLock();
2408     return 0;
2409   }
2410 
2411   // Stop the driving thread...
2412   RTC_LOG(LS_VERBOSE) << "closing down the webrtc_core_audio_capture_thread...";
2413   // Manual-reset event; it will remain signalled to stop all capture threads.
2414   SetEvent(_hShutdownCaptureEvent);
2415 
2416   _UnLock();
2417   DWORD ret = WaitForSingleObject(_hRecThread, 2000);
2418   if (ret != WAIT_OBJECT_0) {
2419     RTC_LOG(LS_ERROR)
2420         << "failed to close down webrtc_core_audio_capture_thread";
2421     err = -1;
2422   } else {
2423     RTC_LOG(LS_VERBOSE) << "webrtc_core_audio_capture_thread is now closed";
2424   }
2425   _Lock();
2426 
2427   ResetEvent(_hShutdownCaptureEvent);  // Must be manually reset.
2428   // Ensure that the thread has released these interfaces properly.
2429   RTC_DCHECK(err == -1 || _ptrClientIn == NULL);
2430   RTC_DCHECK(err == -1 || _ptrCaptureClient == NULL);
2431 
2432   _recIsInitialized = false;
2433   _recording = false;
2434 
2435   // These will create thread leaks in the result of an error,
2436   // but we can at least resume the call.
2437   CloseHandle(_hRecThread);
2438   _hRecThread = NULL;
2439 
2440   if (_builtInAecEnabled) {
2441     RTC_DCHECK(_dmo);
2442     // This is necessary. Otherwise the DMO can generate garbage render
2443     // audio even after rendering has stopped.
2444     HRESULT hr = _dmo->FreeStreamingResources();
2445     if (FAILED(hr)) {
2446       _TraceCOMError(hr);
2447       err = -1;
2448     }
2449   }
2450 
2451   _UnLock();
2452 
2453   return err;
2454 }
2455 
2456 // ----------------------------------------------------------------------------
2457 //  RecordingIsInitialized
2458 // ----------------------------------------------------------------------------
2459 
RecordingIsInitialized() const2460 bool AudioDeviceWindowsCore::RecordingIsInitialized() const {
2461   return (_recIsInitialized);
2462 }
2463 
2464 // ----------------------------------------------------------------------------
2465 //  Recording
2466 // ----------------------------------------------------------------------------
2467 
Recording() const2468 bool AudioDeviceWindowsCore::Recording() const {
2469   return (_recording);
2470 }
2471 
2472 // ----------------------------------------------------------------------------
2473 //  PlayoutIsInitialized
2474 // ----------------------------------------------------------------------------
2475 
PlayoutIsInitialized() const2476 bool AudioDeviceWindowsCore::PlayoutIsInitialized() const {
2477   return (_playIsInitialized);
2478 }
2479 
2480 // ----------------------------------------------------------------------------
2481 //  StartPlayout
2482 // ----------------------------------------------------------------------------
2483 
StartPlayout()2484 int32_t AudioDeviceWindowsCore::StartPlayout() {
2485   if (!_playIsInitialized) {
2486     return -1;
2487   }
2488 
2489   if (_hPlayThread != NULL) {
2490     return 0;
2491   }
2492 
2493   if (_playing) {
2494     return 0;
2495   }
2496 
2497   {
2498     MutexLock lockScoped(&mutex_);
2499 
2500     // Create thread which will drive the rendering.
2501     RTC_DCHECK(_hPlayThread == NULL);
2502     _hPlayThread = CreateThread(NULL, 0, WSAPIRenderThread, this, 0, NULL);
2503     if (_hPlayThread == NULL) {
2504       RTC_LOG(LS_ERROR) << "failed to create the playout thread";
2505       return -1;
2506     }
2507 
2508     // Set thread priority to highest possible.
2509     SetThreadPriority(_hPlayThread, THREAD_PRIORITY_TIME_CRITICAL);
2510   }  // critScoped
2511 
2512   DWORD ret = WaitForSingleObject(_hRenderStartedEvent, 1000);
2513   if (ret != WAIT_OBJECT_0) {
2514     RTC_LOG(LS_VERBOSE) << "rendering did not start up properly";
2515     return -1;
2516   }
2517 
2518   _playing = true;
2519   RTC_LOG(LS_VERBOSE) << "rendering audio stream has now started...";
2520 
2521   return 0;
2522 }
2523 
2524 // ----------------------------------------------------------------------------
2525 //  StopPlayout
2526 // ----------------------------------------------------------------------------
2527 
StopPlayout()2528 int32_t AudioDeviceWindowsCore::StopPlayout() {
2529   if (!_playIsInitialized) {
2530     return 0;
2531   }
2532 
2533   {
2534     MutexLock lockScoped(&mutex_);
2535 
2536     if (_hPlayThread == NULL) {
2537       RTC_LOG(LS_VERBOSE)
2538           << "no rendering stream is active => close down WASAPI only";
2539       SAFE_RELEASE(_ptrClientOut);
2540       SAFE_RELEASE(_ptrRenderClient);
2541       _playIsInitialized = false;
2542       _playing = false;
2543       return 0;
2544     }
2545 
2546     // stop the driving thread...
2547     RTC_LOG(LS_VERBOSE)
2548         << "closing down the webrtc_core_audio_render_thread...";
2549     SetEvent(_hShutdownRenderEvent);
2550   }  // critScoped
2551 
2552   DWORD ret = WaitForSingleObject(_hPlayThread, 2000);
2553   if (ret != WAIT_OBJECT_0) {
2554     // the thread did not stop as it should
2555     RTC_LOG(LS_ERROR) << "failed to close down webrtc_core_audio_render_thread";
2556     CloseHandle(_hPlayThread);
2557     _hPlayThread = NULL;
2558     _playIsInitialized = false;
2559     _playing = false;
2560     return -1;
2561   }
2562 
2563   {
2564     MutexLock lockScoped(&mutex_);
2565     RTC_LOG(LS_VERBOSE) << "webrtc_core_audio_render_thread is now closed";
2566 
2567     // to reset this event manually at each time we finish with it,
2568     // in case that the render thread has exited before StopPlayout(),
2569     // this event might be caught by the new render thread within same VoE
2570     // instance.
2571     ResetEvent(_hShutdownRenderEvent);
2572 
2573     SAFE_RELEASE(_ptrClientOut);
2574     SAFE_RELEASE(_ptrRenderClient);
2575 
2576     _playIsInitialized = false;
2577     _playing = false;
2578 
2579     CloseHandle(_hPlayThread);
2580     _hPlayThread = NULL;
2581 
2582     if (_builtInAecEnabled && _recording) {
2583       // The DMO won't provide us captured output data unless we
2584       // give it render data to process.
2585       //
2586       // We still permit the playout to shutdown, and trace a warning.
2587       // Otherwise, VoE can get into a state which will never permit
2588       // playout to stop properly.
2589       RTC_LOG(LS_WARNING)
2590           << "Recording should be stopped before playout when using the"
2591              " built-in AEC";
2592     }
2593 
2594     // Reset the playout delay value.
2595     _sndCardPlayDelay = 0;
2596   }  // critScoped
2597 
2598   return 0;
2599 }
2600 
2601 // ----------------------------------------------------------------------------
2602 //  PlayoutDelay
2603 // ----------------------------------------------------------------------------
2604 
PlayoutDelay(uint16_t & delayMS) const2605 int32_t AudioDeviceWindowsCore::PlayoutDelay(uint16_t& delayMS) const {
2606   MutexLock lockScoped(&mutex_);
2607   delayMS = static_cast<uint16_t>(_sndCardPlayDelay);
2608   return 0;
2609 }
2610 
BuiltInAECIsAvailable() const2611 bool AudioDeviceWindowsCore::BuiltInAECIsAvailable() const {
2612   return _dmo != nullptr;
2613 }
2614 
2615 // ----------------------------------------------------------------------------
2616 //  Playing
2617 // ----------------------------------------------------------------------------
2618 
Playing() const2619 bool AudioDeviceWindowsCore::Playing() const {
2620   return (_playing);
2621 }
2622 
2623 // ============================================================================
2624 //                                 Private Methods
2625 // ============================================================================
2626 
2627 // ----------------------------------------------------------------------------
2628 //  [static] WSAPIRenderThread
2629 // ----------------------------------------------------------------------------
2630 
WSAPIRenderThread(LPVOID context)2631 DWORD WINAPI AudioDeviceWindowsCore::WSAPIRenderThread(LPVOID context) {
2632   return reinterpret_cast<AudioDeviceWindowsCore*>(context)->DoRenderThread();
2633 }
2634 
2635 // ----------------------------------------------------------------------------
2636 //  [static] WSAPICaptureThread
2637 // ----------------------------------------------------------------------------
2638 
WSAPICaptureThread(LPVOID context)2639 DWORD WINAPI AudioDeviceWindowsCore::WSAPICaptureThread(LPVOID context) {
2640   return reinterpret_cast<AudioDeviceWindowsCore*>(context)->DoCaptureThread();
2641 }
2642 
WSAPICaptureThreadPollDMO(LPVOID context)2643 DWORD WINAPI AudioDeviceWindowsCore::WSAPICaptureThreadPollDMO(LPVOID context) {
2644   return reinterpret_cast<AudioDeviceWindowsCore*>(context)
2645       ->DoCaptureThreadPollDMO();
2646 }
2647 
2648 // ----------------------------------------------------------------------------
2649 //  DoRenderThread
2650 // ----------------------------------------------------------------------------
2651 
DoRenderThread()2652 DWORD AudioDeviceWindowsCore::DoRenderThread() {
2653   bool keepPlaying = true;
2654   HANDLE waitArray[2] = {_hShutdownRenderEvent, _hRenderSamplesReadyEvent};
2655   HRESULT hr = S_OK;
2656   HANDLE hMmTask = NULL;
2657 
2658   // Initialize COM as MTA in this thread.
2659   ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA);
2660   if (!comInit.Succeeded()) {
2661     RTC_LOG(LS_ERROR) << "failed to initialize COM in render thread";
2662     return 1;
2663   }
2664 
2665   rtc::SetCurrentThreadName("webrtc_core_audio_render_thread");
2666 
2667   // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread
2668   // priority.
2669   //
2670   if (_winSupportAvrt) {
2671     DWORD taskIndex(0);
2672     hMmTask = _PAvSetMmThreadCharacteristicsA("Pro Audio", &taskIndex);
2673     if (hMmTask) {
2674       if (FALSE == _PAvSetMmThreadPriority(hMmTask, AVRT_PRIORITY_CRITICAL)) {
2675         RTC_LOG(LS_WARNING) << "failed to boost play-thread using MMCSS";
2676       }
2677       RTC_LOG(LS_VERBOSE)
2678           << "render thread is now registered with MMCSS (taskIndex="
2679           << taskIndex << ")";
2680     } else {
2681       RTC_LOG(LS_WARNING) << "failed to enable MMCSS on render thread (err="
2682                           << GetLastError() << ")";
2683       _TraceCOMError(GetLastError());
2684     }
2685   }
2686 
2687   _Lock();
2688 
2689   IAudioClock* clock = NULL;
2690 
2691   // Get size of rendering buffer (length is expressed as the number of audio
2692   // frames the buffer can hold). This value is fixed during the rendering
2693   // session.
2694   //
2695   UINT32 bufferLength = 0;
2696   hr = _ptrClientOut->GetBufferSize(&bufferLength);
2697   EXIT_ON_ERROR(hr);
2698   RTC_LOG(LS_VERBOSE) << "[REND] size of buffer       : " << bufferLength;
2699 
2700   // Get maximum latency for the current stream (will not change for the
2701   // lifetime  of the IAudioClient object).
2702   //
2703   REFERENCE_TIME latency;
2704   _ptrClientOut->GetStreamLatency(&latency);
2705   RTC_LOG(LS_VERBOSE) << "[REND] max stream latency   : " << (DWORD)latency
2706                       << " (" << (double)(latency / 10000.0) << " ms)";
2707 
2708   // Get the length of the periodic interval separating successive processing
2709   // passes by the audio engine on the data in the endpoint buffer.
2710   //
2711   // The period between processing passes by the audio engine is fixed for a
2712   // particular audio endpoint device and represents the smallest processing
2713   // quantum for the audio engine. This period plus the stream latency between
2714   // the buffer and endpoint device represents the minimum possible latency that
2715   // an audio application can achieve. Typical value: 100000 <=> 0.01 sec =
2716   // 10ms.
2717   //
2718   REFERENCE_TIME devPeriod = 0;
2719   REFERENCE_TIME devPeriodMin = 0;
2720   _ptrClientOut->GetDevicePeriod(&devPeriod, &devPeriodMin);
2721   RTC_LOG(LS_VERBOSE) << "[REND] device period        : " << (DWORD)devPeriod
2722                       << " (" << (double)(devPeriod / 10000.0) << " ms)";
2723 
2724   // Derive initial rendering delay.
2725   // Example: 10*(960/480) + 15 = 20 + 15 = 35ms
2726   //
2727   int playout_delay = 10 * (bufferLength / _playBlockSize) +
2728                       (int)((latency + devPeriod) / 10000);
2729   _sndCardPlayDelay = playout_delay;
2730   _writtenSamples = 0;
2731   RTC_LOG(LS_VERBOSE) << "[REND] initial delay        : " << playout_delay;
2732 
2733   double endpointBufferSizeMS =
2734       10.0 * ((double)bufferLength / (double)_devicePlayBlockSize);
2735   RTC_LOG(LS_VERBOSE) << "[REND] endpointBufferSizeMS : "
2736                       << endpointBufferSizeMS;
2737 
2738   // Before starting the stream, fill the rendering buffer with silence.
2739   //
2740   BYTE* pData = NULL;
2741   hr = _ptrRenderClient->GetBuffer(bufferLength, &pData);
2742   EXIT_ON_ERROR(hr);
2743 
2744   hr =
2745       _ptrRenderClient->ReleaseBuffer(bufferLength, AUDCLNT_BUFFERFLAGS_SILENT);
2746   EXIT_ON_ERROR(hr);
2747 
2748   _writtenSamples += bufferLength;
2749 
2750   hr = _ptrClientOut->GetService(__uuidof(IAudioClock), (void**)&clock);
2751   if (FAILED(hr)) {
2752     RTC_LOG(LS_WARNING)
2753         << "failed to get IAudioClock interface from the IAudioClient";
2754   }
2755 
2756   // Start up the rendering audio stream.
2757   hr = _ptrClientOut->Start();
2758   EXIT_ON_ERROR(hr);
2759 
2760   _UnLock();
2761 
2762   // Set event which will ensure that the calling thread modifies the playing
2763   // state to true.
2764   //
2765   SetEvent(_hRenderStartedEvent);
2766 
2767   // >> ------------------ THREAD LOOP ------------------
2768 
2769   while (keepPlaying) {
2770     // Wait for a render notification event or a shutdown event
2771     DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500);
2772     switch (waitResult) {
2773       case WAIT_OBJECT_0 + 0:  // _hShutdownRenderEvent
2774         keepPlaying = false;
2775         break;
2776       case WAIT_OBJECT_0 + 1:  // _hRenderSamplesReadyEvent
2777         break;
2778       case WAIT_TIMEOUT:  // timeout notification
2779         RTC_LOG(LS_WARNING) << "render event timed out after 0.5 seconds";
2780         goto Exit;
2781       default:  // unexpected error
2782         RTC_LOG(LS_WARNING) << "unknown wait termination on render side";
2783         goto Exit;
2784     }
2785 
2786     while (keepPlaying) {
2787       _Lock();
2788 
2789       // Sanity check to ensure that essential states are not modified
2790       // during the unlocked period.
2791       if (_ptrRenderClient == NULL || _ptrClientOut == NULL) {
2792         _UnLock();
2793         RTC_LOG(LS_ERROR)
2794             << "output state has been modified during unlocked period";
2795         goto Exit;
2796       }
2797 
2798       // Get the number of frames of padding (queued up to play) in the endpoint
2799       // buffer.
2800       UINT32 padding = 0;
2801       hr = _ptrClientOut->GetCurrentPadding(&padding);
2802       EXIT_ON_ERROR(hr);
2803 
2804       // Derive the amount of available space in the output buffer
2805       uint32_t framesAvailable = bufferLength - padding;
2806 
2807       // Do we have 10 ms available in the render buffer?
2808       if (framesAvailable < _playBlockSize) {
2809         // Not enough space in render buffer to store next render packet.
2810         _UnLock();
2811         break;
2812       }
2813 
2814       // Write n*10ms buffers to the render buffer
2815       const uint32_t n10msBuffers = (framesAvailable / _playBlockSize);
2816       for (uint32_t n = 0; n < n10msBuffers; n++) {
2817         // Get pointer (i.e., grab the buffer) to next space in the shared
2818         // render buffer.
2819         hr = _ptrRenderClient->GetBuffer(_playBlockSize, &pData);
2820         EXIT_ON_ERROR(hr);
2821 
2822         if (_ptrAudioBuffer) {
2823           // Request data to be played out (#bytes =
2824           // _playBlockSize*_audioFrameSize)
2825           _UnLock();
2826           int32_t nSamples =
2827               _ptrAudioBuffer->RequestPlayoutData(_playBlockSize);
2828           _Lock();
2829 
2830           if (nSamples == -1) {
2831             _UnLock();
2832             RTC_LOG(LS_ERROR) << "failed to read data from render client";
2833             goto Exit;
2834           }
2835 
2836           // Sanity check to ensure that essential states are not modified
2837           // during the unlocked period
2838           if (_ptrRenderClient == NULL || _ptrClientOut == NULL) {
2839             _UnLock();
2840             RTC_LOG(LS_ERROR)
2841                 << "output state has been modified during unlocked"
2842                    " period";
2843             goto Exit;
2844           }
2845           if (nSamples != static_cast<int32_t>(_playBlockSize)) {
2846             RTC_LOG(LS_WARNING)
2847                 << "nSamples(" << nSamples << ") != _playBlockSize"
2848                 << _playBlockSize << ")";
2849           }
2850 
2851           // Get the actual (stored) data
2852           nSamples = _ptrAudioBuffer->GetPlayoutData((int8_t*)pData);
2853         }
2854 
2855         DWORD dwFlags(0);
2856         hr = _ptrRenderClient->ReleaseBuffer(_playBlockSize, dwFlags);
2857         // See http://msdn.microsoft.com/en-us/library/dd316605(VS.85).aspx
2858         // for more details regarding AUDCLNT_E_DEVICE_INVALIDATED.
2859         EXIT_ON_ERROR(hr);
2860 
2861         _writtenSamples += _playBlockSize;
2862       }
2863 
2864       // Check the current delay on the playout side.
2865       if (clock) {
2866         UINT64 pos = 0;
2867         UINT64 freq = 1;
2868         clock->GetPosition(&pos, NULL);
2869         clock->GetFrequency(&freq);
2870         playout_delay = ROUND((double(_writtenSamples) / _devicePlaySampleRate -
2871                                double(pos) / freq) *
2872                               1000.0);
2873         _sndCardPlayDelay = playout_delay;
2874       }
2875 
2876       _UnLock();
2877     }
2878   }
2879 
2880   // ------------------ THREAD LOOP ------------------ <<
2881 
2882   SleepMs(static_cast<DWORD>(endpointBufferSizeMS + 0.5));
2883   hr = _ptrClientOut->Stop();
2884 
2885 Exit:
2886   SAFE_RELEASE(clock);
2887 
2888   if (FAILED(hr)) {
2889     _ptrClientOut->Stop();
2890     _UnLock();
2891     _TraceCOMError(hr);
2892   }
2893 
2894   if (_winSupportAvrt) {
2895     if (NULL != hMmTask) {
2896       _PAvRevertMmThreadCharacteristics(hMmTask);
2897     }
2898   }
2899 
2900   _Lock();
2901 
2902   if (keepPlaying) {
2903     if (_ptrClientOut != NULL) {
2904       hr = _ptrClientOut->Stop();
2905       if (FAILED(hr)) {
2906         _TraceCOMError(hr);
2907       }
2908       hr = _ptrClientOut->Reset();
2909       if (FAILED(hr)) {
2910         _TraceCOMError(hr);
2911       }
2912     }
2913     RTC_LOG(LS_ERROR)
2914         << "Playout error: rendering thread has ended pre-maturely";
2915   } else {
2916     RTC_LOG(LS_VERBOSE) << "_Rendering thread is now terminated properly";
2917   }
2918 
2919   _UnLock();
2920 
2921   return (DWORD)hr;
2922 }
2923 
InitCaptureThreadPriority()2924 DWORD AudioDeviceWindowsCore::InitCaptureThreadPriority() {
2925   _hMmTask = NULL;
2926 
2927   rtc::SetCurrentThreadName("webrtc_core_audio_capture_thread");
2928 
2929   // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread
2930   // priority.
2931   if (_winSupportAvrt) {
2932     DWORD taskIndex(0);
2933     _hMmTask = _PAvSetMmThreadCharacteristicsA("Pro Audio", &taskIndex);
2934     if (_hMmTask) {
2935       if (!_PAvSetMmThreadPriority(_hMmTask, AVRT_PRIORITY_CRITICAL)) {
2936         RTC_LOG(LS_WARNING) << "failed to boost rec-thread using MMCSS";
2937       }
2938       RTC_LOG(LS_VERBOSE)
2939           << "capture thread is now registered with MMCSS (taskIndex="
2940           << taskIndex << ")";
2941     } else {
2942       RTC_LOG(LS_WARNING) << "failed to enable MMCSS on capture thread (err="
2943                           << GetLastError() << ")";
2944       _TraceCOMError(GetLastError());
2945     }
2946   }
2947 
2948   return S_OK;
2949 }
2950 
RevertCaptureThreadPriority()2951 void AudioDeviceWindowsCore::RevertCaptureThreadPriority() {
2952   if (_winSupportAvrt) {
2953     if (NULL != _hMmTask) {
2954       _PAvRevertMmThreadCharacteristics(_hMmTask);
2955     }
2956   }
2957 
2958   _hMmTask = NULL;
2959 }
2960 
DoCaptureThreadPollDMO()2961 DWORD AudioDeviceWindowsCore::DoCaptureThreadPollDMO() {
2962   RTC_DCHECK(_mediaBuffer);
2963   bool keepRecording = true;
2964 
2965   // Initialize COM as MTA in this thread.
2966   ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA);
2967   if (!comInit.Succeeded()) {
2968     RTC_LOG(LS_ERROR) << "failed to initialize COM in polling DMO thread";
2969     return 1;
2970   }
2971 
2972   HRESULT hr = InitCaptureThreadPriority();
2973   if (FAILED(hr)) {
2974     return hr;
2975   }
2976 
2977   // Set event which will ensure that the calling thread modifies the
2978   // recording state to true.
2979   SetEvent(_hCaptureStartedEvent);
2980 
2981   // >> ---------------------------- THREAD LOOP ----------------------------
2982   while (keepRecording) {
2983     // Poll the DMO every 5 ms.
2984     // (The same interval used in the Wave implementation.)
2985     DWORD waitResult = WaitForSingleObject(_hShutdownCaptureEvent, 5);
2986     switch (waitResult) {
2987       case WAIT_OBJECT_0:  // _hShutdownCaptureEvent
2988         keepRecording = false;
2989         break;
2990       case WAIT_TIMEOUT:  // timeout notification
2991         break;
2992       default:  // unexpected error
2993         RTC_LOG(LS_WARNING) << "Unknown wait termination on capture side";
2994         hr = -1;  // To signal an error callback.
2995         keepRecording = false;
2996         break;
2997     }
2998 
2999     while (keepRecording) {
3000       MutexLock lockScoped(&mutex_);
3001 
3002       DWORD dwStatus = 0;
3003       {
3004         DMO_OUTPUT_DATA_BUFFER dmoBuffer = {0};
3005         dmoBuffer.pBuffer = _mediaBuffer.get();
3006         dmoBuffer.pBuffer->AddRef();
3007 
3008         // Poll the DMO for AEC processed capture data. The DMO will
3009         // copy available data to `dmoBuffer`, and should only return
3010         // 10 ms frames. The value of `dwStatus` should be ignored.
3011         hr = _dmo->ProcessOutput(0, 1, &dmoBuffer, &dwStatus);
3012         SAFE_RELEASE(dmoBuffer.pBuffer);
3013         dwStatus = dmoBuffer.dwStatus;
3014       }
3015       if (FAILED(hr)) {
3016         _TraceCOMError(hr);
3017         keepRecording = false;
3018         RTC_DCHECK_NOTREACHED();
3019         break;
3020       }
3021 
3022       ULONG bytesProduced = 0;
3023       BYTE* data;
3024       // Get a pointer to the data buffer. This should be valid until
3025       // the next call to ProcessOutput.
3026       hr = _mediaBuffer->GetBufferAndLength(&data, &bytesProduced);
3027       if (FAILED(hr)) {
3028         _TraceCOMError(hr);
3029         keepRecording = false;
3030         RTC_DCHECK_NOTREACHED();
3031         break;
3032       }
3033 
3034       if (bytesProduced > 0) {
3035         const int kSamplesProduced = bytesProduced / _recAudioFrameSize;
3036         // TODO(andrew): verify that this is always satisfied. It might
3037         // be that ProcessOutput will try to return more than 10 ms if
3038         // we fail to call it frequently enough.
3039         RTC_DCHECK_EQ(kSamplesProduced, static_cast<int>(_recBlockSize));
3040         RTC_DCHECK_EQ(sizeof(BYTE), sizeof(int8_t));
3041         _ptrAudioBuffer->SetRecordedBuffer(reinterpret_cast<int8_t*>(data),
3042                                            kSamplesProduced);
3043         _ptrAudioBuffer->SetVQEData(0, 0);
3044 
3045         _UnLock();  // Release lock while making the callback.
3046         _ptrAudioBuffer->DeliverRecordedData();
3047         _Lock();
3048       }
3049 
3050       // Reset length to indicate buffer availability.
3051       hr = _mediaBuffer->SetLength(0);
3052       if (FAILED(hr)) {
3053         _TraceCOMError(hr);
3054         keepRecording = false;
3055         RTC_DCHECK_NOTREACHED();
3056         break;
3057       }
3058 
3059       if (!(dwStatus & DMO_OUTPUT_DATA_BUFFERF_INCOMPLETE)) {
3060         // The DMO cannot currently produce more data. This is the
3061         // normal case; otherwise it means the DMO had more than 10 ms
3062         // of data available and ProcessOutput should be called again.
3063         break;
3064       }
3065     }
3066   }
3067   // ---------------------------- THREAD LOOP ---------------------------- <<
3068 
3069   RevertCaptureThreadPriority();
3070 
3071   if (FAILED(hr)) {
3072     RTC_LOG(LS_ERROR)
3073         << "Recording error: capturing thread has ended prematurely";
3074   } else {
3075     RTC_LOG(LS_VERBOSE) << "Capturing thread is now terminated properly";
3076   }
3077 
3078   return hr;
3079 }
3080 
3081 // ----------------------------------------------------------------------------
3082 //  DoCaptureThread
3083 // ----------------------------------------------------------------------------
3084 
DoCaptureThread()3085 DWORD AudioDeviceWindowsCore::DoCaptureThread() {
3086   bool keepRecording = true;
3087   HANDLE waitArray[2] = {_hShutdownCaptureEvent, _hCaptureSamplesReadyEvent};
3088   HRESULT hr = S_OK;
3089 
3090   LARGE_INTEGER t1;
3091 
3092   BYTE* syncBuffer = NULL;
3093   UINT32 syncBufIndex = 0;
3094 
3095   _readSamples = 0;
3096 
3097   // Initialize COM as MTA in this thread.
3098   ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA);
3099   if (!comInit.Succeeded()) {
3100     RTC_LOG(LS_ERROR) << "failed to initialize COM in capture thread";
3101     return 1;
3102   }
3103 
3104   hr = InitCaptureThreadPriority();
3105   if (FAILED(hr)) {
3106     return hr;
3107   }
3108 
3109   _Lock();
3110 
3111   // Get size of capturing buffer (length is expressed as the number of audio
3112   // frames the buffer can hold). This value is fixed during the capturing
3113   // session.
3114   //
3115   UINT32 bufferLength = 0;
3116   if (_ptrClientIn == NULL) {
3117     RTC_LOG(LS_ERROR)
3118         << "input state has been modified before capture loop starts.";
3119     return 1;
3120   }
3121   hr = _ptrClientIn->GetBufferSize(&bufferLength);
3122   EXIT_ON_ERROR(hr);
3123   RTC_LOG(LS_VERBOSE) << "[CAPT] size of buffer       : " << bufferLength;
3124 
3125   // Allocate memory for sync buffer.
3126   // It is used for compensation between native 44.1 and internal 44.0 and
3127   // for cases when the capture buffer is larger than 10ms.
3128   //
3129   const UINT32 syncBufferSize = 2 * (bufferLength * _recAudioFrameSize);
3130   syncBuffer = new BYTE[syncBufferSize];
3131   if (syncBuffer == NULL) {
3132     return (DWORD)E_POINTER;
3133   }
3134   RTC_LOG(LS_VERBOSE) << "[CAPT] size of sync buffer  : " << syncBufferSize
3135                       << " [bytes]";
3136 
3137   // Get maximum latency for the current stream (will not change for the
3138   // lifetime of the IAudioClient object).
3139   //
3140   REFERENCE_TIME latency;
3141   _ptrClientIn->GetStreamLatency(&latency);
3142   RTC_LOG(LS_VERBOSE) << "[CAPT] max stream latency   : " << (DWORD)latency
3143                       << " (" << (double)(latency / 10000.0) << " ms)";
3144 
3145   // Get the length of the periodic interval separating successive processing
3146   // passes by the audio engine on the data in the endpoint buffer.
3147   //
3148   REFERENCE_TIME devPeriod = 0;
3149   REFERENCE_TIME devPeriodMin = 0;
3150   _ptrClientIn->GetDevicePeriod(&devPeriod, &devPeriodMin);
3151   RTC_LOG(LS_VERBOSE) << "[CAPT] device period        : " << (DWORD)devPeriod
3152                       << " (" << (double)(devPeriod / 10000.0) << " ms)";
3153 
3154   double extraDelayMS = (double)((latency + devPeriod) / 10000.0);
3155   RTC_LOG(LS_VERBOSE) << "[CAPT] extraDelayMS         : " << extraDelayMS;
3156 
3157   double endpointBufferSizeMS =
3158       10.0 * ((double)bufferLength / (double)_recBlockSize);
3159   RTC_LOG(LS_VERBOSE) << "[CAPT] endpointBufferSizeMS : "
3160                       << endpointBufferSizeMS;
3161 
3162   // Start up the capturing stream.
3163   //
3164   hr = _ptrClientIn->Start();
3165   EXIT_ON_ERROR(hr);
3166 
3167   _UnLock();
3168 
3169   // Set event which will ensure that the calling thread modifies the recording
3170   // state to true.
3171   //
3172   SetEvent(_hCaptureStartedEvent);
3173 
3174   // >> ---------------------------- THREAD LOOP ----------------------------
3175 
3176   while (keepRecording) {
3177     // Wait for a capture notification event or a shutdown event
3178     DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500);
3179     switch (waitResult) {
3180       case WAIT_OBJECT_0 + 0:  // _hShutdownCaptureEvent
3181         keepRecording = false;
3182         break;
3183       case WAIT_OBJECT_0 + 1:  // _hCaptureSamplesReadyEvent
3184         break;
3185       case WAIT_TIMEOUT:  // timeout notification
3186         RTC_LOG(LS_WARNING) << "capture event timed out after 0.5 seconds";
3187         goto Exit;
3188       default:  // unexpected error
3189         RTC_LOG(LS_WARNING) << "unknown wait termination on capture side";
3190         goto Exit;
3191     }
3192 
3193     while (keepRecording) {
3194       BYTE* pData = 0;
3195       UINT32 framesAvailable = 0;
3196       DWORD flags = 0;
3197       UINT64 recTime = 0;
3198       UINT64 recPos = 0;
3199 
3200       _Lock();
3201 
3202       // Sanity check to ensure that essential states are not modified
3203       // during the unlocked period.
3204       if (_ptrCaptureClient == NULL || _ptrClientIn == NULL) {
3205         _UnLock();
3206         RTC_LOG(LS_ERROR)
3207             << "input state has been modified during unlocked period";
3208         goto Exit;
3209       }
3210 
3211       //  Find out how much capture data is available
3212       //
3213       hr = _ptrCaptureClient->GetBuffer(
3214           &pData,            // packet which is ready to be read by used
3215           &framesAvailable,  // #frames in the captured packet (can be zero)
3216           &flags,            // support flags (check)
3217           &recPos,    // device position of first audio frame in data packet
3218           &recTime);  // value of performance counter at the time of recording
3219                       // the first audio frame
3220 
3221       if (SUCCEEDED(hr)) {
3222         if (AUDCLNT_S_BUFFER_EMPTY == hr) {
3223           // Buffer was empty => start waiting for a new capture notification
3224           // event
3225           _UnLock();
3226           break;
3227         }
3228 
3229         if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
3230           // Treat all of the data in the packet as silence and ignore the
3231           // actual data values.
3232           RTC_LOG(LS_WARNING) << "AUDCLNT_BUFFERFLAGS_SILENT";
3233           pData = NULL;
3234         }
3235 
3236         RTC_DCHECK_NE(framesAvailable, 0);
3237 
3238         if (pData) {
3239           CopyMemory(&syncBuffer[syncBufIndex * _recAudioFrameSize], pData,
3240                      framesAvailable * _recAudioFrameSize);
3241         } else {
3242           ZeroMemory(&syncBuffer[syncBufIndex * _recAudioFrameSize],
3243                      framesAvailable * _recAudioFrameSize);
3244         }
3245         RTC_DCHECK_GE(syncBufferSize, (syncBufIndex * _recAudioFrameSize) +
3246                                           framesAvailable * _recAudioFrameSize);
3247 
3248         // Release the capture buffer
3249         //
3250         hr = _ptrCaptureClient->ReleaseBuffer(framesAvailable);
3251         EXIT_ON_ERROR(hr);
3252 
3253         _readSamples += framesAvailable;
3254         syncBufIndex += framesAvailable;
3255 
3256         QueryPerformanceCounter(&t1);
3257 
3258         // Get the current recording and playout delay.
3259         uint32_t sndCardRecDelay = (uint32_t)(
3260             ((((UINT64)t1.QuadPart * _perfCounterFactor) - recTime) / 10000) +
3261             (10 * syncBufIndex) / _recBlockSize - 10);
3262         uint32_t sndCardPlayDelay = static_cast<uint32_t>(_sndCardPlayDelay);
3263 
3264         while (syncBufIndex >= _recBlockSize) {
3265           if (_ptrAudioBuffer) {
3266             _ptrAudioBuffer->SetRecordedBuffer((const int8_t*)syncBuffer,
3267                                                _recBlockSize);
3268             _ptrAudioBuffer->SetVQEData(sndCardPlayDelay, sndCardRecDelay);
3269 
3270             _ptrAudioBuffer->SetTypingStatus(KeyPressed());
3271 
3272             _UnLock();  // release lock while making the callback
3273             _ptrAudioBuffer->DeliverRecordedData();
3274             _Lock();  // restore the lock
3275 
3276             // Sanity check to ensure that essential states are not modified
3277             // during the unlocked period
3278             if (_ptrCaptureClient == NULL || _ptrClientIn == NULL) {
3279               _UnLock();
3280               RTC_LOG(LS_ERROR) << "input state has been modified during"
3281                                    " unlocked period";
3282               goto Exit;
3283             }
3284           }
3285 
3286           // store remaining data which was not able to deliver as 10ms segment
3287           MoveMemory(&syncBuffer[0],
3288                      &syncBuffer[_recBlockSize * _recAudioFrameSize],
3289                      (syncBufIndex - _recBlockSize) * _recAudioFrameSize);
3290           syncBufIndex -= _recBlockSize;
3291           sndCardRecDelay -= 10;
3292         }
3293       } else {
3294         // If GetBuffer returns AUDCLNT_E_BUFFER_ERROR, the thread consuming the
3295         // audio samples must wait for the next processing pass. The client
3296         // might benefit from keeping a count of the failed GetBuffer calls. If
3297         // GetBuffer returns this error repeatedly, the client can start a new
3298         // processing loop after shutting down the current client by calling
3299         // IAudioClient::Stop, IAudioClient::Reset, and releasing the audio
3300         // client.
3301         RTC_LOG(LS_ERROR) << "IAudioCaptureClient::GetBuffer returned"
3302                              " AUDCLNT_E_BUFFER_ERROR, hr = 0x"
3303                           << rtc::ToHex(hr);
3304         goto Exit;
3305       }
3306 
3307       _UnLock();
3308     }
3309   }
3310 
3311   // ---------------------------- THREAD LOOP ---------------------------- <<
3312 
3313   if (_ptrClientIn) {
3314     hr = _ptrClientIn->Stop();
3315   }
3316 
3317 Exit:
3318   if (FAILED(hr)) {
3319     _ptrClientIn->Stop();
3320     _UnLock();
3321     _TraceCOMError(hr);
3322   }
3323 
3324   RevertCaptureThreadPriority();
3325 
3326   _Lock();
3327 
3328   if (keepRecording) {
3329     if (_ptrClientIn != NULL) {
3330       hr = _ptrClientIn->Stop();
3331       if (FAILED(hr)) {
3332         _TraceCOMError(hr);
3333       }
3334       hr = _ptrClientIn->Reset();
3335       if (FAILED(hr)) {
3336         _TraceCOMError(hr);
3337       }
3338     }
3339 
3340     RTC_LOG(LS_ERROR)
3341         << "Recording error: capturing thread has ended pre-maturely";
3342   } else {
3343     RTC_LOG(LS_VERBOSE) << "_Capturing thread is now terminated properly";
3344   }
3345 
3346   SAFE_RELEASE(_ptrClientIn);
3347   SAFE_RELEASE(_ptrCaptureClient);
3348 
3349   _UnLock();
3350 
3351   if (syncBuffer) {
3352     delete[] syncBuffer;
3353   }
3354 
3355   return (DWORD)hr;
3356 }
3357 
EnableBuiltInAEC(bool enable)3358 int32_t AudioDeviceWindowsCore::EnableBuiltInAEC(bool enable) {
3359   if (_recIsInitialized) {
3360     RTC_LOG(LS_ERROR)
3361         << "Attempt to set Windows AEC with recording already initialized";
3362     return -1;
3363   }
3364 
3365   if (_dmo == NULL) {
3366     RTC_LOG(LS_ERROR)
3367         << "Built-in AEC DMO was not initialized properly at create time";
3368     return -1;
3369   }
3370 
3371   _builtInAecEnabled = enable;
3372   return 0;
3373 }
3374 
_Lock()3375 void AudioDeviceWindowsCore::_Lock() RTC_NO_THREAD_SAFETY_ANALYSIS {
3376   mutex_.Lock();
3377 }
3378 
_UnLock()3379 void AudioDeviceWindowsCore::_UnLock() RTC_NO_THREAD_SAFETY_ANALYSIS {
3380   mutex_.Unlock();
3381 }
3382 
SetDMOProperties()3383 int AudioDeviceWindowsCore::SetDMOProperties() {
3384   HRESULT hr = S_OK;
3385   RTC_DCHECK(_dmo);
3386 
3387   rtc::scoped_refptr<IPropertyStore> ps;
3388   {
3389     IPropertyStore* ptrPS = NULL;
3390     hr = _dmo->QueryInterface(IID_IPropertyStore,
3391                               reinterpret_cast<void**>(&ptrPS));
3392     if (FAILED(hr) || ptrPS == NULL) {
3393       _TraceCOMError(hr);
3394       return -1;
3395     }
3396     ps = ptrPS;
3397     SAFE_RELEASE(ptrPS);
3398   }
3399 
3400   // Set the AEC system mode.
3401   // SINGLE_CHANNEL_AEC - AEC processing only.
3402   if (SetVtI4Property(ps.get(), MFPKEY_WMAAECMA_SYSTEM_MODE,
3403                       SINGLE_CHANNEL_AEC)) {
3404     return -1;
3405   }
3406 
3407   // Set the AEC source mode.
3408   // VARIANT_TRUE - Source mode (we poll the AEC for captured data).
3409   if (SetBoolProperty(ps.get(), MFPKEY_WMAAECMA_DMO_SOURCE_MODE,
3410                       VARIANT_TRUE) == -1) {
3411     return -1;
3412   }
3413 
3414   // Enable the feature mode.
3415   // This lets us override all the default processing settings below.
3416   if (SetBoolProperty(ps.get(), MFPKEY_WMAAECMA_FEATURE_MODE, VARIANT_TRUE) ==
3417       -1) {
3418     return -1;
3419   }
3420 
3421   // Disable analog AGC (default enabled).
3422   if (SetBoolProperty(ps.get(), MFPKEY_WMAAECMA_MIC_GAIN_BOUNDER,
3423                       VARIANT_FALSE) == -1) {
3424     return -1;
3425   }
3426 
3427   // Disable noise suppression (default enabled).
3428   // 0 - Disabled, 1 - Enabled
3429   if (SetVtI4Property(ps.get(), MFPKEY_WMAAECMA_FEATR_NS, 0) == -1) {
3430     return -1;
3431   }
3432 
3433   // Relevant parameters to leave at default settings:
3434   // MFPKEY_WMAAECMA_FEATR_AGC - Digital AGC (disabled).
3435   // MFPKEY_WMAAECMA_FEATR_CENTER_CLIP - AEC center clipping (enabled).
3436   // MFPKEY_WMAAECMA_FEATR_ECHO_LENGTH - Filter length (256 ms).
3437   // TODO(andrew): investigate decresing the length to 128 ms.
3438   // MFPKEY_WMAAECMA_FEATR_FRAME_SIZE - Frame size (0).
3439   //   0 is automatic; defaults to 160 samples (or 10 ms frames at the
3440   //   selected 16 kHz) as long as mic array processing is disabled.
3441   // MFPKEY_WMAAECMA_FEATR_NOISE_FILL - Comfort noise (enabled).
3442   // MFPKEY_WMAAECMA_FEATR_VAD - VAD (disabled).
3443 
3444   // Set the devices selected by VoE. If using a default device, we need to
3445   // search for the device index.
3446   int inDevIndex = _inputDeviceIndex;
3447   int outDevIndex = _outputDeviceIndex;
3448   if (!_usingInputDeviceIndex) {
3449     ERole role = eCommunications;
3450     if (_inputDevice == AudioDeviceModule::kDefaultDevice) {
3451       role = eConsole;
3452     }
3453 
3454     if (_GetDefaultDeviceIndex(eCapture, role, &inDevIndex) == -1) {
3455       return -1;
3456     }
3457   }
3458 
3459   if (!_usingOutputDeviceIndex) {
3460     ERole role = eCommunications;
3461     if (_outputDevice == AudioDeviceModule::kDefaultDevice) {
3462       role = eConsole;
3463     }
3464 
3465     if (_GetDefaultDeviceIndex(eRender, role, &outDevIndex) == -1) {
3466       return -1;
3467     }
3468   }
3469 
3470   DWORD devIndex = static_cast<uint32_t>(outDevIndex << 16) +
3471                    static_cast<uint32_t>(0x0000ffff & inDevIndex);
3472   RTC_LOG(LS_VERBOSE) << "Capture device index: " << inDevIndex
3473                       << ", render device index: " << outDevIndex;
3474   if (SetVtI4Property(ps.get(), MFPKEY_WMAAECMA_DEVICE_INDEXES, devIndex) ==
3475       -1) {
3476     return -1;
3477   }
3478 
3479   return 0;
3480 }
3481 
SetBoolProperty(IPropertyStore * ptrPS,REFPROPERTYKEY key,VARIANT_BOOL value)3482 int AudioDeviceWindowsCore::SetBoolProperty(IPropertyStore* ptrPS,
3483                                             REFPROPERTYKEY key,
3484                                             VARIANT_BOOL value) {
3485   PROPVARIANT pv;
3486   PropVariantInit(&pv);
3487   pv.vt = VT_BOOL;
3488   pv.boolVal = value;
3489   HRESULT hr = ptrPS->SetValue(key, pv);
3490   PropVariantClear(&pv);
3491   if (FAILED(hr)) {
3492     _TraceCOMError(hr);
3493     return -1;
3494   }
3495   return 0;
3496 }
3497 
SetVtI4Property(IPropertyStore * ptrPS,REFPROPERTYKEY key,LONG value)3498 int AudioDeviceWindowsCore::SetVtI4Property(IPropertyStore* ptrPS,
3499                                             REFPROPERTYKEY key,
3500                                             LONG value) {
3501   PROPVARIANT pv;
3502   PropVariantInit(&pv);
3503   pv.vt = VT_I4;
3504   pv.lVal = value;
3505   HRESULT hr = ptrPS->SetValue(key, pv);
3506   PropVariantClear(&pv);
3507   if (FAILED(hr)) {
3508     _TraceCOMError(hr);
3509     return -1;
3510   }
3511   return 0;
3512 }
3513 
3514 // ----------------------------------------------------------------------------
3515 //  _RefreshDeviceList
3516 //
3517 //  Creates a new list of endpoint rendering or capture devices after
3518 //  deleting any previously created (and possibly out-of-date) list of
3519 //  such devices.
3520 // ----------------------------------------------------------------------------
3521 
_RefreshDeviceList(EDataFlow dir)3522 int32_t AudioDeviceWindowsCore::_RefreshDeviceList(EDataFlow dir) {
3523   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3524 
3525   HRESULT hr = S_OK;
3526   IMMDeviceCollection* pCollection = NULL;
3527 
3528   RTC_DCHECK(dir == eRender || dir == eCapture);
3529   RTC_DCHECK(_ptrEnumerator);
3530 
3531   // Create a fresh list of devices using the specified direction
3532   hr = _ptrEnumerator->EnumAudioEndpoints(dir, DEVICE_STATE_ACTIVE,
3533                                           &pCollection);
3534   if (FAILED(hr)) {
3535     _TraceCOMError(hr);
3536     SAFE_RELEASE(pCollection);
3537     return -1;
3538   }
3539 
3540   if (dir == eRender) {
3541     SAFE_RELEASE(_ptrRenderCollection);
3542     _ptrRenderCollection = pCollection;
3543   } else {
3544     SAFE_RELEASE(_ptrCaptureCollection);
3545     _ptrCaptureCollection = pCollection;
3546   }
3547 
3548   return 0;
3549 }
3550 
3551 // ----------------------------------------------------------------------------
3552 //  _DeviceListCount
3553 //
3554 //  Gets a count of the endpoint rendering or capture devices in the
3555 //  current list of such devices.
3556 // ----------------------------------------------------------------------------
3557 
_DeviceListCount(EDataFlow dir)3558 int16_t AudioDeviceWindowsCore::_DeviceListCount(EDataFlow dir) {
3559   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3560 
3561   HRESULT hr = S_OK;
3562   UINT count = 0;
3563 
3564   RTC_DCHECK(eRender == dir || eCapture == dir);
3565 
3566   if (eRender == dir && NULL != _ptrRenderCollection) {
3567     hr = _ptrRenderCollection->GetCount(&count);
3568   } else if (NULL != _ptrCaptureCollection) {
3569     hr = _ptrCaptureCollection->GetCount(&count);
3570   }
3571 
3572   if (FAILED(hr)) {
3573     _TraceCOMError(hr);
3574     return -1;
3575   }
3576 
3577   return static_cast<int16_t>(count);
3578 }
3579 
3580 // ----------------------------------------------------------------------------
3581 //  _GetListDeviceName
3582 //
3583 //  Gets the friendly name of an endpoint rendering or capture device
3584 //  from the current list of such devices. The caller uses an index
3585 //  into the list to identify the device.
3586 //
3587 //  Uses: _ptrRenderCollection or _ptrCaptureCollection which is updated
3588 //  in _RefreshDeviceList().
3589 // ----------------------------------------------------------------------------
3590 
_GetListDeviceName(EDataFlow dir,int index,LPWSTR szBuffer,int bufferLen)3591 int32_t AudioDeviceWindowsCore::_GetListDeviceName(EDataFlow dir,
3592                                                    int index,
3593                                                    LPWSTR szBuffer,
3594                                                    int bufferLen) {
3595   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3596 
3597   HRESULT hr = S_OK;
3598   IMMDevice* pDevice = NULL;
3599 
3600   RTC_DCHECK(dir == eRender || dir == eCapture);
3601 
3602   if (eRender == dir && NULL != _ptrRenderCollection) {
3603     hr = _ptrRenderCollection->Item(index, &pDevice);
3604   } else if (NULL != _ptrCaptureCollection) {
3605     hr = _ptrCaptureCollection->Item(index, &pDevice);
3606   }
3607 
3608   if (FAILED(hr)) {
3609     _TraceCOMError(hr);
3610     SAFE_RELEASE(pDevice);
3611     return -1;
3612   }
3613 
3614   int32_t res = _GetDeviceName(pDevice, szBuffer, bufferLen);
3615   SAFE_RELEASE(pDevice);
3616   return res;
3617 }
3618 
3619 // ----------------------------------------------------------------------------
3620 //  _GetDefaultDeviceName
3621 //
3622 //  Gets the friendly name of an endpoint rendering or capture device
3623 //  given a specified device role.
3624 //
3625 //  Uses: _ptrEnumerator
3626 // ----------------------------------------------------------------------------
3627 
_GetDefaultDeviceName(EDataFlow dir,ERole role,LPWSTR szBuffer,int bufferLen)3628 int32_t AudioDeviceWindowsCore::_GetDefaultDeviceName(EDataFlow dir,
3629                                                       ERole role,
3630                                                       LPWSTR szBuffer,
3631                                                       int bufferLen) {
3632   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3633 
3634   HRESULT hr = S_OK;
3635   IMMDevice* pDevice = NULL;
3636 
3637   RTC_DCHECK(dir == eRender || dir == eCapture);
3638   RTC_DCHECK(role == eConsole || role == eCommunications);
3639   RTC_DCHECK(_ptrEnumerator);
3640 
3641   hr = _ptrEnumerator->GetDefaultAudioEndpoint(dir, role, &pDevice);
3642 
3643   if (FAILED(hr)) {
3644     _TraceCOMError(hr);
3645     SAFE_RELEASE(pDevice);
3646     return -1;
3647   }
3648 
3649   int32_t res = _GetDeviceName(pDevice, szBuffer, bufferLen);
3650   SAFE_RELEASE(pDevice);
3651   return res;
3652 }
3653 
3654 // ----------------------------------------------------------------------------
3655 //  _GetListDeviceID
3656 //
3657 //  Gets the unique ID string of an endpoint rendering or capture device
3658 //  from the current list of such devices. The caller uses an index
3659 //  into the list to identify the device.
3660 //
3661 //  Uses: _ptrRenderCollection or _ptrCaptureCollection which is updated
3662 //  in _RefreshDeviceList().
3663 // ----------------------------------------------------------------------------
3664 
_GetListDeviceID(EDataFlow dir,int index,LPWSTR szBuffer,int bufferLen)3665 int32_t AudioDeviceWindowsCore::_GetListDeviceID(EDataFlow dir,
3666                                                  int index,
3667                                                  LPWSTR szBuffer,
3668                                                  int bufferLen) {
3669   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3670 
3671   HRESULT hr = S_OK;
3672   IMMDevice* pDevice = NULL;
3673 
3674   RTC_DCHECK(dir == eRender || dir == eCapture);
3675 
3676   if (eRender == dir && NULL != _ptrRenderCollection) {
3677     hr = _ptrRenderCollection->Item(index, &pDevice);
3678   } else if (NULL != _ptrCaptureCollection) {
3679     hr = _ptrCaptureCollection->Item(index, &pDevice);
3680   }
3681 
3682   if (FAILED(hr)) {
3683     _TraceCOMError(hr);
3684     SAFE_RELEASE(pDevice);
3685     return -1;
3686   }
3687 
3688   int32_t res = _GetDeviceID(pDevice, szBuffer, bufferLen);
3689   SAFE_RELEASE(pDevice);
3690   return res;
3691 }
3692 
3693 // ----------------------------------------------------------------------------
3694 //  _GetDefaultDeviceID
3695 //
3696 //  Gets the uniqe device ID of an endpoint rendering or capture device
3697 //  given a specified device role.
3698 //
3699 //  Uses: _ptrEnumerator
3700 // ----------------------------------------------------------------------------
3701 
_GetDefaultDeviceID(EDataFlow dir,ERole role,LPWSTR szBuffer,int bufferLen)3702 int32_t AudioDeviceWindowsCore::_GetDefaultDeviceID(EDataFlow dir,
3703                                                     ERole role,
3704                                                     LPWSTR szBuffer,
3705                                                     int bufferLen) {
3706   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3707 
3708   HRESULT hr = S_OK;
3709   IMMDevice* pDevice = NULL;
3710 
3711   RTC_DCHECK(dir == eRender || dir == eCapture);
3712   RTC_DCHECK(role == eConsole || role == eCommunications);
3713   RTC_DCHECK(_ptrEnumerator);
3714 
3715   hr = _ptrEnumerator->GetDefaultAudioEndpoint(dir, role, &pDevice);
3716 
3717   if (FAILED(hr)) {
3718     _TraceCOMError(hr);
3719     SAFE_RELEASE(pDevice);
3720     return -1;
3721   }
3722 
3723   int32_t res = _GetDeviceID(pDevice, szBuffer, bufferLen);
3724   SAFE_RELEASE(pDevice);
3725   return res;
3726 }
3727 
_GetDefaultDeviceIndex(EDataFlow dir,ERole role,int * index)3728 int32_t AudioDeviceWindowsCore::_GetDefaultDeviceIndex(EDataFlow dir,
3729                                                        ERole role,
3730                                                        int* index) {
3731   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3732 
3733   HRESULT hr = S_OK;
3734   WCHAR szDefaultDeviceID[MAX_PATH] = {0};
3735   WCHAR szDeviceID[MAX_PATH] = {0};
3736 
3737   const size_t kDeviceIDLength = sizeof(szDeviceID) / sizeof(szDeviceID[0]);
3738   RTC_DCHECK_EQ(kDeviceIDLength,
3739                 sizeof(szDefaultDeviceID) / sizeof(szDefaultDeviceID[0]));
3740 
3741   if (_GetDefaultDeviceID(dir, role, szDefaultDeviceID, kDeviceIDLength) ==
3742       -1) {
3743     return -1;
3744   }
3745 
3746   IMMDeviceCollection* collection = _ptrCaptureCollection;
3747   if (dir == eRender) {
3748     collection = _ptrRenderCollection;
3749   }
3750 
3751   if (!collection) {
3752     RTC_LOG(LS_ERROR) << "Device collection not valid";
3753     return -1;
3754   }
3755 
3756   UINT count = 0;
3757   hr = collection->GetCount(&count);
3758   if (FAILED(hr)) {
3759     _TraceCOMError(hr);
3760     return -1;
3761   }
3762 
3763   *index = -1;
3764   for (UINT i = 0; i < count; i++) {
3765     memset(szDeviceID, 0, sizeof(szDeviceID));
3766     rtc::scoped_refptr<IMMDevice> device;
3767     {
3768       IMMDevice* ptrDevice = NULL;
3769       hr = collection->Item(i, &ptrDevice);
3770       if (FAILED(hr) || ptrDevice == NULL) {
3771         _TraceCOMError(hr);
3772         return -1;
3773       }
3774       device = ptrDevice;
3775       SAFE_RELEASE(ptrDevice);
3776     }
3777 
3778     if (_GetDeviceID(device.get(), szDeviceID, kDeviceIDLength) == -1) {
3779       return -1;
3780     }
3781 
3782     if (wcsncmp(szDefaultDeviceID, szDeviceID, kDeviceIDLength) == 0) {
3783       // Found a match.
3784       *index = i;
3785       break;
3786     }
3787   }
3788 
3789   if (*index == -1) {
3790     RTC_LOG(LS_ERROR) << "Unable to find collection index for default device";
3791     return -1;
3792   }
3793 
3794   return 0;
3795 }
3796 
3797 // ----------------------------------------------------------------------------
3798 //  _GetDeviceName
3799 // ----------------------------------------------------------------------------
3800 
_GetDeviceName(IMMDevice * pDevice,LPWSTR pszBuffer,int bufferLen)3801 int32_t AudioDeviceWindowsCore::_GetDeviceName(IMMDevice* pDevice,
3802                                                LPWSTR pszBuffer,
3803                                                int bufferLen) {
3804   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3805 
3806   static const WCHAR szDefault[] = L"<Device not available>";
3807 
3808   HRESULT hr = E_FAIL;
3809   IPropertyStore* pProps = NULL;
3810   PROPVARIANT varName;
3811 
3812   RTC_DCHECK(pszBuffer);
3813   RTC_DCHECK_GT(bufferLen, 0);
3814 
3815   if (pDevice != NULL) {
3816     hr = pDevice->OpenPropertyStore(STGM_READ, &pProps);
3817     if (FAILED(hr)) {
3818       RTC_LOG(LS_ERROR) << "IMMDevice::OpenPropertyStore failed, hr = 0x"
3819                         << rtc::ToHex(hr);
3820     }
3821   }
3822 
3823   // Initialize container for property value.
3824   PropVariantInit(&varName);
3825 
3826   if (SUCCEEDED(hr)) {
3827     // Get the endpoint device's friendly-name property.
3828     hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName);
3829     if (FAILED(hr)) {
3830       RTC_LOG(LS_ERROR) << "IPropertyStore::GetValue failed, hr = 0x"
3831                         << rtc::ToHex(hr);
3832     }
3833   }
3834 
3835   if ((SUCCEEDED(hr)) && (VT_EMPTY == varName.vt)) {
3836     hr = E_FAIL;
3837     RTC_LOG(LS_ERROR) << "IPropertyStore::GetValue returned no value,"
3838                          " hr = 0x"
3839                       << rtc::ToHex(hr);
3840   }
3841 
3842   if ((SUCCEEDED(hr)) && (VT_LPWSTR != varName.vt)) {
3843     // The returned value is not a wide null terminated string.
3844     hr = E_UNEXPECTED;
3845     RTC_LOG(LS_ERROR) << "IPropertyStore::GetValue returned unexpected"
3846                          " type, hr = 0x"
3847                       << rtc::ToHex(hr);
3848   }
3849 
3850   if (SUCCEEDED(hr) && (varName.pwszVal != NULL)) {
3851     // Copy the valid device name to the provided ouput buffer.
3852     wcsncpy_s(pszBuffer, bufferLen, varName.pwszVal, _TRUNCATE);
3853   } else {
3854     // Failed to find the device name.
3855     wcsncpy_s(pszBuffer, bufferLen, szDefault, _TRUNCATE);
3856   }
3857 
3858   PropVariantClear(&varName);
3859   SAFE_RELEASE(pProps);
3860 
3861   return 0;
3862 }
3863 
3864 // ----------------------------------------------------------------------------
3865 //  _GetDeviceID
3866 // ----------------------------------------------------------------------------
3867 
_GetDeviceID(IMMDevice * pDevice,LPWSTR pszBuffer,int bufferLen)3868 int32_t AudioDeviceWindowsCore::_GetDeviceID(IMMDevice* pDevice,
3869                                              LPWSTR pszBuffer,
3870                                              int bufferLen) {
3871   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3872 
3873   static const WCHAR szDefault[] = L"<Device not available>";
3874 
3875   HRESULT hr = E_FAIL;
3876   LPWSTR pwszID = NULL;
3877 
3878   RTC_DCHECK(pszBuffer);
3879   RTC_DCHECK_GT(bufferLen, 0);
3880 
3881   if (pDevice != NULL) {
3882     hr = pDevice->GetId(&pwszID);
3883   }
3884 
3885   if (hr == S_OK) {
3886     // Found the device ID.
3887     wcsncpy_s(pszBuffer, bufferLen, pwszID, _TRUNCATE);
3888   } else {
3889     // Failed to find the device ID.
3890     wcsncpy_s(pszBuffer, bufferLen, szDefault, _TRUNCATE);
3891   }
3892 
3893   CoTaskMemFree(pwszID);
3894   return 0;
3895 }
3896 
3897 // ----------------------------------------------------------------------------
3898 //  _GetDefaultDevice
3899 // ----------------------------------------------------------------------------
3900 
_GetDefaultDevice(EDataFlow dir,ERole role,IMMDevice ** ppDevice)3901 int32_t AudioDeviceWindowsCore::_GetDefaultDevice(EDataFlow dir,
3902                                                   ERole role,
3903                                                   IMMDevice** ppDevice) {
3904   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3905 
3906   HRESULT hr(S_OK);
3907 
3908   RTC_DCHECK(_ptrEnumerator);
3909 
3910   hr = _ptrEnumerator->GetDefaultAudioEndpoint(dir, role, ppDevice);
3911   if (FAILED(hr)) {
3912     _TraceCOMError(hr);
3913     return -1;
3914   }
3915 
3916   return 0;
3917 }
3918 
3919 // ----------------------------------------------------------------------------
3920 //  _GetListDevice
3921 // ----------------------------------------------------------------------------
3922 
_GetListDevice(EDataFlow dir,int index,IMMDevice ** ppDevice)3923 int32_t AudioDeviceWindowsCore::_GetListDevice(EDataFlow dir,
3924                                                int index,
3925                                                IMMDevice** ppDevice) {
3926   HRESULT hr(S_OK);
3927 
3928   RTC_DCHECK(_ptrEnumerator);
3929 
3930   IMMDeviceCollection* pCollection = NULL;
3931 
3932   hr = _ptrEnumerator->EnumAudioEndpoints(
3933       dir,
3934       DEVICE_STATE_ACTIVE,  // only active endpoints are OK
3935       &pCollection);
3936   if (FAILED(hr)) {
3937     _TraceCOMError(hr);
3938     SAFE_RELEASE(pCollection);
3939     return -1;
3940   }
3941 
3942   hr = pCollection->Item(index, ppDevice);
3943   if (FAILED(hr)) {
3944     _TraceCOMError(hr);
3945     SAFE_RELEASE(pCollection);
3946     return -1;
3947   }
3948 
3949   SAFE_RELEASE(pCollection);
3950 
3951   return 0;
3952 }
3953 
3954 // ----------------------------------------------------------------------------
3955 //  _EnumerateEndpointDevicesAll
3956 // ----------------------------------------------------------------------------
3957 
_EnumerateEndpointDevicesAll(EDataFlow dataFlow) const3958 int32_t AudioDeviceWindowsCore::_EnumerateEndpointDevicesAll(
3959     EDataFlow dataFlow) const {
3960   RTC_DLOG(LS_VERBOSE) << __FUNCTION__;
3961 
3962   RTC_DCHECK(_ptrEnumerator);
3963 
3964   HRESULT hr = S_OK;
3965   IMMDeviceCollection* pCollection = NULL;
3966   IMMDevice* pEndpoint = NULL;
3967   IPropertyStore* pProps = NULL;
3968   IAudioEndpointVolume* pEndpointVolume = NULL;
3969   LPWSTR pwszID = NULL;
3970 
3971   // Generate a collection of audio endpoint devices in the system.
3972   // Get states for *all* endpoint devices.
3973   // Output: IMMDeviceCollection interface.
3974   hr = _ptrEnumerator->EnumAudioEndpoints(
3975       dataFlow,  // data-flow direction (input parameter)
3976       DEVICE_STATE_ACTIVE | DEVICE_STATE_DISABLED | DEVICE_STATE_UNPLUGGED,
3977       &pCollection);  // release interface when done
3978 
3979   EXIT_ON_ERROR(hr);
3980 
3981   // use the IMMDeviceCollection interface...
3982 
3983   UINT count = 0;
3984 
3985   // Retrieve a count of the devices in the device collection.
3986   hr = pCollection->GetCount(&count);
3987   EXIT_ON_ERROR(hr);
3988   if (dataFlow == eRender)
3989     RTC_LOG(LS_VERBOSE) << "#rendering endpoint devices (counting all): "
3990                         << count;
3991   else if (dataFlow == eCapture)
3992     RTC_LOG(LS_VERBOSE) << "#capturing endpoint devices (counting all): "
3993                         << count;
3994 
3995   if (count == 0) {
3996     return 0;
3997   }
3998 
3999   // Each loop prints the name of an endpoint device.
4000   for (ULONG i = 0; i < count; i++) {
4001     RTC_LOG(LS_VERBOSE) << "Endpoint " << i << ":";
4002 
4003     // Get pointer to endpoint number i.
4004     // Output: IMMDevice interface.
4005     hr = pCollection->Item(i, &pEndpoint);
4006     CONTINUE_ON_ERROR(hr);
4007 
4008     // use the IMMDevice interface of the specified endpoint device...
4009 
4010     // Get the endpoint ID string (uniquely identifies the device among all
4011     // audio endpoint devices)
4012     hr = pEndpoint->GetId(&pwszID);
4013     CONTINUE_ON_ERROR(hr);
4014     RTC_LOG(LS_VERBOSE) << "ID string    : " << pwszID;
4015 
4016     // Retrieve an interface to the device's property store.
4017     // Output: IPropertyStore interface.
4018     hr = pEndpoint->OpenPropertyStore(STGM_READ, &pProps);
4019     CONTINUE_ON_ERROR(hr);
4020 
4021     // use the IPropertyStore interface...
4022 
4023     PROPVARIANT varName;
4024     // Initialize container for property value.
4025     PropVariantInit(&varName);
4026 
4027     // Get the endpoint's friendly-name property.
4028     // Example: "Speakers (Realtek High Definition Audio)"
4029     hr = pProps->GetValue(PKEY_Device_FriendlyName, &varName);
4030     CONTINUE_ON_ERROR(hr);
4031     RTC_LOG(LS_VERBOSE) << "friendly name: \"" << varName.pwszVal << "\"";
4032 
4033     // Get the endpoint's current device state
4034     DWORD dwState;
4035     hr = pEndpoint->GetState(&dwState);
4036     CONTINUE_ON_ERROR(hr);
4037     if (dwState & DEVICE_STATE_ACTIVE)
4038       RTC_LOG(LS_VERBOSE) << "state (0x" << rtc::ToHex(dwState)
4039                           << ")  : *ACTIVE*";
4040     if (dwState & DEVICE_STATE_DISABLED)
4041       RTC_LOG(LS_VERBOSE) << "state (0x" << rtc::ToHex(dwState)
4042                           << ")  : DISABLED";
4043     if (dwState & DEVICE_STATE_NOTPRESENT)
4044       RTC_LOG(LS_VERBOSE) << "state (0x" << rtc::ToHex(dwState)
4045                           << ")  : NOTPRESENT";
4046     if (dwState & DEVICE_STATE_UNPLUGGED)
4047       RTC_LOG(LS_VERBOSE) << "state (0x" << rtc::ToHex(dwState)
4048                           << ")  : UNPLUGGED";
4049 
4050     // Check the hardware volume capabilities.
4051     DWORD dwHwSupportMask = 0;
4052     hr = pEndpoint->Activate(__uuidof(IAudioEndpointVolume), CLSCTX_ALL, NULL,
4053                              (void**)&pEndpointVolume);
4054     CONTINUE_ON_ERROR(hr);
4055     hr = pEndpointVolume->QueryHardwareSupport(&dwHwSupportMask);
4056     CONTINUE_ON_ERROR(hr);
4057     if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_VOLUME)
4058       // The audio endpoint device supports a hardware volume control
4059       RTC_LOG(LS_VERBOSE) << "hwmask (0x" << rtc::ToHex(dwHwSupportMask)
4060                           << ") : HARDWARE_SUPPORT_VOLUME";
4061     if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_MUTE)
4062       // The audio endpoint device supports a hardware mute control
4063       RTC_LOG(LS_VERBOSE) << "hwmask (0x" << rtc::ToHex(dwHwSupportMask)
4064                           << ") : HARDWARE_SUPPORT_MUTE";
4065     if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_METER)
4066       // The audio endpoint device supports a hardware peak meter
4067       RTC_LOG(LS_VERBOSE) << "hwmask (0x" << rtc::ToHex(dwHwSupportMask)
4068                           << ") : HARDWARE_SUPPORT_METER";
4069 
4070     // Check the channel count (#channels in the audio stream that enters or
4071     // leaves the audio endpoint device)
4072     UINT nChannelCount(0);
4073     hr = pEndpointVolume->GetChannelCount(&nChannelCount);
4074     CONTINUE_ON_ERROR(hr);
4075     RTC_LOG(LS_VERBOSE) << "#channels    : " << nChannelCount;
4076 
4077     if (dwHwSupportMask & ENDPOINT_HARDWARE_SUPPORT_VOLUME) {
4078       // Get the volume range.
4079       float fLevelMinDB(0.0);
4080       float fLevelMaxDB(0.0);
4081       float fVolumeIncrementDB(0.0);
4082       hr = pEndpointVolume->GetVolumeRange(&fLevelMinDB, &fLevelMaxDB,
4083                                            &fVolumeIncrementDB);
4084       CONTINUE_ON_ERROR(hr);
4085       RTC_LOG(LS_VERBOSE) << "volume range : " << fLevelMinDB << " (min), "
4086                           << fLevelMaxDB << " (max), " << fVolumeIncrementDB
4087                           << " (inc) [dB]";
4088 
4089       // The volume range from vmin = fLevelMinDB to vmax = fLevelMaxDB is
4090       // divided into n uniform intervals of size vinc = fVolumeIncrementDB,
4091       // where n = (vmax ?vmin) / vinc. The values vmin, vmax, and vinc are
4092       // measured in decibels. The client can set the volume level to one of n +
4093       // 1 discrete values in the range from vmin to vmax.
4094       int n = (int)((fLevelMaxDB - fLevelMinDB) / fVolumeIncrementDB);
4095       RTC_LOG(LS_VERBOSE) << "#intervals   : " << n;
4096 
4097       // Get information about the current step in the volume range.
4098       // This method represents the volume level of the audio stream that enters
4099       // or leaves the audio endpoint device as an index or "step" in a range of
4100       // discrete volume levels. Output value nStepCount is the number of steps
4101       // in the range. Output value nStep is the step index of the current
4102       // volume level. If the number of steps is n = nStepCount, then step index
4103       // nStep can assume values from 0 (minimum volume) to n ?1 (maximum
4104       // volume).
4105       UINT nStep(0);
4106       UINT nStepCount(0);
4107       hr = pEndpointVolume->GetVolumeStepInfo(&nStep, &nStepCount);
4108       CONTINUE_ON_ERROR(hr);
4109       RTC_LOG(LS_VERBOSE) << "volume steps : " << nStep << " (nStep), "
4110                           << nStepCount << " (nStepCount)";
4111     }
4112   Next:
4113     if (FAILED(hr)) {
4114       RTC_LOG(LS_VERBOSE) << "Error when logging device information";
4115     }
4116     CoTaskMemFree(pwszID);
4117     pwszID = NULL;
4118     PropVariantClear(&varName);
4119     SAFE_RELEASE(pProps);
4120     SAFE_RELEASE(pEndpoint);
4121     SAFE_RELEASE(pEndpointVolume);
4122   }
4123   SAFE_RELEASE(pCollection);
4124   return 0;
4125 
4126 Exit:
4127   _TraceCOMError(hr);
4128   CoTaskMemFree(pwszID);
4129   pwszID = NULL;
4130   SAFE_RELEASE(pCollection);
4131   SAFE_RELEASE(pEndpoint);
4132   SAFE_RELEASE(pEndpointVolume);
4133   SAFE_RELEASE(pProps);
4134   return -1;
4135 }
4136 
4137 // ----------------------------------------------------------------------------
4138 //  _TraceCOMError
4139 // ----------------------------------------------------------------------------
4140 
_TraceCOMError(HRESULT hr) const4141 void AudioDeviceWindowsCore::_TraceCOMError(HRESULT hr) const {
4142   wchar_t buf[MAXERRORLENGTH];
4143   wchar_t errorText[MAXERRORLENGTH];
4144 
4145   const DWORD dwFlags =
4146       FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
4147   const DWORD dwLangID = MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US);
4148 
4149   // Gets the system's human readable message string for this HRESULT.
4150   // All error message in English by default.
4151   DWORD messageLength = ::FormatMessageW(dwFlags, 0, hr, dwLangID, errorText,
4152                                          MAXERRORLENGTH, NULL);
4153 
4154   RTC_DCHECK_LE(messageLength, MAXERRORLENGTH);
4155 
4156   // Trims tailing white space (FormatMessage() leaves a trailing cr-lf.).
4157   for (; messageLength && ::isspace(errorText[messageLength - 1]);
4158        --messageLength) {
4159     errorText[messageLength - 1] = '\0';
4160   }
4161 
4162   RTC_LOG(LS_ERROR) << "Core Audio method failed (hr=" << hr << ")";
4163   StringCchPrintfW(buf, MAXERRORLENGTH, L"Error details: ");
4164   StringCchCatW(buf, MAXERRORLENGTH, errorText);
4165   RTC_LOG(LS_ERROR) << rtc::ToUtf8(buf);
4166 }
4167 
KeyPressed() const4168 bool AudioDeviceWindowsCore::KeyPressed() const {
4169   int key_down = 0;
4170   for (int key = VK_SPACE; key < VK_NUMLOCK; key++) {
4171     short res = GetAsyncKeyState(key);
4172     key_down |= res & 0x1;  // Get the LSB
4173   }
4174   return (key_down > 0);
4175 }
4176 }  // namespace webrtc
4177 
4178 #endif  // WEBRTC_WINDOWS_CORE_AUDIO_BUILD
4179