• 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_base_win.h"
12 #include "modules/audio_device/audio_device_buffer.h"
13 
14 #include <memory>
15 #include <string>
16 
17 #include "rtc_base/arraysize.h"
18 #include "rtc_base/checks.h"
19 #include "rtc_base/logging.h"
20 #include "rtc_base/numerics/safe_conversions.h"
21 #include "rtc_base/time_utils.h"
22 #include "rtc_base/win/windows_version.h"
23 
24 using Microsoft::WRL::ComPtr;
25 
26 namespace webrtc {
27 namespace webrtc_win {
28 namespace {
29 
30 // Even if the device supports low latency and even if IAudioClient3 can be
31 // used (requires Win10 or higher), we currently disable any attempts to
32 // initialize the client for low-latency.
33 // TODO(henrika): more research is needed before we can enable low-latency.
34 const bool kEnableLowLatencyIfSupported = false;
35 
36 // Each unit of reference time is 100 nanoseconds, hence |kReftimesPerSec|
37 // corresponds to one second.
38 // TODO(henrika): possibly add usage in Init().
39 // const REFERENCE_TIME kReferenceTimesPerSecond = 10000000;
40 
41 enum DefaultDeviceType {
42   kUndefined = -1,
43   kDefault = 0,
44   kDefaultCommunications = 1,
45   kDefaultDeviceTypeMaxCount = kDefaultCommunications + 1,
46 };
47 
DirectionToString(CoreAudioBase::Direction direction)48 const char* DirectionToString(CoreAudioBase::Direction direction) {
49   switch (direction) {
50     case CoreAudioBase::Direction::kOutput:
51       return "Output";
52     case CoreAudioBase::Direction::kInput:
53       return "Input";
54     default:
55       return "Unkown";
56   }
57 }
58 
RoleToString(const ERole role)59 const char* RoleToString(const ERole role) {
60   switch (role) {
61     case eConsole:
62       return "Console";
63     case eMultimedia:
64       return "Multimedia";
65     case eCommunications:
66       return "Communications";
67     default:
68       return "Unsupported";
69   }
70 }
71 
IndexToString(int index)72 std::string IndexToString(int index) {
73   std::string ss = std::to_string(index);
74   switch (index) {
75     case kDefault:
76       ss += " (Default)";
77       break;
78     case kDefaultCommunications:
79       ss += " (Communications)";
80       break;
81     default:
82       break;
83   }
84   return ss;
85 }
86 
SessionStateToString(AudioSessionState state)87 const char* SessionStateToString(AudioSessionState state) {
88   switch (state) {
89     case AudioSessionStateActive:
90       return "Active";
91     case AudioSessionStateInactive:
92       return "Inactive";
93     case AudioSessionStateExpired:
94       return "Expired";
95     default:
96       return "Invalid";
97   }
98 }
99 
SessionDisconnectReasonToString(AudioSessionDisconnectReason reason)100 const char* SessionDisconnectReasonToString(
101     AudioSessionDisconnectReason reason) {
102   switch (reason) {
103     case DisconnectReasonDeviceRemoval:
104       return "DeviceRemoval";
105     case DisconnectReasonServerShutdown:
106       return "ServerShutdown";
107     case DisconnectReasonFormatChanged:
108       return "FormatChanged";
109     case DisconnectReasonSessionLogoff:
110       return "SessionLogoff";
111     case DisconnectReasonSessionDisconnected:
112       return "Disconnected";
113     case DisconnectReasonExclusiveModeOverride:
114       return "ExclusiveModeOverride";
115     default:
116       return "Invalid";
117   }
118 }
119 
Run(void * obj)120 void Run(void* obj) {
121   RTC_DCHECK(obj);
122   reinterpret_cast<CoreAudioBase*>(obj)->ThreadRun();
123 }
124 
125 // Returns true if the selected audio device supports low latency, i.e, if it
126 // is possible to initialize the engine using periods less than the default
127 // period (10ms).
IsLowLatencySupported(IAudioClient3 * client3,const WAVEFORMATEXTENSIBLE * format,uint32_t * min_period_in_frames)128 bool IsLowLatencySupported(IAudioClient3* client3,
129                            const WAVEFORMATEXTENSIBLE* format,
130                            uint32_t* min_period_in_frames) {
131   RTC_DLOG(INFO) << __FUNCTION__;
132 
133   // Get the range of periodicities supported by the engine for the specified
134   // stream format.
135   uint32_t default_period = 0;
136   uint32_t fundamental_period = 0;
137   uint32_t min_period = 0;
138   uint32_t max_period = 0;
139   if (FAILED(core_audio_utility::GetSharedModeEnginePeriod(
140           client3, format, &default_period, &fundamental_period, &min_period,
141           &max_period))) {
142     return false;
143   }
144 
145   // Low latency is supported if the shortest allowed period is less than the
146   // default engine period.
147   // TODO(henrika): verify that this assumption is correct.
148   const bool low_latency = min_period < default_period;
149   RTC_LOG(INFO) << "low_latency: " << low_latency;
150   *min_period_in_frames = low_latency ? min_period : 0;
151   return low_latency;
152 }
153 
154 }  // namespace
155 
CoreAudioBase(Direction direction,bool automatic_restart,OnDataCallback data_callback,OnErrorCallback error_callback)156 CoreAudioBase::CoreAudioBase(Direction direction,
157                              bool automatic_restart,
158                              OnDataCallback data_callback,
159                              OnErrorCallback error_callback)
160     : format_(),
161       direction_(direction),
162       automatic_restart_(automatic_restart),
163       on_data_callback_(data_callback),
164       on_error_callback_(error_callback),
165       device_index_(kUndefined),
166       is_restarting_(false) {
167   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction) << "]";
168   RTC_DLOG(INFO) << "Automatic restart: " << automatic_restart;
169   RTC_DLOG(INFO) << "Windows version: " << rtc::rtc_win::GetVersion();
170 
171   // Create the event which the audio engine will signal each time a buffer
172   // becomes ready to be processed by the client.
173   audio_samples_event_.Set(CreateEvent(nullptr, false, false, nullptr));
174   RTC_DCHECK(audio_samples_event_.IsValid());
175 
176   // Event to be set in Stop() when rendering/capturing shall stop.
177   stop_event_.Set(CreateEvent(nullptr, false, false, nullptr));
178   RTC_DCHECK(stop_event_.IsValid());
179 
180   // Event to be set when it has been detected that an active device has been
181   // invalidated or the stream format has changed.
182   restart_event_.Set(CreateEvent(nullptr, false, false, nullptr));
183   RTC_DCHECK(restart_event_.IsValid());
184 }
185 
~CoreAudioBase()186 CoreAudioBase::~CoreAudioBase() {
187   RTC_DLOG(INFO) << __FUNCTION__;
188   RTC_DCHECK_EQ(ref_count_, 1);
189 }
190 
GetDataFlow() const191 EDataFlow CoreAudioBase::GetDataFlow() const {
192   return direction_ == CoreAudioBase::Direction::kOutput ? eRender : eCapture;
193 }
194 
IsRestarting() const195 bool CoreAudioBase::IsRestarting() const {
196   return is_restarting_;
197 }
198 
TimeSinceStart() const199 int64_t CoreAudioBase::TimeSinceStart() const {
200   return rtc::TimeSince(start_time_);
201 }
202 
NumberOfActiveDevices() const203 int CoreAudioBase::NumberOfActiveDevices() const {
204   return core_audio_utility::NumberOfActiveDevices(GetDataFlow());
205 }
206 
NumberOfEnumeratedDevices() const207 int CoreAudioBase::NumberOfEnumeratedDevices() const {
208   const int num_active = NumberOfActiveDevices();
209   return num_active > 0 ? num_active + kDefaultDeviceTypeMaxCount : 0;
210 }
211 
ReleaseCOMObjects()212 void CoreAudioBase::ReleaseCOMObjects() {
213   RTC_DLOG(INFO) << __FUNCTION__;
214   // ComPtr::Reset() sets the ComPtr to nullptr releasing any previous
215   // reference.
216   if (audio_client_) {
217     audio_client_.Reset();
218   }
219   if (audio_clock_.Get()) {
220     audio_clock_.Reset();
221   }
222   if (audio_session_control_.Get()) {
223     audio_session_control_.Reset();
224   }
225 }
226 
IsDefaultDevice(int index) const227 bool CoreAudioBase::IsDefaultDevice(int index) const {
228   return index == kDefault;
229 }
230 
IsDefaultCommunicationsDevice(int index) const231 bool CoreAudioBase::IsDefaultCommunicationsDevice(int index) const {
232   return index == kDefaultCommunications;
233 }
234 
IsDefaultDeviceId(const std::string & device_id) const235 bool CoreAudioBase::IsDefaultDeviceId(const std::string& device_id) const {
236   // Returns true if |device_id| corresponds to the id of the default
237   // device. Note that, if only one device is available (or if the user has not
238   // explicitly set a default device), |device_id| will also math
239   // IsDefaultCommunicationsDeviceId().
240   return (IsInput() &&
241           (device_id == core_audio_utility::GetDefaultInputDeviceID())) ||
242          (IsOutput() &&
243           (device_id == core_audio_utility::GetDefaultOutputDeviceID()));
244 }
245 
IsDefaultCommunicationsDeviceId(const std::string & device_id) const246 bool CoreAudioBase::IsDefaultCommunicationsDeviceId(
247     const std::string& device_id) const {
248   // Returns true if |device_id| corresponds to the id of the default
249   // communication device. Note that, if only one device is available (or if
250   // the user has not explicitly set a communication device), |device_id| will
251   // also math IsDefaultDeviceId().
252   return (IsInput() &&
253           (device_id ==
254            core_audio_utility::GetCommunicationsInputDeviceID())) ||
255          (IsOutput() &&
256           (device_id == core_audio_utility::GetCommunicationsOutputDeviceID()));
257 }
258 
IsInput() const259 bool CoreAudioBase::IsInput() const {
260   return direction_ == CoreAudioBase::Direction::kInput;
261 }
262 
IsOutput() const263 bool CoreAudioBase::IsOutput() const {
264   return direction_ == CoreAudioBase::Direction::kOutput;
265 }
266 
GetDeviceID(int index) const267 std::string CoreAudioBase::GetDeviceID(int index) const {
268   if (index >= NumberOfEnumeratedDevices()) {
269     RTC_LOG(LS_ERROR) << "Invalid device index";
270     return std::string();
271   }
272 
273   std::string device_id;
274   if (IsDefaultDevice(index)) {
275     device_id = IsInput() ? core_audio_utility::GetDefaultInputDeviceID()
276                           : core_audio_utility::GetDefaultOutputDeviceID();
277   } else if (IsDefaultCommunicationsDevice(index)) {
278     device_id = IsInput()
279                     ? core_audio_utility::GetCommunicationsInputDeviceID()
280                     : core_audio_utility::GetCommunicationsOutputDeviceID();
281   } else {
282     AudioDeviceNames device_names;
283     bool ok = IsInput()
284                   ? core_audio_utility::GetInputDeviceNames(&device_names)
285                   : core_audio_utility::GetOutputDeviceNames(&device_names);
286     if (ok) {
287       device_id = device_names[index].unique_id;
288     }
289   }
290   return device_id;
291 }
292 
SetDevice(int index)293 int CoreAudioBase::SetDevice(int index) {
294   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
295                  << "]: index=" << IndexToString(index);
296   if (initialized_) {
297     return -1;
298   }
299 
300   std::string device_id = GetDeviceID(index);
301   RTC_DLOG(INFO) << "index=" << IndexToString(index)
302                  << " => device_id: " << device_id;
303   device_index_ = index;
304   device_id_ = device_id;
305 
306   return device_id_.empty() ? -1 : 0;
307 }
308 
DeviceName(int index,std::string * name,std::string * guid) const309 int CoreAudioBase::DeviceName(int index,
310                               std::string* name,
311                               std::string* guid) const {
312   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
313                  << "]: index=" << IndexToString(index);
314   if (index > NumberOfEnumeratedDevices() - 1) {
315     RTC_LOG(LS_ERROR) << "Invalid device index";
316     return -1;
317   }
318 
319   AudioDeviceNames device_names;
320   bool ok = IsInput() ? core_audio_utility::GetInputDeviceNames(&device_names)
321                       : core_audio_utility::GetOutputDeviceNames(&device_names);
322   // Validate the index one extra time in-case the size of the generated list
323   // did not match NumberOfEnumeratedDevices().
324   if (!ok || static_cast<int>(device_names.size()) <= index) {
325     RTC_LOG(LS_ERROR) << "Failed to get the device name";
326     return -1;
327   }
328 
329   *name = device_names[index].device_name;
330   RTC_DLOG(INFO) << "name: " << *name;
331   if (guid != nullptr) {
332     *guid = device_names[index].unique_id;
333     RTC_DLOG(INFO) << "guid: " << *guid;
334   }
335   return 0;
336 }
337 
Init()338 bool CoreAudioBase::Init() {
339   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
340                  << "]";
341   RTC_DCHECK_GE(device_index_, 0);
342   RTC_DCHECK(!device_id_.empty());
343   RTC_DCHECK(audio_device_buffer_);
344   RTC_DCHECK(!audio_client_);
345   RTC_DCHECK(!audio_session_control_.Get());
346 
347   // Use an existing combination of |device_index_| and |device_id_| to set
348   // parameters which are required to create an audio client. It is up to the
349   // parent class to set |device_index_| and |device_id_|.
350   std::string device_id = AudioDeviceName::kDefaultDeviceId;
351   ERole role = ERole();
352   if (IsDefaultDevice(device_index_)) {
353     role = eConsole;
354   } else if (IsDefaultCommunicationsDevice(device_index_)) {
355     role = eCommunications;
356   } else {
357     device_id = device_id_;
358   }
359   RTC_LOG(LS_INFO) << "Unique device identifier: device_id=" << device_id
360                    << ", role=" << RoleToString(role);
361 
362   // Create an IAudioClient interface which enables us to create and initialize
363   // an audio stream between an audio application and the audio engine.
364   ComPtr<IAudioClient> audio_client;
365   if (core_audio_utility::GetAudioClientVersion() == 3) {
366     RTC_DLOG(INFO) << "Using IAudioClient3";
367     audio_client =
368         core_audio_utility::CreateClient3(device_id, GetDataFlow(), role);
369   } else if (core_audio_utility::GetAudioClientVersion() == 2) {
370     RTC_DLOG(INFO) << "Using IAudioClient2";
371     audio_client =
372         core_audio_utility::CreateClient2(device_id, GetDataFlow(), role);
373   } else {
374     RTC_DLOG(INFO) << "Using IAudioClient";
375     audio_client =
376         core_audio_utility::CreateClient(device_id, GetDataFlow(), role);
377   }
378   if (!audio_client) {
379     return false;
380   }
381 
382   // Set extra client properties before initialization if the audio client
383   // supports it.
384   // TODO(henrika): evaluate effect(s) of making these changes. Also, perhaps
385   // these types of settings belongs to the client and not the utility parts.
386   if (core_audio_utility::GetAudioClientVersion() >= 2) {
387     if (FAILED(core_audio_utility::SetClientProperties(
388             static_cast<IAudioClient2*>(audio_client.Get())))) {
389       return false;
390     }
391   }
392 
393   // Retrieve preferred audio input or output parameters for the given client
394   // and the specified client properties. Override the preferred rate if sample
395   // rate has been defined by the user. Rate conversion will be performed by
396   // the audio engine to match the client if needed.
397   AudioParameters params;
398   HRESULT res = sample_rate_ ? core_audio_utility::GetPreferredAudioParameters(
399                                    audio_client.Get(), &params, *sample_rate_)
400                              : core_audio_utility::GetPreferredAudioParameters(
401                                    audio_client.Get(), &params);
402   if (FAILED(res)) {
403     return false;
404   }
405 
406   // Define the output WAVEFORMATEXTENSIBLE format in |format_|.
407   WAVEFORMATEX* format = &format_.Format;
408   format->wFormatTag = WAVE_FORMAT_EXTENSIBLE;
409   // Check the preferred channel configuration and request implicit channel
410   // upmixing (audio engine extends from 2 to N channels internally) if the
411   // preferred number of channels is larger than two; i.e., initialize the
412   // stream in stereo even if the preferred configuration is multi-channel.
413   if (params.channels() <= 2) {
414     format->nChannels = rtc::dchecked_cast<WORD>(params.channels());
415   } else {
416     // TODO(henrika): ensure that this approach works on different multi-channel
417     // devices. Verified on:
418     // - Corsair VOID PRO Surround USB Adapter (supports 7.1)
419     RTC_LOG(LS_WARNING)
420         << "Using channel upmixing in WASAPI audio engine (2 => "
421         << params.channels() << ")";
422     format->nChannels = 2;
423   }
424   format->nSamplesPerSec = params.sample_rate();
425   format->wBitsPerSample = rtc::dchecked_cast<WORD>(params.bits_per_sample());
426   format->nBlockAlign = (format->wBitsPerSample / 8) * format->nChannels;
427   format->nAvgBytesPerSec = format->nSamplesPerSec * format->nBlockAlign;
428   format->cbSize = sizeof(WAVEFORMATEXTENSIBLE) - sizeof(WAVEFORMATEX);
429   // Add the parts which are unique for the WAVE_FORMAT_EXTENSIBLE structure.
430   format_.Samples.wValidBitsPerSample =
431       rtc::dchecked_cast<WORD>(params.bits_per_sample());
432   format_.dwChannelMask =
433       format->nChannels == 1 ? KSAUDIO_SPEAKER_MONO : KSAUDIO_SPEAKER_STEREO;
434   format_.SubFormat = KSDATAFORMAT_SUBTYPE_PCM;
435   RTC_DLOG(INFO) << core_audio_utility::WaveFormatToString(&format_);
436 
437   // Verify that the format is supported but exclude the test if the default
438   // sample rate has been overridden. If so, the WASAPI audio engine will do
439   // any necessary conversions between the client format we have given it and
440   // the playback mix format or recording split format.
441   if (!sample_rate_) {
442     if (!core_audio_utility::IsFormatSupported(
443             audio_client.Get(), AUDCLNT_SHAREMODE_SHARED, &format_)) {
444       return false;
445     }
446   }
447 
448   // Check if low-latency is supported and use special initialization if it is.
449   // Low-latency initialization requires these things:
450   // - IAudioClient3 (>= Win10)
451   // - HDAudio driver
452   // - kEnableLowLatencyIfSupported changed from false (default) to true.
453   // TODO(henrika): IsLowLatencySupported() returns AUDCLNT_E_UNSUPPORTED_FORMAT
454   // when |sample_rate_.has_value()| returns true if rate conversion is
455   // actually required (i.e., client asks for other than the default rate).
456   bool low_latency_support = false;
457   uint32_t min_period_in_frames = 0;
458   if (kEnableLowLatencyIfSupported &&
459       core_audio_utility::GetAudioClientVersion() >= 3) {
460     low_latency_support =
461         IsLowLatencySupported(static_cast<IAudioClient3*>(audio_client.Get()),
462                               &format_, &min_period_in_frames);
463   }
464 
465   if (low_latency_support) {
466     RTC_DCHECK_GE(core_audio_utility::GetAudioClientVersion(), 3);
467     // Use IAudioClient3::InitializeSharedAudioStream() API to initialize a
468     // low-latency event-driven client. Request the smallest possible
469     // periodicity.
470     // TODO(henrika): evaluate this scheme in terms of CPU etc.
471     if (FAILED(core_audio_utility::SharedModeInitializeLowLatency(
472             static_cast<IAudioClient3*>(audio_client.Get()), &format_,
473             audio_samples_event_, min_period_in_frames,
474             sample_rate_.has_value(), &endpoint_buffer_size_frames_))) {
475       return false;
476     }
477   } else {
478     // Initialize the audio stream between the client and the device in shared
479     // mode using event-driven buffer handling. Also, using 0 as requested
480     // buffer size results in a default (minimum) endpoint buffer size.
481     // TODO(henrika): possibly increase |requested_buffer_size| to add
482     // robustness.
483     const REFERENCE_TIME requested_buffer_size = 0;
484     if (FAILED(core_audio_utility::SharedModeInitialize(
485             audio_client.Get(), &format_, audio_samples_event_,
486             requested_buffer_size, sample_rate_.has_value(),
487             &endpoint_buffer_size_frames_))) {
488       return false;
489     }
490   }
491 
492   // Check device period and the preferred buffer size and log a warning if
493   // WebRTC's buffer size is not an even divisor of the preferred buffer size
494   // in Core Audio.
495   // TODO(henrika): sort out if a non-perfect match really is an issue.
496   // TODO(henrika): compare with IAudioClient3::GetSharedModeEnginePeriod().
497   REFERENCE_TIME device_period;
498   if (FAILED(core_audio_utility::GetDevicePeriod(
499           audio_client.Get(), AUDCLNT_SHAREMODE_SHARED, &device_period))) {
500     return false;
501   }
502   const double device_period_in_seconds =
503       static_cast<double>(
504           core_audio_utility::ReferenceTimeToTimeDelta(device_period).ms()) /
505       1000.0L;
506   const int preferred_frames_per_buffer =
507       static_cast<int>(params.sample_rate() * device_period_in_seconds + 0.5);
508   RTC_DLOG(INFO) << "preferred_frames_per_buffer: "
509                  << preferred_frames_per_buffer;
510   if (preferred_frames_per_buffer % params.frames_per_buffer()) {
511     RTC_LOG(WARNING) << "Buffer size of " << params.frames_per_buffer()
512                      << " is not an even divisor of "
513                      << preferred_frames_per_buffer;
514   }
515 
516   // Create an AudioSessionControl interface given the initialized client.
517   // The IAudioControl interface enables a client to configure the control
518   // parameters for an audio session and to monitor events in the session.
519   ComPtr<IAudioSessionControl> audio_session_control =
520       core_audio_utility::CreateAudioSessionControl(audio_client.Get());
521   if (!audio_session_control.Get()) {
522     return false;
523   }
524 
525   // The Sndvol program displays volume and mute controls for sessions that
526   // are in the active and inactive states.
527   AudioSessionState state;
528   if (FAILED(audio_session_control->GetState(&state))) {
529     return false;
530   }
531   RTC_DLOG(INFO) << "audio session state: " << SessionStateToString(state);
532   RTC_DCHECK_EQ(state, AudioSessionStateInactive);
533 
534   // Register the client to receive notifications of session events, including
535   // changes in the stream state.
536   if (FAILED(audio_session_control->RegisterAudioSessionNotification(this))) {
537     return false;
538   }
539 
540   // Store valid COM interfaces.
541   audio_client_ = audio_client;
542   audio_session_control_ = audio_session_control;
543 
544   return true;
545 }
546 
Start()547 bool CoreAudioBase::Start() {
548   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
549                  << "]";
550   if (IsRestarting()) {
551     // Audio thread should be alive during internal restart since the restart
552     // callback is triggered on that thread and it also makes the restart
553     // sequence less complex.
554     RTC_DCHECK(audio_thread_);
555   }
556 
557   // Start an audio thread but only if one does not already exist (which is the
558   // case during restart).
559   if (!audio_thread_) {
560     audio_thread_ = std::make_unique<rtc::PlatformThread>(
561         Run, this, IsInput() ? "wasapi_capture_thread" : "wasapi_render_thread",
562         rtc::kRealtimePriority);
563     RTC_DCHECK(audio_thread_);
564     audio_thread_->Start();
565     if (!audio_thread_->IsRunning()) {
566       StopThread();
567       RTC_LOG(LS_ERROR) << "Failed to start audio thread";
568       return false;
569     }
570     RTC_DLOG(INFO) << "Started thread with name: " << audio_thread_->name()
571                    << " and id: " << audio_thread_->GetThreadRef();
572   }
573 
574   // Start streaming data between the endpoint buffer and the audio engine.
575   _com_error error = audio_client_->Start();
576   if (FAILED(error.Error())) {
577     StopThread();
578     RTC_LOG(LS_ERROR) << "IAudioClient::Start failed: "
579                       << core_audio_utility::ErrorToString(error);
580     return false;
581   }
582 
583   start_time_ = rtc::TimeMillis();
584   num_data_callbacks_ = 0;
585 
586   return true;
587 }
588 
Stop()589 bool CoreAudioBase::Stop() {
590   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
591                  << "]";
592   RTC_DLOG(INFO) << "total activity time: " << TimeSinceStart();
593 
594   // Stop audio streaming.
595   _com_error error = audio_client_->Stop();
596   if (FAILED(error.Error())) {
597     RTC_LOG(LS_ERROR) << "IAudioClient::Stop failed: "
598                       << core_audio_utility::ErrorToString(error);
599   }
600   // Stop and destroy the audio thread but only when a restart attempt is not
601   // ongoing.
602   if (!IsRestarting()) {
603     StopThread();
604   }
605 
606   // Flush all pending data and reset the audio clock stream position to 0.
607   error = audio_client_->Reset();
608   if (FAILED(error.Error())) {
609     RTC_LOG(LS_ERROR) << "IAudioClient::Reset failed: "
610                       << core_audio_utility::ErrorToString(error);
611   }
612 
613   if (IsOutput()) {
614     // Extra safety check to ensure that the buffers are cleared.
615     // If the buffers are not cleared correctly, the next call to Start()
616     // would fail with AUDCLNT_E_BUFFER_ERROR at
617     // IAudioRenderClient::GetBuffer().
618     UINT32 num_queued_frames = 0;
619     audio_client_->GetCurrentPadding(&num_queued_frames);
620     RTC_DCHECK_EQ(0u, num_queued_frames);
621   }
622 
623   // Delete the previous registration by the client to receive notifications
624   // about audio session events.
625   RTC_DLOG(INFO) << "audio session state: "
626                  << SessionStateToString(GetAudioSessionState());
627   error = audio_session_control_->UnregisterAudioSessionNotification(this);
628   if (FAILED(error.Error())) {
629     RTC_LOG(LS_ERROR)
630         << "IAudioSessionControl::UnregisterAudioSessionNotification failed: "
631         << core_audio_utility::ErrorToString(error);
632   }
633 
634   // To ensure that the restart process is as simple as possible, the audio
635   // thread is not destroyed during restart attempts triggered by internal
636   // error callbacks.
637   if (!IsRestarting()) {
638     thread_checker_audio_.Detach();
639   }
640 
641   // Release all allocated COM interfaces to allow for a restart without
642   // intermediate destruction.
643   ReleaseCOMObjects();
644 
645   return true;
646 }
647 
IsVolumeControlAvailable(bool * available) const648 bool CoreAudioBase::IsVolumeControlAvailable(bool* available) const {
649   // A valid IAudioClient is required to access the ISimpleAudioVolume interface
650   // properly. It is possible to use IAudioSessionManager::GetSimpleAudioVolume
651   // as well but we use the audio client here to ensure that the initialized
652   // audio session is visible under group box labeled "Applications" in
653   // Sndvol.exe.
654   if (!audio_client_) {
655     return false;
656   }
657 
658   // Try to create an ISimpleAudioVolume instance.
659   ComPtr<ISimpleAudioVolume> audio_volume =
660       core_audio_utility::CreateSimpleAudioVolume(audio_client_.Get());
661   if (!audio_volume.Get()) {
662     RTC_DLOG(LS_ERROR) << "Volume control is not supported";
663     return false;
664   }
665 
666   // Try to use the valid volume control.
667   float volume = 0.0;
668   _com_error error = audio_volume->GetMasterVolume(&volume);
669   if (error.Error() != S_OK) {
670     RTC_LOG(LS_ERROR) << "ISimpleAudioVolume::GetMasterVolume failed: "
671                       << core_audio_utility::ErrorToString(error);
672     *available = false;
673   }
674   RTC_DLOG(INFO) << "master volume for output audio session: " << volume;
675 
676   *available = true;
677   return false;
678 }
679 
680 // Internal test method which can be used in tests to emulate a restart signal.
681 // It simply sets the same event which is normally triggered by session and
682 // device notifications. Hence, the emulated restart sequence covers most parts
683 // of a real sequence expect the actual device switch.
Restart()684 bool CoreAudioBase::Restart() {
685   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
686                  << "]";
687   if (!automatic_restart()) {
688     return false;
689   }
690   is_restarting_ = true;
691   SetEvent(restart_event_.Get());
692   return true;
693 }
694 
StopThread()695 void CoreAudioBase::StopThread() {
696   RTC_DLOG(INFO) << __FUNCTION__;
697   RTC_DCHECK(!IsRestarting());
698   if (audio_thread_) {
699     if (audio_thread_->IsRunning()) {
700       RTC_DLOG(INFO) << "Sets stop_event...";
701       SetEvent(stop_event_.Get());
702       RTC_DLOG(INFO) << "PlatformThread::Stop...";
703       audio_thread_->Stop();
704     }
705     audio_thread_.reset();
706 
707     // Ensure that we don't quit the main thread loop immediately next
708     // time Start() is called.
709     ResetEvent(stop_event_.Get());
710     ResetEvent(restart_event_.Get());
711   }
712 }
713 
HandleRestartEvent()714 bool CoreAudioBase::HandleRestartEvent() {
715   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
716                  << "]";
717   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
718   RTC_DCHECK(audio_thread_);
719   RTC_DCHECK(IsRestarting());
720   // Let each client (input and/or output) take care of its own restart
721   // sequence since each side might need unique actions.
722   // TODO(henrika): revisit and investigate if one common base implementation
723   // is possible
724   bool restart_ok = on_error_callback_(ErrorType::kStreamDisconnected);
725   is_restarting_ = false;
726   return restart_ok;
727 }
728 
SwitchDeviceIfNeeded()729 bool CoreAudioBase::SwitchDeviceIfNeeded() {
730   RTC_DLOG(INFO) << __FUNCTION__ << "[" << DirectionToString(direction())
731                  << "]";
732   RTC_DCHECK_RUN_ON(&thread_checker_audio_);
733   RTC_DCHECK(IsRestarting());
734 
735   RTC_DLOG(INFO) << "device_index=" << device_index_
736                  << " => device_id: " << device_id_;
737 
738   // Ensure that at least one device exists and can be utilized. The most
739   // probable cause for ending up here is that a device has been removed.
740   if (core_audio_utility::NumberOfActiveDevices(IsInput() ? eCapture
741                                                           : eRender) < 1) {
742     RTC_DLOG(LS_ERROR) << "All devices are disabled or removed";
743     return false;
744   }
745 
746   // Get the unique device ID for the index which is currently used. It seems
747   // safe to assume that if the ID is the same as the existing device ID, then
748   // the device configuration is the same as before.
749   std::string device_id = GetDeviceID(device_index_);
750   if (device_id != device_id_) {
751     RTC_LOG(LS_WARNING)
752         << "Device configuration has changed => changing device selection...";
753     // TODO(henrika): depending on the current state and how we got here, we
754     // must select a new device here.
755     if (SetDevice(kDefault) == -1) {
756       RTC_LOG(LS_WARNING) << "Failed to set new audio device";
757       return false;
758     }
759   } else {
760     RTC_LOG(INFO)
761         << "Device configuration has not changed => keeping selected device";
762   }
763   return true;
764 }
765 
GetAudioSessionState() const766 AudioSessionState CoreAudioBase::GetAudioSessionState() const {
767   AudioSessionState state = AudioSessionStateInactive;
768   RTC_DCHECK(audio_session_control_.Get());
769   _com_error error = audio_session_control_->GetState(&state);
770   if (FAILED(error.Error())) {
771     RTC_DLOG(LS_ERROR) << "IAudioSessionControl::GetState failed: "
772                        << core_audio_utility::ErrorToString(error);
773   }
774   return state;
775 }
776 
777 // TODO(henrika): only used for debugging purposes currently.
AddRef()778 ULONG CoreAudioBase::AddRef() {
779   ULONG new_ref = InterlockedIncrement(&ref_count_);
780   // RTC_DLOG(INFO) << "__AddRef => " << new_ref;
781   return new_ref;
782 }
783 
784 // TODO(henrika): does not call delete this.
Release()785 ULONG CoreAudioBase::Release() {
786   ULONG new_ref = InterlockedDecrement(&ref_count_);
787   // RTC_DLOG(INFO) << "__Release => " << new_ref;
788   return new_ref;
789 }
790 
791 // TODO(henrika): can probably be replaced by "return S_OK" only.
QueryInterface(REFIID iid,void ** object)792 HRESULT CoreAudioBase::QueryInterface(REFIID iid, void** object) {
793   if (object == nullptr) {
794     return E_POINTER;
795   }
796   if (iid == IID_IUnknown || iid == __uuidof(IAudioSessionEvents)) {
797     *object = static_cast<IAudioSessionEvents*>(this);
798     return S_OK;
799   };
800   *object = nullptr;
801   return E_NOINTERFACE;
802 }
803 
804 // IAudioSessionEvents::OnStateChanged.
OnStateChanged(AudioSessionState new_state)805 HRESULT CoreAudioBase::OnStateChanged(AudioSessionState new_state) {
806   RTC_DLOG(INFO) << "___" << __FUNCTION__ << "["
807                  << DirectionToString(direction())
808                  << "] new_state: " << SessionStateToString(new_state);
809   return S_OK;
810 }
811 
812 // When a session is disconnected because of a device removal or format change
813 // event, we want to inform the audio thread about the lost audio session and
814 // trigger an attempt to restart audio using a new (default) device.
815 // This method is called on separate threads owned by the session manager and
816 // it can happen that the same type of callback is called more than once for the
817 // same event.
OnSessionDisconnected(AudioSessionDisconnectReason disconnect_reason)818 HRESULT CoreAudioBase::OnSessionDisconnected(
819     AudioSessionDisconnectReason disconnect_reason) {
820   RTC_DLOG(INFO) << "___" << __FUNCTION__ << "["
821                  << DirectionToString(direction()) << "] reason: "
822                  << SessionDisconnectReasonToString(disconnect_reason);
823   // Ignore changes in the audio session (don't try to restart) if the user
824   // has explicitly asked for this type of ADM during construction.
825   if (!automatic_restart()) {
826     RTC_DLOG(LS_WARNING) << "___Automatic restart is disabled";
827     return S_OK;
828   }
829 
830   if (IsRestarting()) {
831     RTC_DLOG(LS_WARNING) << "___Ignoring since restart is already active";
832     return S_OK;
833   }
834 
835   // By default, automatic restart is enabled and the restart event will be set
836   // below if the device was removed or the format was changed.
837   if (disconnect_reason == DisconnectReasonDeviceRemoval ||
838       disconnect_reason == DisconnectReasonFormatChanged) {
839     is_restarting_ = true;
840     SetEvent(restart_event_.Get());
841   }
842   return S_OK;
843 }
844 
845 // IAudioSessionEvents::OnDisplayNameChanged
OnDisplayNameChanged(LPCWSTR new_display_name,LPCGUID event_context)846 HRESULT CoreAudioBase::OnDisplayNameChanged(LPCWSTR new_display_name,
847                                             LPCGUID event_context) {
848   return S_OK;
849 }
850 
851 // IAudioSessionEvents::OnIconPathChanged
OnIconPathChanged(LPCWSTR new_icon_path,LPCGUID event_context)852 HRESULT CoreAudioBase::OnIconPathChanged(LPCWSTR new_icon_path,
853                                          LPCGUID event_context) {
854   return S_OK;
855 }
856 
857 // IAudioSessionEvents::OnSimpleVolumeChanged
OnSimpleVolumeChanged(float new_simple_volume,BOOL new_mute,LPCGUID event_context)858 HRESULT CoreAudioBase::OnSimpleVolumeChanged(float new_simple_volume,
859                                              BOOL new_mute,
860                                              LPCGUID event_context) {
861   return S_OK;
862 }
863 
864 // IAudioSessionEvents::OnChannelVolumeChanged
OnChannelVolumeChanged(DWORD channel_count,float new_channel_volumes[],DWORD changed_channel,LPCGUID event_context)865 HRESULT CoreAudioBase::OnChannelVolumeChanged(DWORD channel_count,
866                                               float new_channel_volumes[],
867                                               DWORD changed_channel,
868                                               LPCGUID event_context) {
869   return S_OK;
870 }
871 
872 // IAudioSessionEvents::OnGroupingParamChanged
OnGroupingParamChanged(LPCGUID new_grouping_param,LPCGUID event_context)873 HRESULT CoreAudioBase::OnGroupingParamChanged(LPCGUID new_grouping_param,
874                                               LPCGUID event_context) {
875   return S_OK;
876 }
877 
ThreadRun()878 void CoreAudioBase::ThreadRun() {
879   if (!core_audio_utility::IsMMCSSSupported()) {
880     RTC_LOG(LS_ERROR) << "MMCSS is not supported";
881     return;
882   }
883   RTC_DLOG(INFO) << "[" << DirectionToString(direction())
884                  << "] ThreadRun starts...";
885   // TODO(henrika): difference between "Pro Audio" and "Audio"?
886   ScopedMMCSSRegistration mmcss_registration(L"Pro Audio");
887   ScopedCOMInitializer com_initializer(ScopedCOMInitializer::kMTA);
888   RTC_DCHECK(mmcss_registration.Succeeded());
889   RTC_DCHECK(com_initializer.Succeeded());
890   RTC_DCHECK(stop_event_.IsValid());
891   RTC_DCHECK(audio_samples_event_.IsValid());
892 
893   bool streaming = true;
894   bool error = false;
895   HANDLE wait_array[] = {stop_event_.Get(), restart_event_.Get(),
896                          audio_samples_event_.Get()};
897 
898   // The device frequency is the frequency generated by the hardware clock in
899   // the audio device. The GetFrequency() method reports a constant frequency.
900   UINT64 device_frequency = 0;
901   _com_error result(S_FALSE);
902   if (audio_clock_) {
903     RTC_DCHECK(IsOutput());
904     result = audio_clock_->GetFrequency(&device_frequency);
905     if (FAILED(result.Error())) {
906       RTC_LOG(LS_ERROR) << "IAudioClock::GetFrequency failed: "
907                         << core_audio_utility::ErrorToString(result);
908     }
909   }
910 
911   // Keep streaming audio until the stop event or the stream-switch event
912   // is signaled. An error event can also break the main thread loop.
913   while (streaming && !error) {
914     // Wait for a close-down event, stream-switch event or a new render event.
915     DWORD wait_result = WaitForMultipleObjects(arraysize(wait_array),
916                                                wait_array, false, INFINITE);
917     switch (wait_result) {
918       case WAIT_OBJECT_0 + 0:
919         // |stop_event_| has been set.
920         streaming = false;
921         break;
922       case WAIT_OBJECT_0 + 1:
923         // |restart_event_| has been set.
924         error = !HandleRestartEvent();
925         break;
926       case WAIT_OBJECT_0 + 2:
927         // |audio_samples_event_| has been set.
928         error = !on_data_callback_(device_frequency);
929         break;
930       default:
931         error = true;
932         break;
933     }
934   }
935 
936   if (streaming && error) {
937     RTC_LOG(LS_ERROR) << "[" << DirectionToString(direction())
938                       << "] WASAPI streaming failed.";
939     // Stop audio streaming since something has gone wrong in our main thread
940     // loop. Note that, we are still in a "started" state, hence a Stop() call
941     // is required to join the thread properly.
942     result = audio_client_->Stop();
943     if (FAILED(result.Error())) {
944       RTC_LOG(LS_ERROR) << "IAudioClient::Stop failed: "
945                         << core_audio_utility::ErrorToString(result);
946     }
947 
948     // TODO(henrika): notify clients that something has gone wrong and that
949     // this stream should be destroyed instead of reused in the future.
950   }
951 
952   RTC_DLOG(INFO) << "[" << DirectionToString(direction())
953                  << "] ...ThreadRun stops";
954 }
955 
956 }  // namespace webrtc_win
957 }  // namespace webrtc
958