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