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