前言概念

WindowsCore的构造函数

image.png

STA与MTA

image.png
STA: Single-Thread Apartment, 中文叫单线程套间。就是在COM库初始化的时候创建一个内存结构,然后让它和调用CoInitialize的线程相关联。这个内存结构针对每个线程都会有一个。支持STA的COM对象只能在创建它的线程里被使用,其它线程如果再创建它就会失败。

MTA: Mutil-Thread Apartment,中文叫多线程套间。COM库在进程中创建一个内存结构,这个内存结构在整个进程中只能有一个,然后让它和调用CoInitializeEx的线程相关联。支持MTA的COM对象可以在任意线程里被使用。多有针对它的调用都会被封装成为消息。

其实STA和MTA是COM规定的一套线程模型,用于保障多线程情况下你的组件代码的同步。比如说有一个COM对象它内部有一个静态变量 gHello,那么这个对象无论生成多少实例对于gHello在内存中只能有一份,那么如果有两个不同的实例在两个线程里面同时去读写它,就有可能出错,所以就要就要有种机制进行同步保护,STA或者MTA就是这种机制。

AVRT库

image.png

DMO

image.png

源码分析

WebRtcVoiceEngine::Init

直接打断点
H:\webrtc-20210315\webrtc-20210315\webrtc\webrtc-checkout\src\media\engine\webrtc_voice_engine.cc
WebRtcVoiceEngine::Init
image.png
调用堆栈

  1. cricket::ChannelManager::Init
  2. ->
  3. cricket::CompositeMediaEngine::Init()
  4. ->
  5. cricket::WebRtcVoiceEngine::Init()

AudioDeviceWindowsCore::AudioDeviceWindowsCore

