• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_input_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/numerics/safe_conversions.h"
20 
21 using Microsoft::WRL::ComPtr;
22 
23 namespace webrtc {
24 namespace webrtc_win {
25 
26 enum AudioDeviceMessageType : uint32_t {
27   kMessageInputStreamDisconnected,
28 };
29 
CoreAudioInput(bool automatic_restart)30 CoreAudioInput::CoreAudioInput(bool automatic_restart)
31     : CoreAudioBase(
32           CoreAudioBase::Direction::kInput,
33           automatic_restart,
34           [this](uint64_t freq) { return OnDataCallback(freq); },
__anon8610932d0202(ErrorType err) 35           [this](ErrorType err) { return OnErrorCallback(err); }) {
36   RTC_DLOG(INFO) << __FUNCTION__;
37   RTC_DCHECK_RUN_ON(&thread_checker_);
38   thread_checker_audio_.Detach();
39 }
40 
~CoreAudioInput()41 CoreAudioInput::~CoreAudioInput() {
42   RTC_DLOG(INFO) << __FUNCTION__;
43   RTC_DCHECK_RUN_ON(&thread_checker_);
44 }
45 
Init()46 int CoreAudioInput::Init() {
47   RTC_DLOG(INFO) << __FUNCTION__;
48   RTC_DCHECK_RUN_ON(&thread_checker_);
49   return 0;
50 }
51 
Terminate()52 int CoreAudioInput::Terminate() {
53   RTC_DLOG(INFO) << __FUNCTION__;
54   RTC_DCHECK_RUN_ON(&thread_checker_);
55   StopRecording();
56   return 0;
57 }
58 
NumDevices() const59 int CoreAudioInput::NumDevices() const {
60   RTC_DCHECK_RUN_ON(&thread_checker_);
61   return core_audio_utility::NumberOfActiveDevices(eCapture);
62 }
63 
SetDevice(int index)64 int CoreAudioInput::SetDevice(int index) {
65   RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
66   RTC_DCHECK_GE(index, 0);
67   RTC_DCHECK_RUN_ON(&thread_checker_);
68   return CoreAudioBase::SetDevice(index);
69 }
70 
SetDevice(AudioDeviceModule::WindowsDeviceType device)71 int CoreAudioInput::SetDevice(AudioDeviceModule::WindowsDeviceType device) {
72   RTC_DLOG(INFO) << __FUNCTION__ << ": "
73                  << ((device == AudioDeviceModule::kDefaultDevice)
74                          ? "Default"
75                          : "DefaultCommunication");
76   RTC_DCHECK_RUN_ON(&thread_checker_);
77   return SetDevice((device == AudioDeviceModule::kDefaultDevice) ? 0 : 1);
78 }
79 
DeviceName(int index,std::string * name,std::string * guid)80 int CoreAudioInput::DeviceName(int index,
81                                std::string* name,
82                                std::string* guid) {
83   RTC_DLOG(INFO) << __FUNCTION__ << ": " << index;
84   RTC_DCHECK_RUN_ON(&thread_checker_);
85   RTC_DCHECK(name);
86   return CoreAudioBase::DeviceName(index, name, guid);
87 }
88 
AttachAudioBuffer(AudioDeviceBuffer * audio_buffer)89 void CoreAudioInput::AttachAudioBuffer(AudioDeviceBuffer* audio_buffer) {
90   RTC_DLOG(INFO) << __FUNCTION__;
91   RTC_DCHECK_RUN_ON(&thread_checker_);
92   audio_device_buffer_ = audio_buffer;
93 }
94 
RecordingIsInitialized() const95 bool CoreAudioInput::RecordingIsInitialized() const {
96   RTC_DLOG(INFO) << __FUNCTION__ << ": " << initialized_;
97   RTC_DCHECK_RUN_ON(&thread_checker_);
98   return initialized_;
99 }
100 
InitRecording()101 int CoreAudioInput::InitRecording() {
102   RTC_DLOG(INFO) << __FUNCTION__;
103   RTC_DCHECK(!initialized_);
104   RTC_DCHECK(!Recording());
105   RTC_DCHECK(!audio_capture_client_);
106 
107   // Creates an IAudioClient instance and stores the valid interface pointer in
108   // |audio_client3_|, |audio_client2_|, or |audio_client_| depending on
109   // platform support. The base class will use optimal input parameters and do
110   // an event driven shared mode initialization. The utilized format will be
111   // stored in |format_| and can be used for configuration and allocation of
112   // audio buffers.
113   if (!CoreAudioBase::Init()) {
114     return -1;
115   }
116   RTC_DCHECK(audio_client_);
117 
118   // Configure the recording side of the audio device buffer using |format_|
119   // after a trivial sanity check of the format structure.
120   RTC_DCHECK(audio_device_buffer_);
121   WAVEFORMATEX* format = &format_.Format;
122   RTC_DCHECK_EQ(format->wFormatTag, WAVE_FORMAT_EXTENSIBLE);
123   audio_device_buffer_->SetRecordingSampleRate(format->nSamplesPerSec);
124   audio_device_buffer_->SetRecordingChannels(format->nChannels);
125 
126   // Create a modified audio buffer class which allows us to supply any number
127   // of samples (and not only multiple of 10ms) to match the optimal buffer
128   // size per callback used by Core Audio.
129   // TODO(henrika): can we share one FineAudioBuffer with the output side?
130   fine_audio_buffer_ = std::make_unique<FineAudioBuffer>(audio_device_buffer_);
131 
132   // Create an IAudioCaptureClient for an initialized IAudioClient.
133   // The IAudioCaptureClient interface enables a client to read input data from
134   // a capture endpoint buffer.
135   ComPtr<IAudioCaptureClient> audio_capture_client =
136       core_audio_utility::CreateCaptureClient(audio_client_.Get());
137   if (!audio_capture_client) {
138     return -1;
139   }
140 
141   // Query performance frequency.
142   LARGE_INTEGER ticks_per_sec = {};
143   qpc_to_100ns_.reset();
144   if (::QueryPerformanceFrequency(&ticks_per_sec)) {
145     double qpc_ticks_per_second =
146         rtc::dchecked_cast<double>(ticks_per_sec.QuadPart);
147     qpc_to_100ns_ = 10000000.0 / qpc_ticks_per_second;
148   }
149 
150   // Store valid COM interfaces.
151   audio_capture_client_ = audio_capture_client;
152 
153   initialized_ = true;
154   return 0;
155 }
156 
StartRecording()157 int CoreAudioInput::StartRecording() {
158   RTC_DLOG(INFO) << __FUNCTION__;
159   RTC_DCHECK(!Recording());
160   RTC_DCHECK(fine_audio_buffer_);
161   RTC_DCHECK(audio_device_buffer_);
162   if (!initialized_) {
163     RTC_DLOG(LS_WARNING)
164         << "Recording can not start since InitRecording must succeed first";
165     return 0;
166   }
167 
168   fine_audio_buffer_->ResetRecord();
169   if (!IsRestarting()) {
170     audio_device_buffer_->StartRecording();
171   }
172 
173   if (!Start()) {
174     return -1;
175   }
176 
177   is_active_ = true;
178   return 0;
179 }
180 
StopRecording()181 int CoreAudioInput::StopRecording() {
182   RTC_DLOG(INFO) << __FUNCTION__;
183   if (!initialized_) {
184     return 0;
185   }
186 
187   // Release resources allocated in InitRecording() and then return if this
188   // method is called without any active input audio.
189   if (!Recording()) {
190     RTC_DLOG(WARNING) << "No input stream is active";
191     ReleaseCOMObjects();
192     initialized_ = false;
193     return 0;
194   }
195 
196   if (!Stop()) {
197     RTC_LOG(LS_ERROR) << "StopRecording failed";
198     return -1;
199   }
200 
201   if (!IsRestarting()) {
202     RTC_DCHECK(audio_device_buffer_);
203     audio_device_buffer_->StopRecording();
204   }
205 
206   // Release all allocated resources to allow for a restart without
207   // intermediate destruction.
208   ReleaseCOMObjects();
209   qpc_to_100ns_.reset();
210 
211   initialized_ = false;
212   is_active_ = false;
213   return 0;
214 }
215 
Recording()216 bool CoreAudioInput::Recording() {
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 CoreAudioInput::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 input device is removed.
RestartRecording()232 int CoreAudioInput::RestartRecording() {
233   RTC_DLOG(INFO) << __FUNCTION__;
234   RTC_DCHECK_RUN_ON(&thread_checker_);
235   if (!Recording()) {
236     return 0;
237   }
238 
239   if (!Restart()) {
240     RTC_LOG(LS_ERROR) << "RestartRecording failed";
241     return -1;
242   }
243   return 0;
244 }
245 
Restarting() const246 bool CoreAudioInput::Restarting() const {
247   RTC_DCHECK_RUN_ON(&thread_checker_);
248   return IsRestarting();
249 }
250 
SetSampleRate(uint32_t sample_rate)251 int CoreAudioInput::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 CoreAudioInput::ReleaseCOMObjects() {
259   RTC_DLOG(INFO) << __FUNCTION__;
260   CoreAudioBase::ReleaseCOMObjects();
261   if (audio_capture_client_.Get()) {
262     audio_capture_client_.Reset();
263   }
264 }
265 
OnDataCallback(uint64_t device_frequency)266 bool CoreAudioInput::OnDataCallback(uint64_t device_frequency) {
267   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
268 
269   if (!initialized_ || !is_active_) {
270     // This is concurrent examination of state across multiple threads so will
271     // be somewhat error prone, but we should still be defensive and not use
272     // audio_capture_client_ if we know it's not there.
273     return false;
274   }
275   if (num_data_callbacks_ == 0) {
276     RTC_LOG(INFO) << "--- Input audio stream is alive ---";
277   }
278   UINT32 num_frames_in_next_packet = 0;
279   _com_error error =
280       audio_capture_client_->GetNextPacketSize(&num_frames_in_next_packet);
281   if (error.Error() == AUDCLNT_E_DEVICE_INVALIDATED) {
282     // Avoid breaking the thread loop implicitly by returning false and return
283     // true instead for AUDCLNT_E_DEVICE_INVALIDATED even it is a valid error
284     // message. We will use notifications about device changes instead to stop
285     // data callbacks and attempt to restart streaming .
286     RTC_DLOG(LS_ERROR) << "AUDCLNT_E_DEVICE_INVALIDATED";
287     return true;
288   }
289   if (FAILED(error.Error())) {
290     RTC_LOG(LS_ERROR) << "IAudioCaptureClient::GetNextPacketSize failed: "
291                       << core_audio_utility::ErrorToString(error);
292     return false;
293   }
294 
295   // Drain the WASAPI capture buffer fully if audio has been recorded.
296   while (num_frames_in_next_packet > 0) {
297     uint8_t* audio_data;
298     UINT32 num_frames_to_read = 0;
299     DWORD flags = 0;
300     UINT64 device_position_frames = 0;
301     UINT64 capture_time_100ns = 0;
302     error = audio_capture_client_->GetBuffer(&audio_data, &num_frames_to_read,
303                                              &flags, &device_position_frames,
304                                              &capture_time_100ns);
305     if (error.Error() == AUDCLNT_S_BUFFER_EMPTY) {
306       // The call succeeded but no capture data is available to be read.
307       // Return and start waiting for new capture event
308       RTC_DCHECK_EQ(num_frames_to_read, 0u);
309       return true;
310     }
311     if (FAILED(error.Error())) {
312       RTC_LOG(LS_ERROR) << "IAudioCaptureClient::GetBuffer failed: "
313                         << core_audio_utility::ErrorToString(error);
314       return false;
315     }
316 
317     // Update input delay estimate but only about once per second to save
318     // resources. The estimate is usually stable.
319     if (num_data_callbacks_ % 100 == 0) {
320       absl::optional<int> opt_record_delay_ms;
321       // TODO(henrika): note that FineAudioBuffer adds latency as well.
322       opt_record_delay_ms = EstimateLatencyMillis(capture_time_100ns);
323       if (opt_record_delay_ms) {
324         latency_ms_ = *opt_record_delay_ms;
325       } else {
326         RTC_DLOG(LS_WARNING) << "Input latency is set to fixed value";
327         latency_ms_ = 20;
328       }
329     }
330     if (num_data_callbacks_ % 500 == 0) {
331       RTC_DLOG(INFO) << "latency: " << latency_ms_;
332     }
333 
334     // The data in the packet is not correlated with the previous packet's
335     // device position; possibly due to a stream state transition or timing
336     // glitch. The behavior of the AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY flag
337     // is undefined on the application's first call to GetBuffer after Start.
338     if (device_position_frames != 0 &&
339         flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY) {
340       RTC_DLOG(LS_WARNING) << "AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY";
341     }
342     // The time at which the device's stream position was recorded is uncertain.
343     // Thus, the client might be unable to accurately set a time stamp for the
344     // current data packet.
345     if (flags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR) {
346       RTC_DLOG(LS_WARNING) << "AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR";
347     }
348 
349     // Treat all of the data in the packet as silence and ignore the actual
350     // data values when AUDCLNT_BUFFERFLAGS_SILENT is set.
351     if (flags & AUDCLNT_BUFFERFLAGS_SILENT) {
352       rtc::ExplicitZeroMemory(audio_data,
353                               format_.Format.nBlockAlign * num_frames_to_read);
354       RTC_DLOG(LS_WARNING) << "Captured audio is replaced by silence";
355     } else {
356       // Copy recorded audio in |audio_data| to the WebRTC sink using the
357       // FineAudioBuffer object.
358       fine_audio_buffer_->DeliverRecordedData(
359           rtc::MakeArrayView(reinterpret_cast<const int16_t*>(audio_data),
360                              format_.Format.nChannels * num_frames_to_read),
361 
362           latency_ms_);
363     }
364 
365     error = audio_capture_client_->ReleaseBuffer(num_frames_to_read);
366     if (FAILED(error.Error())) {
367       RTC_LOG(LS_ERROR) << "IAudioCaptureClient::ReleaseBuffer failed: "
368                         << core_audio_utility::ErrorToString(error);
369       return false;
370     }
371 
372     error =
373         audio_capture_client_->GetNextPacketSize(&num_frames_in_next_packet);
374     if (FAILED(error.Error())) {
375       RTC_LOG(LS_ERROR) << "IAudioCaptureClient::GetNextPacketSize failed: "
376                         << core_audio_utility::ErrorToString(error);
377       return false;
378     }
379   }
380   ++num_data_callbacks_;
381   return true;
382 }
383 
OnErrorCallback(ErrorType error)384 bool CoreAudioInput::OnErrorCallback(ErrorType error) {
385   RTC_DLOG(INFO) << __FUNCTION__ << ": " << as_integer(error);
386   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
387   if (error == CoreAudioBase::ErrorType::kStreamDisconnected) {
388     HandleStreamDisconnected();
389   } else {
390     RTC_DLOG(WARNING) << "Unsupported error type";
391   }
392   return true;
393 }
394 
EstimateLatencyMillis(uint64_t capture_time_100ns)395 absl::optional<int> CoreAudioInput::EstimateLatencyMillis(
396     uint64_t capture_time_100ns) {
397   if (!qpc_to_100ns_) {
398     return absl::nullopt;
399   }
400   // Input parameter |capture_time_100ns| contains the performance counter at
401   // the time that the audio endpoint device recorded the device position of
402   // the first audio frame in the data packet converted into 100ns units.
403   // We derive a delay estimate by:
404   // - sampling the current performance counter (qpc_now_raw),
405   // - converting it into 100ns time units (now_time_100ns), and
406   // - subtracting |capture_time_100ns| from now_time_100ns.
407   LARGE_INTEGER perf_counter_now = {};
408   if (!::QueryPerformanceCounter(&perf_counter_now)) {
409     return absl::nullopt;
410   }
411   uint64_t qpc_now_raw = perf_counter_now.QuadPart;
412   uint64_t now_time_100ns = qpc_now_raw * (*qpc_to_100ns_);
413   webrtc::TimeDelta delay_us = webrtc::TimeDelta::Micros(
414       0.1 * (now_time_100ns - capture_time_100ns) + 0.5);
415   return delay_us.ms();
416 }
417 
418 // Called from OnErrorCallback() when error type is kStreamDisconnected.
419 // Note that this method is called on the audio thread and the internal restart
420 // sequence is also executed on that same thread. The audio thread is therefore
421 // not stopped during restart. Such a scheme also makes the restart process less
422 // complex.
423 // Note that, none of the called methods are thread checked since they can also
424 // be called on the main thread. Thread checkers are instead added on one layer
425 // above (in audio_device_module.cc) which ensures that the public API is thread
426 // safe.
427 // TODO(henrika): add more details.
HandleStreamDisconnected()428 bool CoreAudioInput::HandleStreamDisconnected() {
429   RTC_DLOG(INFO) << "<<<--- " << __FUNCTION__;
430   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
431   RTC_DCHECK(automatic_restart());
432 
433   if (StopRecording() != 0) {
434     return false;
435   }
436 
437   if (!SwitchDeviceIfNeeded()) {
438     return false;
439   }
440 
441   if (InitRecording() != 0) {
442     return false;
443   }
444   if (StartRecording() != 0) {
445     return false;
446   }
447 
448   RTC_DLOG(INFO) << __FUNCTION__ << " --->>>";
449   return true;
450 }
451 
452 }  // namespace webrtc_win
453 }  // namespace webrtc
454