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