1 /*
2  *  Copyright (c) 2018 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 #include "modules/audio_device/win/core_audio_output_win.h"
12 
13 #include <memory>
14 
15 #include "modules/audio_device/audio_device_buffer.h"
16 #include "modules/audio_device/fine_audio_buffer.h"
17 #include "rtc_base/checks.h"
18 #include "rtc_base/logging.h"
19 #include "rtc_base/time_utils.h"
20 
21 using Microsoft::WRL::ComPtr;
22 
23 namespace webrtc {
24 namespace webrtc_win {
25 
CoreAudioOutput(bool automatic_restart)26 CoreAudioOutput::CoreAudioOutput(bool automatic_restart)
27     : CoreAudioBase(
28           CoreAudioBase::Direction::kOutput,
29           automatic_restart,
30           [this](uint64_t freq) { return OnDataCallback(freq); },
__anon4344cdc50202(ErrorType err) 31           [this](ErrorType err) { return OnErrorCallback(err); }) {
32   RTC_DLOG(INFO) << __FUNCTION__;
33   RTC_DCHECK_RUN_ON(&thread_checker_);
34   thread_checker_audio_.Detach();
35 }
36 
~CoreAudioOutput()37 CoreAudioOutput::~CoreAudioOutput() {
38   RTC_DLOG(INFO) << __FUNCTION__;
39   RTC_DCHECK_RUN_ON(&thread_checker_);
40   Terminate();
41 }
42 
Init()43 int CoreAudioOutput::Init() {
44   RTC_DLOG(INFO) << __FUNCTION__;
45   RTC_DCHECK_RUN_ON(&thread_checker_);
46   return 0;
47 }
48 
Terminate()49 int CoreAudioOutput::Terminate() {
50   RTC_DLOG(INFO) << __FUNCTION__;
51   RTC_DCHECK_RUN_ON(&thread_checker_);
52   StopPlayout();
53   return 0;
54 }
55 
NumDevices() const56 int CoreAudioOutput::NumDevices() const {
57   RTC_DCHECK_RUN_ON(&thread_checker_);
58   return core_audio_utility::NumberOfActiveDevices(eRender);
59 }
60 
SetDevice(int index)61 int CoreAudioOutput::SetDevice(int index) {
62   RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
63   RTC_DCHECK_GE(index, 0);
64   RTC_DCHECK_RUN_ON(&thread_checker_);
65   return CoreAudioBase::SetDevice(index);
66 }
67 
SetDevice(AudioDeviceModule::WindowsDeviceType device)68 int CoreAudioOutput::SetDevice(AudioDeviceModule::WindowsDeviceType device) {
69   RTC_DLOG(INFO) << __FUNCTION__ << ": "
70                  << ((device == AudioDeviceModule::kDefaultDevice)
71                          ? "Default"
72                          : "DefaultCommunication");
73   RTC_DCHECK_RUN_ON(&thread_checker_);
74   return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1);
75 }
76 
DeviceName(int index,std::string * name,std::string * guid)77 int CoreAudioOutput::DeviceName(int index,
78                                 std::string* name,
79                                 std::string* guid) {
80   RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
81   RTC_DCHECK_RUN_ON(&thread_checker_);
82   RTC_DCHECK(name);
83   return CoreAudioBase::DeviceName(index, name, guid);
84 }
85 
AttachAudioBuffer(AudioDeviceBuffer * audio_buffer)86 void CoreAudioOutput::AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) {
87   RTC_DLOG(INFO) << __FUNCTION__;
88   RTC_DCHECK_RUN_ON(&thread_checker_);
89   audio_device_buffer_ = audio_buffer;
90 }
91 
PlayoutIsInitialized() const92 bool CoreAudioOutput::PlayoutIsInitialized() const {
93   RTC_DLOG(INFO) << __FUNCTION__;
94   RTC_DCHECK_RUN_ON(&thread_checker_);
95   return initialized_;
96 }
97 
InitPlayout()98 int CoreAudioOutput::InitPlayout() {
99   RTC_DLOG(INFO) << __FUNCTION__ << ": " << IsRestarting();
100   RTC_DCHECK(!initialized_);
101   RTC_DCHECK(!Playing());
102   RTC_DCHECK(!audio_render_client_);
103 
104   // Creates an IAudioClient instance and stores the valid interface pointer in
105   // |audio_client3_|, |audio_client2_|, or |audio_client_| depending on
106   // platform support. The base class will use optimal output parameters and do
107   // an event driven shared mode initialization. The utilized format will be
108   // stored in |format_| and can be used for configuration and allocation of
109   // audio buffers.
110   if (!CoreAudioBase::Init()) {
111     return -1;
112   }
113   RTC_DCHECK(audio_client_);
114 
115   // Configure the playout side of the audio device buffer using |format_|
116   // after a trivial sanity check of the format structure.
117   RTC_DCHECK(audio_device_buffer_);
118   WAVEFORMATEX* format = &format_.Format;
119   RTC_DCHECK_EQ(format->wFormatTag, WAVE_FORMAT_EXTENSIBLE);
120   audio_device_buffer_->SetPlayoutSampleRate(format->nSamplesPerSec);
121   audio_device_buffer_->SetPlayoutChannels(format->nChannels);
122 
123   // Create a modified audio buffer class which allows us to ask for any number
124   // of samples (and not only multiple of 10ms) to match the optimal
125   // buffer size per callback used by Core Audio.
126   // TODO(henrika): can we share one FineAudioBuffer with the input side?
127   fine_audio_buffer_ = std::make_unique<FineAudioBuffer>(audio_device_buffer_);
128 
129   // Create an IAudioRenderClient for an initialized IAudioClient.
130   // The IAudioRenderClient interface enables us to write output data to
131   // a rendering endpoint buffer.
132   ComPtr<IAudioRenderClient> audio_render_client =
133       core_audio_utility::CreateRenderClient(audio_client_.Get());
134   if (!audio_render_client.Get()) {
135     return -1;
136   }
137 
138   ComPtr<IAudioClock> audio_clock =
139       core_audio_utility::CreateAudioClock(audio_client_.Get());
140   if (!audio_clock.Get()) {
141     return -1;
142   }
143 
144   // Store valid COM interfaces.
145   audio_render_client_ = audio_render_client;
146   audio_clock_ = audio_clock;
147 
148   initialized_ = true;
149   return 0;
150 }
151 
StartPlayout()152 int CoreAudioOutput::StartPlayout() {
153   RTC_DLOG(INFO) << __FUNCTION__ << ": " << IsRestarting();
154   RTC_DCHECK(!Playing());
155   RTC_DCHECK(fine_audio_buffer_);
156   RTC_DCHECK(audio_device_buffer_);
157   if (!initialized_) {
158     RTC_DLOG(LS_WARNING)
159         << "Playout can not start since InitPlayout must succeed first";
160   }
161 
162   fine_audio_buffer_->ResetPlayout();
163   if (!IsRestarting()) {
164     audio_device_buffer_->StartPlayout();
165   }
166 
167   if (!core_audio_utility::FillRenderEndpointBufferWithSilence(
168           audio_client_.Get(), audio_render_client_.Get())) {
169     RTC_LOG(LS_WARNING) << "Failed to prepare output endpoint with silence";
170   }
171 
172   num_frames_written_ = endpoint_buffer_size_frames_;
173 
174   if (!Start()) {
175     return -1;
176   }
177 
178   is_active_ = true;
179   return 0;
180 }
181 
StopPlayout()182 int CoreAudioOutput::StopPlayout() {
183   RTC_DLOG(INFO) << __FUNCTION__ << ": " << IsRestarting();
184   if (!initialized_) {
185     return 0;
186   }
187 
188   // Release resources allocated in InitPlayout() and then return if this
189   // method is called without any active output audio.
190   if (!Playing()) {
191     RTC_DLOG(WARNING) << "No output stream is active";
192     ReleaseCOMObjects();
193     initialized_ = false;
194     return 0;
195   }
196 
197   if (!Stop()) {
198     RTC_LOG(LS_ERROR) << "StopPlayout failed";
199     return -1;
200   }
201 
202   if (!IsRestarting()) {
203     RTC_DCHECK(audio_device_buffer_);
204     audio_device_buffer_->StopPlayout();
205   }
206 
207   // Release all allocated resources to allow for a restart without
208   // intermediate destruction.
209   ReleaseCOMObjects();
210 
211   initialized_ = false;
212   is_active_ = false;
213   return 0;
214 }
215 
Playing()216 bool CoreAudioOutput::Playing() {
217   RTC_DLOG(INFO) << __FUNCTION__ << ": " << is_active_;
218   return is_active_;
219 }
220 
221 // TODO(henrika): finalize support of audio session volume control. As is, we
222 // are not compatible with the old ADM implementation since it allows accessing
223 // the volume control with any active audio output stream.
VolumeIsAvailable(bool * available)224 int CoreAudioOutput::VolumeIsAvailable(bool* available) {
225   RTC_DLOG(INFO) << __FUNCTION__;
226   RTC_DCHECK_RUN_ON(&thread_checker_);
227   return IsVolumeControlAvailable(available) ? 0 : -1;
228 }
229 
230 // Triggers the restart sequence. Only used for testing purposes to emulate
231 // a real event where e.g. an active output device is removed.
RestartPlayout()232 int CoreAudioOutput::RestartPlayout() {
233   RTC_DLOG(INFO) << __FUNCTION__;
234   RTC_DCHECK_RUN_ON(&thread_checker_);
235   if (!Playing()) {
236     return 0;
237   }
238   if (!Restart()) {
239     RTC_LOG(LS_ERROR) << "RestartPlayout failed";
240     return -1;
241   }
242   return 0;
243 }
244 
Restarting() const245 bool CoreAudioOutput::Restarting() const {
246   RTC_DLOG(INFO) << __FUNCTION__;
247   RTC_DCHECK_RUN_ON(&thread_checker_);
248   return IsRestarting();
249 }
250 
SetSampleRate(uint32_t sample_rate)251 int CoreAudioOutput::SetSampleRate(uint32_t sample_rate) {
252   RTC_DLOG(INFO) << __FUNCTION__;
253   RTC_DCHECK_RUN_ON(&thread_checker_);
254   sample_rate_ = sample_rate;
255   return 0;
256 }
257 
ReleaseCOMObjects()258 void CoreAudioOutput::ReleaseCOMObjects() {
259   RTC_DLOG(INFO) << __FUNCTION__;
260   CoreAudioBase::ReleaseCOMObjects();
261   if (audio_render_client_.Get()) {
262     audio_render_client_.Reset();
263   }
264 }
265 
OnErrorCallback(ErrorType error)266 bool CoreAudioOutput::OnErrorCallback(ErrorType error) {
267   RTC_DLOG(INFO) << __FUNCTION__ << ": " << as_integer(error);
268   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
269   if (!initialized_ || !Playing()) {
270     return true;
271   }
272 
273   if (error == CoreAudioBase::ErrorType::kStreamDisconnected) {
274     HandleStreamDisconnected();
275   } else {
276     RTC_DLOG(WARNING) << "Unsupported error type";
277   }
278   return true;
279 }
280 
OnDataCallback(uint64_t device_frequency)281 bool CoreAudioOutput::OnDataCallback(uint64_t device_frequency) {
282   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
283   if (num_data_callbacks_ == 0) {
284     RTC_LOG(INFO) << "--- Output audio stream is alive ---";
285   }
286   // Get the padding value which indicates the amount of valid unread data that
287   // the endpoint buffer currently contains.
288   UINT32 num_unread_frames = 0;
289   _com_error error = audio_client_->GetCurrentPadding(&num_unread_frames);
290   if (error.Error() == AUDCLNT_E_DEVICE_INVALIDATED) {
291     // Avoid breaking the thread loop implicitly by returning false and return
292     // true instead for AUDCLNT_E_DEVICE_INVALIDATED even it is a valid error
293     // message. We will use notifications about device changes instead to stop
294     // data callbacks and attempt to restart streaming .
295     RTC_DLOG(LS_ERROR) << "AUDCLNT_E_DEVICE_INVALIDATED";
296     return true;
297   }
298   if (FAILED(error.Error())) {
299     RTC_LOG(LS_ERROR) << "IAudioClient::GetCurrentPadding failed: "
300                       << core_audio_utility::ErrorToString(error);
301     return false;
302   }
303 
304   // Contains how much new data we can write to the buffer without the risk of
305   // overwriting previously written data that the audio engine has not yet read
306   // from the buffer. I.e., it is the maximum buffer size we can request when
307   // calling IAudioRenderClient::GetBuffer().
308   UINT32 num_requested_frames =
309       endpoint_buffer_size_frames_ - num_unread_frames;
310   if (num_requested_frames == 0) {
311     RTC_DLOG(LS_WARNING)
312         << "Audio thread is signaled but no new audio samples are needed";
313     return true;
314   }
315 
316   // Request all available space in the rendering endpoint buffer into which the
317   // client can later write an audio packet.
318   uint8_t* audio_data;
319   error = audio_render_client_->GetBuffer(num_requested_frames, &audio_data);
320   if (FAILED(error.Error())) {
321     RTC_LOG(LS_ERROR) << "IAudioRenderClient::GetBuffer failed: "
322                       << core_audio_utility::ErrorToString(error);
323     return false;
324   }
325 
326   // Update output delay estimate but only about once per second to save
327   // resources. The estimate is usually stable.
328   if (num_data_callbacks_ % 100 == 0) {
329     // TODO(henrika): note that FineAudioBuffer adds latency as well.
330     latency_ms_ = EstimateOutputLatencyMillis(device_frequency);
331     if (num_data_callbacks_ % 500 == 0) {
332       RTC_DLOG(INFO) << "latency: " << latency_ms_;
333     }
334   }
335 
336   // Get audio data from WebRTC and write it to the allocated buffer in
337   // |audio_data|. The playout latency is not updated for each callback.
338   fine_audio_buffer_->GetPlayoutData(
339       rtc::MakeArrayView(reinterpret_cast<int16_t*>(audio_data),
340                          num_requested_frames * format_.Format.nChannels),
341       latency_ms_);
342 
343   // Release the buffer space acquired in IAudioRenderClient::GetBuffer.
344   error = audio_render_client_->ReleaseBuffer(num_requested_frames, 0);
345   if (FAILED(error.Error())) {
346     RTC_LOG(LS_ERROR) << "IAudioRenderClient::ReleaseBuffer failed: "
347                       << core_audio_utility::ErrorToString(error);
348     return false;
349   }
350 
351   num_frames_written_ += num_requested_frames;
352   ++num_data_callbacks_;
353 
354   return true;
355 }
356 
357 // TODO(henrika): IAudioClock2::GetDevicePosition could perhaps be used here
358 // instead. Tried it once, but it crashed for capture devices.
EstimateOutputLatencyMillis(uint64_t device_frequency)359 int CoreAudioOutput::EstimateOutputLatencyMillis(uint64_t device_frequency) {
360   UINT64 position = 0;
361   UINT64 qpc_position = 0;
362   int delay_ms = 0;
363   // Get the device position through output parameter |position|. This is the
364   // stream position of the sample that is currently playing through the
365   // speakers.
366   _com_error error = audio_clock_->GetPosition(&position, &qpc_position);
367   if (error.Error() == S_OK) {
368     // Number of frames already played out through the speaker.
369     const uint64_t num_played_out_frames =
370         format_.Format.nSamplesPerSec * position / device_frequency;
371 
372     // Number of frames that have been written to the buffer but not yet
373     // played out corresponding to the estimated latency measured in number
374     // of audio frames.
375     const uint64_t delay_frames = num_frames_written_ - num_played_out_frames;
376 
377     // Convert latency in number of frames into milliseconds.
378     webrtc::TimeDelta delay =
379         webrtc::TimeDelta::Micros(delay_frames * rtc::kNumMicrosecsPerSec /
380                                   format_.Format.nSamplesPerSec);
381     delay_ms = delay.ms();
382   }
383   return delay_ms;
384 }
385 
386 // Called from OnErrorCallback() when error type is kStreamDisconnected.
387 // Note that this method is called on the audio thread and the internal restart
388 // sequence is also executed on that same thread. The audio thread is therefore
389 // not stopped during restart. Such a scheme also makes the restart process less
390 // complex.
391 // Note that, none of the called methods are thread checked since they can also
392 // be called on the main thread. Thread checkers are instead added on one layer
393 // above (in audio_device_module.cc) which ensures that the public API is thread
394 // safe.
395 // TODO(henrika): add more details.
HandleStreamDisconnected()396 bool CoreAudioOutput::HandleStreamDisconnected() {
397   RTC_DLOG(INFO) << "<<<--- " << __FUNCTION__;
398   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
399   RTC_DCHECK(automatic_restart());
400 
401   if (StopPlayout() != 0) {
402     return false;
403   }
404 
405   if (!SwitchDeviceIfNeeded()) {
406     return false;
407   }
408 
409   if (InitPlayout() != 0) {
410     return false;
411   }
412 
413   if (StartPlayout() != 0) {
414     return false;
415   }
416   RTC_DLOG(INFO) << __FUNCTION__ << " --->>>";
417   return true;
418 }
419 
420 }  // namespace webrtc_win
421 
422 }  // namespace webrtc
423