上一节《7-17 播放声音的具体流程

AudioState::AddReceivingStream

H:\webrtc-20210315\webrtc-20210315\webrtc\webrtc-checkout\src\audio\audio_state.cc

  1. void AudioState::AddReceivingStream(webrtc::AudioReceiveStream* stream) {
  2. ***
  3. auto* adm = config_.audio_device_module.get();
  4. if (!adm->Playing()) {
  5. if (adm->InitPlayout() == 0) {
  6. if (playout_enabled_) {
  7. adm->StartPlayout();
  8. ***
  9. }

AudioDeviceModuleImpl::StartPlayout

H:\webrtc-20210315\webrtc-20210315\webrtc\webrtc-checkout\src\modules\audio_device\audio_device_impl.cc

  1. int32_t AudioDeviceModuleImpl::StartPlayout() {
  2. RTC_LOG(INFO) << __FUNCTION__;
  3. CHECKinitialized_();
  4. if (Playing()) {
  5. return 0;
  6. }
  7. audio_device_buffer_.StartPlayout();
  8. int32_t result = audio_device_->StartPlayout();
  9. RTC_LOG(INFO) << "output: " << result;
  10. RTC_HISTOGRAM_BOOLEAN("WebRTC.Audio.StartPlayoutSuccess",
  11. static_cast<int>(result == 0));
  12. return result;
  13. }

AudioDeviceWindowsCore::StartPlayout

H:\webrtc-20210315\webrtc-20210315\webrtc\webrtc-checkout\src\modules\audio_device\win\audio_device_core_win.cc
播放声音的主要逻辑都是在这里完成。

  1. int32_t AudioDeviceWindowsCore::StartPlayout() {
  2. if (!_playIsInitialized) {
  3. return -1;
  4. }
  5. if (_hPlayThread != NULL) {
  6. return 0;
  7. }
  8. if (_playing) {
  9. return 0;
  10. }
  11. {
  12. MutexLock lockScoped(&mutex_);
  13. // Create thread which will drive the rendering.
  14. assert(_hPlayThread == NULL);
  15. _hPlayThread = CreateThread(NULL, 0, WSAPIRenderThread, this, 0, NULL);
  16. if (_hPlayThread == NULL) {
  17. RTC_LOG(LS_ERROR) << "failed to create the playout thread";
  18. return -1;
  19. }
  20. // Set thread priority to highest possible.
  21. SetThreadPriority(_hPlayThread, THREAD_PRIORITY_TIME_CRITICAL);
  22. } // critScoped
  23. DWORD ret = WaitForSingleObject(_hRenderStartedEvent, 1000);
  24. if (ret != WAIT_OBJECT_0) {
  25. RTC_LOG(LS_VERBOSE) << "rendering did not start up properly";
  26. return -1;
  27. }
  28. _playing = true;
  29. RTC_LOG(LS_VERBOSE) << "rendering audio stream has now started...";
  30. return 0;
  31. }
  32. DWORD WINAPI AudioDeviceWindowsCore::WSAPIRenderThread(LPVOID context) {
  33. return reinterpret_cast<AudioDeviceWindowsCore*>(context)->DoRenderThread();
  34. }

image.png

WSAPIRenderThread线程函数

  1. DWORD WINAPI AudioDeviceWindowsCore::WSAPIRenderThread(LPVOID context) {
  2. return reinterpret_cast<AudioDeviceWindowsCore*>(context)->DoRenderThread();
  3. }

AudioDeviceWindowsCore::DoRenderThread

  1. DWORD AudioDeviceWindowsCore::DoRenderThread() {
  2. ***
  3. // Initialize COM as MTA in this thread.
  4. ScopedCOMInitializer comInit(ScopedCOMInitializer::kMTA);
  5. if (!comInit.Succeeded()) {
  6. RTC_LOG(LS_ERROR) << "failed to initialize COM in render thread";
  7. return 1;
  8. }
  9. rtc::SetCurrentThreadName("webrtc_core_audio_render_thread");
  10. // Use Multimedia Class Scheduler Service (MMCSS) to boost the thread
  11. // priority.
  12. // 是否支持avrt
  13. if (_winSupportAvrt) {
  14. DWORD taskIndex(0);
  15. hMmTask = _PAvSetMmThreadCharacteristicsA("Pro Audio", &taskIndex);
  16. if (hMmTask) {
  17. if (FALSE == _PAvSetMmThreadPriority(hMmTask, AVRT_PRIORITY_CRITICAL)) {
  18. ****
  19. }
  20. _Lock();
  21. IAudioClock* clock = NULL;
  22. // Get size of rendering buffer (length is expressed as the number of audio
  23. // frames the buffer can hold). This value is fixed during the rendering
  24. // session.
  25. // 获取扬声器的buffer大小,断点值为1056
  26. UINT32 bufferLength = 0;
  27. hr = _ptrClientOut->GetBufferSize(&bufferLength);
  28. EXIT_ON_ERROR(hr);
  29. RTC_LOG(LS_VERBOSE) << "[REND] size of buffer : " << bufferLength;
  30. // Get maximum latency for the current stream (will not change for the
  31. // lifetime of the IAudioClient object).
  32. //
  33. REFERENCE_TIME latency;
  34. _ptrClientOut->GetStreamLatency(&latency); //打断点值为0,没有延迟
  35. RTC_LOG(LS_VERBOSE) << "[REND] max stream latency : " << (DWORD)latency
  36. << " (" << (double)(latency / 10000.0) << " ms)";
  37. // Get the length of the periodic interval separating successive processing
  38. // passes by the audio engine on the data in the endpoint buffer.
  39. //
  40. // The period between processing passes by the audio engine is fixed for a
  41. // particular audio endpoint device and represents the smallest processing
  42. // quantum for the audio engine. This period plus the stream latency between
  43. // the buffer and endpoint device represents the minimum possible latency that
  44. // an audio application can achieve. Typical value: 100000 <=> 0.01 sec =
  45. // 10ms.
  46. // 播放周期
  47. REFERENCE_TIME devPeriod = 0;
  48. REFERENCE_TIME devPeriodMin = 0;
  49. _ptrClientOut->GetDevicePeriod(&devPeriod, &devPeriodMin);//断点值, 10万,3万
  50. RTC_LOG(LS_VERBOSE) << "[REND] device period : " << (DWORD)devPeriod
  51. << " (" << (double)(devPeriod / 10000.0) << " ms)";
  52. // Derive initial rendering delay.
  53. // Example: 10*(960/480) + 15 = 20 + 15 = 35ms
  54. // 这里的10是10毫秒,播放缓冲区大小,当前断点值 10* (1056/480) + (0+ 10万)/1万 = 30
  55. int playout_delay = 10 * (bufferLength / _playBlockSize) +
  56. (int)((latency + devPeriod) / 10000);
  57. _sndCardPlayDelay = playout_delay;
  58. _writtenSamples = 0;
  59. RTC_LOG(LS_VERBOSE) << "[REND] initial delay : " << playout_delay;
  60. // 缓存区可以播放的时间
  61. double endpointBufferSizeMS =
  62. 10.0 * ((double)bufferLength / (double)_devicePlayBlockSize);
  63. RTC_LOG(LS_VERBOSE) << "[REND] endpointBufferSizeMS : "
  64. << endpointBufferSizeMS;
  65. // Before starting the stream, fill the rendering buffer with silence.
  66. //获取缓存区的地址,后面将数据写到这里
  67. BYTE* pData = NULL;
  68. hr = _ptrRenderClient->GetBuffer(bufferLength, &pData);
  69. EXIT_ON_ERROR(hr);
  70. // 释放缓存区,同时默认写入静默音
  71. hr =
  72. _ptrRenderClient->ReleaseBuffer(bufferLength, AUDCLNT_BUFFERFLAGS_SILENT);
  73. EXIT_ON_ERROR(hr);
  74. _writtenSamples += bufferLength;
  75. // 获取IAudioClock服务
  76. hr = _ptrClientOut->GetService(__uuidof(IAudioClock), (void**)&clock);
  77. if (FAILED(hr)) {
  78. RTC_LOG(LS_WARNING)
  79. << "failed to get IAudioClock interface from the IAudioClient";
  80. }
  81. // 启动音频流
  82. // Start up the rendering audio stream.
  83. hr = _ptrClientOut->Start();
  84. EXIT_ON_ERROR(hr);
  85. _UnLock();
  86. // Set event which will ensure that the calling thread modifies the playing
  87. // state to true.
  88. // 发送一个开始播放的通知
  89. SetEvent(_hRenderStartedEvent);
  90. // >> ------------------ THREAD LOOP ------------------
  91. while (keepPlaying) {
  92. // Wait for a render notification event or a shutdown event
  93. // 等待信号和解析
  94. DWORD waitResult = WaitForMultipleObjects(2, waitArray, FALSE, 500);
  95. switch (waitResult) {
  96. case WAIT_OBJECT_0 + 0: // _hShutdownRenderEvent
  97. keepPlaying = false;
  98. break;
  99. case WAIT_OBJECT_0 + 1: // _hRenderSamplesReadyEvent
  100. break;
  101. case WAIT_TIMEOUT: // timeout notification
  102. RTC_LOG(LS_WARNING) << "render event timed out after 0.5 seconds";
  103. goto Exit;
  104. default: // unexpected error
  105. RTC_LOG(LS_WARNING) << "unknown wait termination on render side";
  106. goto Exit;
  107. }
  108. while (keepPlaying) {
  109. _Lock();
  110. // Sanity check to ensure that essential states are not modified
  111. // during the unlocked period.
  112. if (_ptrRenderClient == NULL || _ptrClientOut == NULL) {
  113. _UnLock();
  114. RTC_LOG(LS_ERROR)
  115. << "output state has been modified during unlocked period";
  116. goto Exit;
  117. }
  118. // Get the number of frames of padding (queued up to play) in the endpoint
  119. // buffer. 获取要填充的大小
  120. UINT32 padding = 0;
  121. hr = _ptrClientOut->GetCurrentPadding(&padding);
  122. EXIT_ON_ERROR(hr);
  123. // Derive the amount of available space in the output buffer
  124. uint32_t framesAvailable = bufferLength - padding;
  125. // Do we have 10 ms available in the render buffer?
  126. if (framesAvailable < _playBlockSize) { // 没有空间填写
  127. // Not enough space in render buffer to store next render packet.
  128. _UnLock();
  129. break;
  130. }
  131. // Write n*10ms buffers to the render buffer
  132. const uint32_t n10msBuffers = (framesAvailable / _playBlockSize);
  133. for (uint32_t n = 0; n < n10msBuffers; n++) {
  134. // Get pointer (i.e., grab the buffer) to next space in the shared
  135. // render buffer.
  136. hr = _ptrRenderClient->GetBuffer(_playBlockSize, &pData);
  137. EXIT_ON_ERROR(hr);
  138. if (_ptrAudioBuffer) {
  139. // Request data to be played out (#bytes =
  140. // _playBlockSize*_audioFrameSize)
  141. _UnLock();
  142. int32_t nSamples =
  143. _ptrAudioBuffer->RequestPlayoutData(_playBlockSize); // 获取播放数据大小
  144. _Lock();
  145. if (nSamples == -1) {
  146. _UnLock();
  147. RTC_LOG(LS_ERROR) << "failed to read data from render client";
  148. goto Exit;
  149. }
  150. // Sanity check to ensure that essential states are not modified
  151. // during the unlocked period
  152. if (_ptrRenderClient == NULL || _ptrClientOut == NULL) {
  153. _UnLock();
  154. RTC_LOG(LS_ERROR)
  155. << "output state has been modified during unlocked"
  156. " period";
  157. goto Exit;
  158. }
  159. if (nSamples != static_cast<int32_t>(_playBlockSize)) {
  160. RTC_LOG(LS_WARNING)
  161. << "nSamples(" << nSamples << ") != _playBlockSize"
  162. << _playBlockSize << ")";
  163. }
  164. // Get the actual (stored) data
  165. // 将_ptrAudioBuffer的数据读取放到扬声器的bufer中,扬声器会不停的去读来播放
  166. nSamples = _ptrAudioBuffer->GetPlayoutData((int8_t*)pData);
  167. }
  168. DWORD dwFlags(0);
  169. hr = _ptrRenderClient->ReleaseBuffer(_playBlockSize, dwFlags);
  170. // See http://msdn.microsoft.com/en-us/library/dd316605(VS.85).aspx
  171. // for more details regarding AUDCLNT_E_DEVICE_INVALIDATED.
  172. EXIT_ON_ERROR(hr);
  173. // 写入的大小要递增
  174. _writtenSamples += _playBlockSize;
  175. }
  176. // Check the current delay on the playout side.
  177. if (clock) {
  178. UINT64 pos = 0;
  179. UINT64 freq = 1;
  180. clock->GetPosition(&pos, NULL);
  181. clock->GetFrequency(&freq);
  182. playout_delay = ROUND((double(_writtenSamples) / _devicePlaySampleRate -
  183. double(pos) / freq) *
  184. 1000.0);
  185. // 每次播放完都要更新该值,时刻获取播放延迟时间
  186. _sndCardPlayDelay = playout_delay;
  187. }
  188. _UnLock();
  189. }
  190. }
  191. // ------------------ THREAD LOOP ------------------ <<
  192. SleepMs(static_cast<DWORD>(endpointBufferSizeMS + 0.5));
  193. hr = _ptrClientOut->Stop();
  194. ****
  195. }