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