打断点
H:\webrtc-20210315\webrtc-20210315\webrtc\webrtc-checkout\src\modules\audio_device\win\audio_device_core_win.cc 构造函数的398行
image.png
调用堆栈
image.png

  1. AudioDeviceWindowsCore::AudioDeviceWindowsCore()
  2. : _avrtLibrary(NULL),
  3. _winSupportAvrt(false),
  4. _comInit(ScopedCOMInitializer::kMTA),
  5. _ptrAudioBuffer(NULL),
  6. _ptrEnumerator(NULL),
  7. _ptrRenderCollection(NULL),
  8. _ptrCaptureCollection(NULL),
  9. _ptrDeviceOut(NULL),
  10. _ptrDeviceIn(NULL),
  11. _ptrClientOut(NULL),
  12. _ptrClientIn(NULL),
  13. _ptrRenderClient(NULL),
  14. _ptrCaptureClient(NULL),
  15. _ptrCaptureVolume(NULL),
  16. _ptrRenderSimpleVolume(NULL),
  17. _dmo(NULL),
  18. _mediaBuffer(NULL),
  19. _builtInAecEnabled(false),
  20. _hRenderSamplesReadyEvent(NULL),
  21. _hPlayThread(NULL),
  22. _hRenderStartedEvent(NULL),
  23. _hShutdownRenderEvent(NULL),
  24. _hCaptureSamplesReadyEvent(NULL),
  25. _hRecThread(NULL),
  26. _hCaptureStartedEvent(NULL),
  27. _hShutdownCaptureEvent(NULL),
  28. _hMmTask(NULL),
  29. _playAudioFrameSize(0),
  30. _playSampleRate(0),
  31. _playBlockSize(0),
  32. _playChannels(2),
  33. _sndCardPlayDelay(0),
  34. _writtenSamples(0),
  35. _readSamples(0),
  36. _recAudioFrameSize(0),
  37. _recSampleRate(0),
  38. _recBlockSize(0),
  39. _recChannels(2),
  40. _initialized(false),
  41. _recording(false),
  42. _playing(false),
  43. _recIsInitialized(false),
  44. _playIsInitialized(false),
  45. _speakerIsInitialized(false),
  46. _microphoneIsInitialized(false),
  47. _usingInputDeviceIndex(false),
  48. _usingOutputDeviceIndex(false),
  49. _inputDevice(AudioDeviceModule::kDefaultCommunicationDevice),
  50. _outputDevice(AudioDeviceModule::kDefaultCommunicationDevice),
  51. _inputDeviceIndex(0),
  52. _outputDeviceIndex(0) {
  53. RTC_DLOG(LS_INFO) << __FUNCTION__ << " created";
  54. RTC_DCHECK(_comInit.Succeeded());
  55. // Try to load the Avrt DLL
  56. if (!_avrtLibrary) {
  57. // Get handle to the Avrt DLL module.
  58. _avrtLibrary = LoadLibrary(TEXT("Avrt.dll"));
  59. if (_avrtLibrary) {
  60. // Handle is valid (should only happen if OS larger than vista & win7).
  61. // Try to get the function addresses.
  62. RTC_LOG(LS_VERBOSE) << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
  63. " The Avrt DLL module is now loaded";
  64. // 获取三个方法
  65. _PAvRevertMmThreadCharacteristics =
  66. (PAvRevertMmThreadCharacteristics)GetProcAddress(
  67. _avrtLibrary, "AvRevertMmThreadCharacteristics");
  68. _PAvSetMmThreadCharacteristicsA =
  69. (PAvSetMmThreadCharacteristicsA)GetProcAddress(
  70. _avrtLibrary, "AvSetMmThreadCharacteristicsA");
  71. _PAvSetMmThreadPriority = (PAvSetMmThreadPriority)GetProcAddress(
  72. _avrtLibrary, "AvSetMmThreadPriority");
  73. if (_PAvRevertMmThreadCharacteristics &&
  74. _PAvSetMmThreadCharacteristicsA && _PAvSetMmThreadPriority) {
  75. RTC_LOG(LS_VERBOSE)
  76. << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
  77. " AvRevertMmThreadCharacteristics() is OK";
  78. RTC_LOG(LS_VERBOSE)
  79. << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
  80. " AvSetMmThreadCharacteristicsA() is OK";
  81. RTC_LOG(LS_VERBOSE)
  82. << "AudioDeviceWindowsCore::AudioDeviceWindowsCore()"
  83. " AvSetMmThreadPriority() is OK";
  84. // 设置标志
  85. _winSupportAvrt = true;
  86. }
  87. }
  88. }
  89. // Create our samples ready events - we want auto reset events that start in
  90. // the not-signaled state. The state of an auto-reset event object remains
  91. // signaled until a single waiting thread is released, at which time the
  92. // system automatically sets the state to nonsignaled. If no threads are
  93. // waiting, the event object's state remains signaled. (Except for
  94. // _hShutdownCaptureEvent, which is used to shutdown multiple threads).
  95. _hRenderSamplesReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  96. _hCaptureSamplesReadyEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  97. _hShutdownRenderEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  98. _hShutdownCaptureEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
  99. _hRenderStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  100. _hCaptureStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  101. _perfCounterFreq.QuadPart = 1;
  102. _perfCounterFactor = 0.0;
  103. // list of number of channels to use on recording side
  104. _recChannelsPrioList[0] = 2; // stereo is prio 1
  105. _recChannelsPrioList[1] = 1; // mono is prio 2
  106. _recChannelsPrioList[2] = 4; // quad is prio 3
  107. // list of number of channels to use on playout side
  108. _playChannelsPrioList[0] = 2; // stereo is prio 1
  109. _playChannelsPrioList[1] = 1; // mono is prio 2
  110. HRESULT hr;
  111. // We know that this API will work since it has already been verified in
  112. // CoreAudioIsSupported, hence no need to check for errors here as well.
  113. // Retrive the IMMDeviceEnumerator API (should load the MMDevAPI.dll)
  114. // TODO(henrika): we should probably move this allocation to Init() instead
  115. // and deallocate in Terminate() to make the implementation more symmetric.
  116. CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
  117. __uuidof(IMMDeviceEnumerator),
  118. reinterpret_cast<void**>(&_ptrEnumerator));
  119. assert(NULL != _ptrEnumerator);
  120. // DMO初始化,主要用于回音消除,硬件层次。如果是软件层次,就是WebRTC的算法了。
  121. // 苹果手机,硬件。。Windows一般使用软件。
  122. // DMO initialization for built-in WASAPI AEC.
  123. {
  124. IMediaObject* ptrDMO = NULL;
  125. hr = CoCreateInstance(CLSID_CWMAudioAEC, NULL, CLSCTX_INPROC_SERVER,
  126. IID_IMediaObject, reinterpret_cast<void**>(&ptrDMO));
  127. if (FAILED(hr) || ptrDMO == NULL) {
  128. // Since we check that _dmo is non-NULL in EnableBuiltInAEC(), the
  129. // feature is prevented from being enabled.
  130. _builtInAecEnabled = false;
  131. _TraceCOMError(hr);
  132. }
  133. _dmo = ptrDMO;
  134. SAFE_RELEASE(ptrDMO);
  135. }
  136. }

其中ScopedCOMInitializer _comInit;
去到该类的定义去,可以发现,选择的是MTA 多线程套间。
class ScopedCOMInitializer {

public:
enum SelectMTA { kMTA };
explicit ScopedCOMInitializer(SelectMTA mta);

}

学习资料

STA和MTA线程模式的区别