xref: /aosp_15_r20/external/webrtc/modules/audio_device/win/core_audio_utility_win.cc (revision d9f758449e529ab9291ac668be2861e7a55c2422)
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_utility_win.h"
12 
13 #include <functiondiscoverykeys_devpkey.h>
14 #include <stdio.h>
15 #include <tchar.h>
16 
17 #include <iomanip>
18 #include <string>
19 #include <utility>
20 
21 #include "absl/strings/string_view.h"
22 #include "rtc_base/arraysize.h"
23 #include "rtc_base/logging.h"
24 #include "rtc_base/platform_thread_types.h"
25 #include "rtc_base/string_utils.h"
26 #include "rtc_base/strings/string_builder.h"
27 #include "rtc_base/win/windows_version.h"
28 
29 using Microsoft::WRL::ComPtr;
30 using webrtc::AudioDeviceName;
31 using webrtc::AudioParameters;
32 
33 namespace webrtc {
34 namespace webrtc_win {
35 namespace {
36 
37 using core_audio_utility::ErrorToString;
38 
39 // Converts from channel mask to list of included channels.
40 // Each audio data format contains channels for one or more of the positions
41 // listed below. The number of channels simply equals the number of nonzero
42 // flag bits in the `channel_mask`. The relative positions of the channels
43 // within each block of audio data always follow the same relative ordering
44 // as the flag bits in the table below. For example, if `channel_mask` contains
45 // the value 0x00000033, the format defines four audio channels that are
46 // assigned for playback to the front-left, front-right, back-left,
47 // and back-right speakers, respectively. The channel data should be interleaved
48 // in that order within each block.
ChannelMaskToString(DWORD channel_mask)49 std::string ChannelMaskToString(DWORD channel_mask) {
50   std::string ss;
51   int n = 0;
52   if (channel_mask & SPEAKER_FRONT_LEFT) {
53     ss += "FRONT_LEFT | ";
54     ++n;
55   }
56   if (channel_mask & SPEAKER_FRONT_RIGHT) {
57     ss += "FRONT_RIGHT | ";
58     ++n;
59   }
60   if (channel_mask & SPEAKER_FRONT_CENTER) {
61     ss += "FRONT_CENTER | ";
62     ++n;
63   }
64   if (channel_mask & SPEAKER_LOW_FREQUENCY) {
65     ss += "LOW_FREQUENCY | ";
66     ++n;
67   }
68   if (channel_mask & SPEAKER_BACK_LEFT) {
69     ss += "BACK_LEFT | ";
70     ++n;
71   }
72   if (channel_mask & SPEAKER_BACK_RIGHT) {
73     ss += "BACK_RIGHT | ";
74     ++n;
75   }
76   if (channel_mask & SPEAKER_FRONT_LEFT_OF_CENTER) {
77     ss += "FRONT_LEFT_OF_CENTER | ";
78     ++n;
79   }
80   if (channel_mask & SPEAKER_FRONT_RIGHT_OF_CENTER) {
81     ss += "RIGHT_OF_CENTER | ";
82     ++n;
83   }
84   if (channel_mask & SPEAKER_BACK_CENTER) {
85     ss += "BACK_CENTER | ";
86     ++n;
87   }
88   if (channel_mask & SPEAKER_SIDE_LEFT) {
89     ss += "SIDE_LEFT | ";
90     ++n;
91   }
92   if (channel_mask & SPEAKER_SIDE_RIGHT) {
93     ss += "SIDE_RIGHT | ";
94     ++n;
95   }
96   if (channel_mask & SPEAKER_TOP_CENTER) {
97     ss += "TOP_CENTER | ";
98     ++n;
99   }
100   if (channel_mask & SPEAKER_TOP_FRONT_LEFT) {
101     ss += "TOP_FRONT_LEFT | ";
102     ++n;
103   }
104   if (channel_mask & SPEAKER_TOP_FRONT_CENTER) {
105     ss += "TOP_FRONT_CENTER | ";
106     ++n;
107   }
108   if (channel_mask & SPEAKER_TOP_FRONT_RIGHT) {
109     ss += "TOP_FRONT_RIGHT | ";
110     ++n;
111   }
112   if (channel_mask & SPEAKER_TOP_BACK_LEFT) {
113     ss += "TOP_BACK_LEFT | ";
114     ++n;
115   }
116   if (channel_mask & SPEAKER_TOP_BACK_CENTER) {
117     ss += "TOP_BACK_CENTER | ";
118     ++n;
119   }
120   if (channel_mask & SPEAKER_TOP_BACK_RIGHT) {
121     ss += "TOP_BACK_RIGHT | ";
122     ++n;
123   }
124 
125   if (!ss.empty()) {
126     // Delete last appended " | " substring.
127     ss.erase(ss.end() - 3, ss.end());
128   }
129   ss += " (";
130   ss += std::to_string(n);
131   ss += ")";
132   return ss;
133 }
134 
135 #if !defined(KSAUDIO_SPEAKER_1POINT1)
136 // These values are only defined in ksmedia.h after a certain version, to build
137 // cleanly for older windows versions this just defines the ones that are
138 // missing.
139 #define KSAUDIO_SPEAKER_1POINT1 (SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY)
140 #define KSAUDIO_SPEAKER_2POINT1 \
141   (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_LOW_FREQUENCY)
142 #define KSAUDIO_SPEAKER_3POINT0 \
143   (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER)
144 #define KSAUDIO_SPEAKER_3POINT1                                      \
145   (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \
146    SPEAKER_LOW_FREQUENCY)
147 #define KSAUDIO_SPEAKER_5POINT0                                      \
148   (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \
149    SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT)
150 #define KSAUDIO_SPEAKER_7POINT0                                      \
151   (SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT | SPEAKER_FRONT_CENTER | \
152    SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT | SPEAKER_SIDE_LEFT |      \
153    SPEAKER_SIDE_RIGHT)
154 #endif
155 
156 #if !defined(AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY)
157 #define AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY 0x08000000
158 #define AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM 0x80000000
159 #endif
160 
161 // Converts the most common format tags defined in mmreg.h into string
162 // equivalents. Mainly intended for log messages.
WaveFormatTagToString(WORD format_tag)163 const char* WaveFormatTagToString(WORD format_tag) {
164   switch (format_tag) {
165     case WAVE_FORMAT_UNKNOWN:
166       return "WAVE_FORMAT_UNKNOWN";
167     case WAVE_FORMAT_PCM:
168       return "WAVE_FORMAT_PCM";
169     case WAVE_FORMAT_IEEE_FLOAT:
170       return "WAVE_FORMAT_IEEE_FLOAT";
171     case WAVE_FORMAT_EXTENSIBLE:
172       return "WAVE_FORMAT_EXTENSIBLE";
173     default:
174       return "UNKNOWN";
175   }
176 }
177 
RoleToString(const ERole role)178 const char* RoleToString(const ERole role) {
179   switch (role) {
180     case eConsole:
181       return "Console";
182     case eMultimedia:
183       return "Multimedia";
184     case eCommunications:
185       return "Communications";
186     default:
187       return "Unsupported";
188   }
189 }
190 
FlowToString(const EDataFlow flow)191 const char* FlowToString(const EDataFlow flow) {
192   switch (flow) {
193     case eRender:
194       return "Render";
195     case eCapture:
196       return "Capture";
197     case eAll:
198       return "Render or Capture";
199     default:
200       return "Unsupported";
201   }
202 }
203 
LoadAudiosesDll()204 bool LoadAudiosesDll() {
205   static const wchar_t* const kAudiosesDLL =
206       L"%WINDIR%\\system32\\audioses.dll";
207   wchar_t path[MAX_PATH] = {0};
208   ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path));
209   RTC_DLOG(LS_INFO) << rtc::ToUtf8(path);
210   return (LoadLibraryExW(path, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) !=
211           nullptr);
212 }
213 
LoadAvrtDll()214 bool LoadAvrtDll() {
215   static const wchar_t* const kAvrtDLL = L"%WINDIR%\\system32\\Avrt.dll";
216   wchar_t path[MAX_PATH] = {0};
217   ExpandEnvironmentStringsW(kAvrtDLL, path, arraysize(path));
218   RTC_DLOG(LS_INFO) << rtc::ToUtf8(path);
219   return (LoadLibraryExW(path, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH) !=
220           nullptr);
221 }
222 
CreateDeviceEnumeratorInternal(bool allow_reinitialize)223 ComPtr<IMMDeviceEnumerator> CreateDeviceEnumeratorInternal(
224     bool allow_reinitialize) {
225   ComPtr<IMMDeviceEnumerator> device_enumerator;
226   _com_error error =
227       ::CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr, CLSCTX_ALL,
228                          IID_PPV_ARGS(&device_enumerator));
229   if (FAILED(error.Error())) {
230     RTC_LOG(LS_ERROR) << "CoCreateInstance failed: " << ErrorToString(error);
231   }
232 
233   if (error.Error() == CO_E_NOTINITIALIZED && allow_reinitialize) {
234     RTC_LOG(LS_ERROR) << "CoCreateInstance failed with CO_E_NOTINITIALIZED";
235     // We have seen crashes which indicates that this method can in fact
236     // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party
237     // modules. Calling CoInitializeEx() is an attempt to resolve the reported
238     // issues. See http://crbug.com/378465 for details.
239     error = CoInitializeEx(nullptr, COINIT_MULTITHREADED);
240     if (FAILED(error.Error())) {
241       error = ::CoCreateInstance(__uuidof(MMDeviceEnumerator), nullptr,
242                                  CLSCTX_ALL, IID_PPV_ARGS(&device_enumerator));
243       if (FAILED(error.Error())) {
244         RTC_LOG(LS_ERROR) << "CoCreateInstance failed: "
245                           << ErrorToString(error);
246       }
247     }
248   }
249   return device_enumerator;
250 }
251 
IsSupportedInternal()252 bool IsSupportedInternal() {
253   // The Core Audio APIs are implemented in the user-mode system components
254   // Audioses.dll and Mmdevapi.dll. Dependency Walker shows that it is
255   // enough to verify possibility to load the Audioses DLL since it depends
256   // on Mmdevapi.dll. See http://crbug.com/166397 why this extra step is
257   // required to guarantee Core Audio support.
258   if (!LoadAudiosesDll())
259     return false;
260 
261   // Being able to load the Audioses.dll does not seem to be sufficient for
262   // all devices to guarantee Core Audio support. To be 100%, we also verify
263   // that it is possible to a create the IMMDeviceEnumerator interface. If
264   // this works as well we should be home free.
265   ComPtr<IMMDeviceEnumerator> device_enumerator =
266       CreateDeviceEnumeratorInternal(false);
267   if (!device_enumerator) {
268     RTC_LOG(LS_ERROR)
269         << "Failed to create Core Audio device enumerator on thread with ID "
270         << rtc::CurrentThreadId();
271     return false;
272   }
273 
274   return true;
275 }
276 
IsDeviceActive(IMMDevice * device)277 bool IsDeviceActive(IMMDevice* device) {
278   DWORD state = DEVICE_STATE_DISABLED;
279   return SUCCEEDED(device->GetState(&state)) && (state & DEVICE_STATE_ACTIVE);
280 }
281 
282 // Retrieve an audio device specified by `device_id` or a default device
283 // specified by data-flow direction and role if `device_id` is default.
CreateDeviceInternal(absl::string_view device_id,EDataFlow data_flow,ERole role)284 ComPtr<IMMDevice> CreateDeviceInternal(absl::string_view device_id,
285                                        EDataFlow data_flow,
286                                        ERole role) {
287   RTC_DLOG(LS_INFO) << "CreateDeviceInternal: "
288                        "id="
289                     << device_id << ", flow=" << FlowToString(data_flow)
290                     << ", role=" << RoleToString(role);
291   ComPtr<IMMDevice> audio_endpoint_device;
292 
293   // Create the IMMDeviceEnumerator interface.
294   ComPtr<IMMDeviceEnumerator> device_enum(CreateDeviceEnumeratorInternal(true));
295   if (!device_enum.Get())
296     return audio_endpoint_device;
297 
298   _com_error error(S_FALSE);
299   if (device_id == AudioDeviceName::kDefaultDeviceId) {
300     // Get the default audio endpoint for the specified data-flow direction and
301     // role. Note that, if only a single rendering or capture device is
302     // available, the system always assigns all three rendering or capture roles
303     // to that device. If the method fails to find a rendering or capture device
304     // for the specified role, this means that no rendering or capture device is
305     // available at all. If no device is available, the method sets the output
306     // pointer to NULL and returns ERROR_NOT_FOUND.
307     error = device_enum->GetDefaultAudioEndpoint(
308         data_flow, role, audio_endpoint_device.GetAddressOf());
309     if (FAILED(error.Error())) {
310       RTC_LOG(LS_ERROR)
311           << "IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: "
312           << ErrorToString(error);
313     }
314   } else {
315     // Ask for an audio endpoint device that is identified by an endpoint ID
316     // string.
317     error = device_enum->GetDevice(rtc::ToUtf16(device_id).c_str(),
318                                    audio_endpoint_device.GetAddressOf());
319     if (FAILED(error.Error())) {
320       RTC_LOG(LS_ERROR) << "IMMDeviceEnumerator::GetDevice failed: "
321                         << ErrorToString(error);
322     }
323   }
324 
325   // Verify that the audio endpoint device is active, i.e., that the audio
326   // adapter that connects to the endpoint device is present and enabled.
327   if (SUCCEEDED(error.Error()) && audio_endpoint_device.Get() &&
328       !IsDeviceActive(audio_endpoint_device.Get())) {
329     RTC_LOG(LS_WARNING) << "Selected endpoint device is not active";
330     audio_endpoint_device.Reset();
331   }
332 
333   return audio_endpoint_device;
334 }
335 
GetDeviceIdInternal(IMMDevice * device)336 std::string GetDeviceIdInternal(IMMDevice* device) {
337   // Retrieve unique name of endpoint device.
338   // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}".
339   LPWSTR device_id;
340   if (SUCCEEDED(device->GetId(&device_id))) {
341     std::string device_id_utf8 = rtc::ToUtf8(device_id, wcslen(device_id));
342     CoTaskMemFree(device_id);
343     return device_id_utf8;
344   } else {
345     return std::string();
346   }
347 }
348 
GetDeviceFriendlyNameInternal(IMMDevice * device)349 std::string GetDeviceFriendlyNameInternal(IMMDevice* device) {
350   // Retrieve user-friendly name of endpoint device.
351   // Example: "Microphone (Realtek High Definition Audio)".
352   ComPtr<IPropertyStore> properties;
353   HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.GetAddressOf());
354   if (FAILED(hr))
355     return std::string();
356 
357   ScopedPropVariant friendly_name_pv;
358   hr = properties->GetValue(PKEY_Device_FriendlyName,
359                             friendly_name_pv.Receive());
360   if (FAILED(hr))
361     return std::string();
362 
363   if (friendly_name_pv.get().vt == VT_LPWSTR &&
364       friendly_name_pv.get().pwszVal) {
365     return rtc::ToUtf8(friendly_name_pv.get().pwszVal,
366                        wcslen(friendly_name_pv.get().pwszVal));
367   } else {
368     return std::string();
369   }
370 }
371 
CreateSessionManager2Internal(IMMDevice * audio_device)372 ComPtr<IAudioSessionManager2> CreateSessionManager2Internal(
373     IMMDevice* audio_device) {
374   if (!audio_device)
375     return ComPtr<IAudioSessionManager2>();
376 
377   ComPtr<IAudioSessionManager2> audio_session_manager;
378   _com_error error =
379       audio_device->Activate(__uuidof(IAudioSessionManager2), CLSCTX_ALL,
380                              nullptr, &audio_session_manager);
381   if (FAILED(error.Error())) {
382     RTC_LOG(LS_ERROR) << "IMMDevice::Activate(IAudioSessionManager2) failed: "
383                       << ErrorToString(error);
384   }
385   return audio_session_manager;
386 }
387 
CreateSessionEnumeratorInternal(IMMDevice * audio_device)388 ComPtr<IAudioSessionEnumerator> CreateSessionEnumeratorInternal(
389     IMMDevice* audio_device) {
390   if (!audio_device) {
391     return ComPtr<IAudioSessionEnumerator>();
392   }
393 
394   ComPtr<IAudioSessionEnumerator> audio_session_enumerator;
395   ComPtr<IAudioSessionManager2> audio_session_manager =
396       CreateSessionManager2Internal(audio_device);
397   if (!audio_session_manager.Get()) {
398     return audio_session_enumerator;
399   }
400   _com_error error =
401       audio_session_manager->GetSessionEnumerator(&audio_session_enumerator);
402   if (FAILED(error.Error())) {
403     RTC_LOG(LS_ERROR)
404         << "IAudioSessionEnumerator::IAudioSessionEnumerator failed: "
405         << ErrorToString(error);
406     return ComPtr<IAudioSessionEnumerator>();
407   }
408   return audio_session_enumerator;
409 }
410 
411 // Creates and activates an IAudioClient COM object given the selected
412 // endpoint device.
CreateClientInternal(IMMDevice * audio_device)413 ComPtr<IAudioClient> CreateClientInternal(IMMDevice* audio_device) {
414   if (!audio_device)
415     return ComPtr<IAudioClient>();
416 
417   ComPtr<IAudioClient> audio_client;
418   _com_error error = audio_device->Activate(__uuidof(IAudioClient), CLSCTX_ALL,
419                                             nullptr, &audio_client);
420   if (FAILED(error.Error())) {
421     RTC_LOG(LS_ERROR) << "IMMDevice::Activate(IAudioClient) failed: "
422                       << ErrorToString(error);
423   }
424   return audio_client;
425 }
426 
CreateClient2Internal(IMMDevice * audio_device)427 ComPtr<IAudioClient2> CreateClient2Internal(IMMDevice* audio_device) {
428   if (!audio_device)
429     return ComPtr<IAudioClient2>();
430 
431   ComPtr<IAudioClient2> audio_client;
432   _com_error error = audio_device->Activate(__uuidof(IAudioClient2), CLSCTX_ALL,
433                                             nullptr, &audio_client);
434   if (FAILED(error.Error())) {
435     RTC_LOG(LS_ERROR) << "IMMDevice::Activate(IAudioClient2) failed: "
436                       << ErrorToString(error);
437   }
438   return audio_client;
439 }
440 
CreateClient3Internal(IMMDevice * audio_device)441 ComPtr<IAudioClient3> CreateClient3Internal(IMMDevice* audio_device) {
442   if (!audio_device)
443     return ComPtr<IAudioClient3>();
444 
445   ComPtr<IAudioClient3> audio_client;
446   _com_error error = audio_device->Activate(__uuidof(IAudioClient3), CLSCTX_ALL,
447                                             nullptr, &audio_client);
448   if (FAILED(error.Error())) {
449     RTC_LOG(LS_ERROR) << "IMMDevice::Activate(IAudioClient3) failed: "
450                       << ErrorToString(error);
451   }
452   return audio_client;
453 }
454 
CreateCollectionInternal(EDataFlow data_flow)455 ComPtr<IMMDeviceCollection> CreateCollectionInternal(EDataFlow data_flow) {
456   ComPtr<IMMDeviceEnumerator> device_enumerator(
457       CreateDeviceEnumeratorInternal(true));
458   if (!device_enumerator) {
459     return ComPtr<IMMDeviceCollection>();
460   }
461 
462   // Generate a collection of active (present and not disabled) audio endpoint
463   // devices for the specified data-flow direction.
464   // This method will succeed even if all devices are disabled.
465   ComPtr<IMMDeviceCollection> collection;
466   _com_error error = device_enumerator->EnumAudioEndpoints(
467       data_flow, DEVICE_STATE_ACTIVE, collection.GetAddressOf());
468   if (FAILED(error.Error())) {
469     RTC_LOG(LS_ERROR) << "IMMDeviceCollection::EnumAudioEndpoints failed: "
470                       << ErrorToString(error);
471   }
472   return collection;
473 }
474 
GetDeviceNamesInternal(EDataFlow data_flow,webrtc::AudioDeviceNames * device_names)475 bool GetDeviceNamesInternal(EDataFlow data_flow,
476                             webrtc::AudioDeviceNames* device_names) {
477   RTC_DLOG(LS_INFO) << "GetDeviceNamesInternal: flow="
478                     << FlowToString(data_flow);
479 
480   // Generate a collection of active audio endpoint devices for the specified
481   // direction.
482   ComPtr<IMMDeviceCollection> collection = CreateCollectionInternal(data_flow);
483   if (!collection.Get()) {
484     RTC_LOG(LS_ERROR) << "Failed to create a collection of active devices";
485     return false;
486   }
487 
488   // Retrieve the number of active (present, not disabled and plugged in) audio
489   // devices for the specified direction.
490   UINT number_of_active_devices = 0;
491   _com_error error = collection->GetCount(&number_of_active_devices);
492   if (FAILED(error.Error())) {
493     RTC_LOG(LS_ERROR) << "IMMDeviceCollection::GetCount failed: "
494                       << ErrorToString(error);
495     return false;
496   }
497 
498   if (number_of_active_devices == 0) {
499     RTC_DLOG(LS_WARNING) << "Found no active devices";
500     return false;
501   }
502 
503   // Loop over all active devices and add friendly name and unique id to the
504   // `device_names` queue. For now, devices are added at indexes 0, 1, ..., N-1
505   // but they will be moved to 2,3,..., N+1 at the next stage when default and
506   // default communication devices are added at index 0 and 1.
507   ComPtr<IMMDevice> audio_device;
508   for (UINT i = 0; i < number_of_active_devices; ++i) {
509     // Retrieve a pointer to the specified item in the device collection.
510     error = collection->Item(i, audio_device.GetAddressOf());
511     if (FAILED(error.Error())) {
512       // Skip this item and try to get the next item instead; will result in an
513       // incomplete list of devices.
514       RTC_LOG(LS_WARNING) << "IMMDeviceCollection::Item failed: "
515                           << ErrorToString(error);
516       continue;
517     }
518     if (!audio_device.Get()) {
519       RTC_LOG(LS_WARNING) << "Invalid audio device";
520       continue;
521     }
522 
523     // Retrieve the complete device name for the given audio device endpoint.
524     AudioDeviceName device_name(
525         GetDeviceFriendlyNameInternal(audio_device.Get()),
526         GetDeviceIdInternal(audio_device.Get()));
527     // Add combination of user-friendly and unique name to the output list.
528     device_names->push_back(device_name);
529   }
530 
531   // Log a warning of the list of device is not complete but let's keep on
532   // trying to add default and default communications device at the front.
533   if (device_names->size() != number_of_active_devices) {
534     RTC_DLOG(LS_WARNING)
535         << "List of device names does not contain all active devices";
536   }
537 
538   // Avoid adding default and default communication devices if no active device
539   // could be added to the queue. We might as well break here and return false
540   // since no active devices were identified.
541   if (device_names->empty()) {
542     RTC_DLOG(LS_ERROR) << "List of active devices is empty";
543     return false;
544   }
545 
546   // Prepend the queue with two more elements: one for the default device and
547   // one for the default communication device (can correspond to the same unique
548   // id if only one active device exists). The first element (index 0) is the
549   // default device and the second element (index 1) is the default
550   // communication device.
551   ERole role[] = {eCommunications, eConsole};
552   ComPtr<IMMDevice> default_device;
553   AudioDeviceName default_device_name;
554   for (size_t i = 0; i < arraysize(role); ++i) {
555     default_device = CreateDeviceInternal(AudioDeviceName::kDefaultDeviceId,
556                                           data_flow, role[i]);
557     if (!default_device.Get()) {
558       // Add empty strings to device name if the device could not be created.
559       RTC_DLOG(LS_WARNING) << "Failed to add device with role: "
560                            << RoleToString(role[i]);
561       default_device_name.device_name = std::string();
562       default_device_name.unique_id = std::string();
563     } else {
564       // Populate the device name with friendly name and unique id.
565       std::string device_name;
566       device_name += (role[i] == eConsole ? "Default - " : "Communication - ");
567       device_name += GetDeviceFriendlyNameInternal(default_device.Get());
568       std::string unique_id = GetDeviceIdInternal(default_device.Get());
569       default_device_name.device_name = std::move(device_name);
570       default_device_name.unique_id = std::move(unique_id);
571     }
572 
573     // Add combination of user-friendly and unique name to the output queue.
574     // The last element (<=> eConsole) will be at the front of the queue, hence
575     // at index 0. Empty strings will be added for cases where no default
576     // devices were found.
577     device_names->push_front(default_device_name);
578   }
579 
580   // Example of log output when only one device is active. Note that the queue
581   // contains two extra elements at index 0 (Default) and 1 (Communication) to
582   // allow selection of device by role instead of id. All elements corresponds
583   // the same unique id.
584   // [0] friendly name: Default - Headset Microphone (2- Arctis 7 Chat)
585   // [0] unique id    : {0.0.1.00000000}.{ff9eed76-196e-467a-b295-26986e69451c}
586   // [1] friendly name: Communication - Headset Microphone (2- Arctis 7 Chat)
587   // [1] unique id    : {0.0.1.00000000}.{ff9eed76-196e-467a-b295-26986e69451c}
588   // [2] friendly name: Headset Microphone (2- Arctis 7 Chat)
589   // [2] unique id    : {0.0.1.00000000}.{ff9eed76-196e-467a-b295-26986e69451c}
590   for (size_t i = 0; i < device_names->size(); ++i) {
591     RTC_DLOG(LS_INFO) << "[" << i
592                       << "] friendly name: " << (*device_names)[i].device_name;
593     RTC_DLOG(LS_INFO) << "[" << i
594                       << "] unique id    : " << (*device_names)[i].unique_id;
595   }
596 
597   return true;
598 }
599 
GetPreferredAudioParametersInternal(IAudioClient * client,AudioParameters * params,int fixed_sample_rate)600 HRESULT GetPreferredAudioParametersInternal(IAudioClient* client,
601                                             AudioParameters* params,
602                                             int fixed_sample_rate) {
603   WAVEFORMATPCMEX mix_format;
604   HRESULT hr = core_audio_utility::GetSharedModeMixFormat(client, &mix_format);
605   if (FAILED(hr))
606     return hr;
607 
608   REFERENCE_TIME default_period = 0;
609   hr = core_audio_utility::GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED,
610                                            &default_period);
611   if (FAILED(hr))
612     return hr;
613 
614   int sample_rate = mix_format.Format.nSamplesPerSec;
615   // Override default sample rate if `fixed_sample_rate` is set and different
616   // from the default rate.
617   if (fixed_sample_rate > 0 && fixed_sample_rate != sample_rate) {
618     RTC_DLOG(LS_INFO) << "Using fixed sample rate instead of the preferred: "
619                       << sample_rate << " is replaced by " << fixed_sample_rate;
620     sample_rate = fixed_sample_rate;
621   }
622   // TODO(henrika): utilize full mix_format.Format.wBitsPerSample.
623   // const size_t bits_per_sample = AudioParameters::kBitsPerSample;
624   // TODO(henrika): improve channel layout support.
625   const size_t channels = mix_format.Format.nChannels;
626 
627   // Use the native device period to derive the smallest possible buffer size
628   // in shared mode.
629   double device_period_in_seconds =
630       static_cast<double>(
631           core_audio_utility::ReferenceTimeToTimeDelta(default_period).ms()) /
632       1000.0L;
633   const size_t frames_per_buffer =
634       static_cast<size_t>(sample_rate * device_period_in_seconds + 0.5);
635 
636   AudioParameters audio_params(sample_rate, channels, frames_per_buffer);
637   *params = audio_params;
638   RTC_DLOG(LS_INFO) << audio_params.ToString();
639 
640   return hr;
641 }
642 
643 }  // namespace
644 
645 namespace core_audio_utility {
646 
647 // core_audio_utility::WaveFormatWrapper implementation.
GetExtensible() const648 WAVEFORMATEXTENSIBLE* WaveFormatWrapper::GetExtensible() const {
649   RTC_CHECK(IsExtensible());
650   return reinterpret_cast<WAVEFORMATEXTENSIBLE*>(ptr_);
651 }
652 
IsExtensible() const653 bool WaveFormatWrapper::IsExtensible() const {
654   return ptr_->wFormatTag == WAVE_FORMAT_EXTENSIBLE && ptr_->cbSize >= 22;
655 }
656 
IsPcm() const657 bool WaveFormatWrapper::IsPcm() const {
658   return IsExtensible() ? GetExtensible()->SubFormat == KSDATAFORMAT_SUBTYPE_PCM
659                         : ptr_->wFormatTag == WAVE_FORMAT_PCM;
660 }
661 
IsFloat() const662 bool WaveFormatWrapper::IsFloat() const {
663   return IsExtensible()
664              ? GetExtensible()->SubFormat == KSDATAFORMAT_SUBTYPE_IEEE_FLOAT
665              : ptr_->wFormatTag == WAVE_FORMAT_IEEE_FLOAT;
666 }
667 
size() const668 size_t WaveFormatWrapper::size() const {
669   return sizeof(*ptr_) + ptr_->cbSize;
670 }
671 
IsSupported()672 bool IsSupported() {
673   RTC_DLOG(LS_INFO) << "IsSupported";
674   static bool g_is_supported = IsSupportedInternal();
675   return g_is_supported;
676 }
677 
IsMMCSSSupported()678 bool IsMMCSSSupported() {
679   RTC_DLOG(LS_INFO) << "IsMMCSSSupported";
680   return LoadAvrtDll();
681 }
682 
NumberOfActiveDevices(EDataFlow data_flow)683 int NumberOfActiveDevices(EDataFlow data_flow) {
684   // Generate a collection of active audio endpoint devices for the specified
685   // data-flow direction.
686   ComPtr<IMMDeviceCollection> collection = CreateCollectionInternal(data_flow);
687   if (!collection.Get()) {
688     return 0;
689   }
690 
691   // Retrieve the number of active audio devices for the specified direction.
692   UINT number_of_active_devices = 0;
693   collection->GetCount(&number_of_active_devices);
694   std::string str;
695   if (data_flow == eCapture) {
696     str = "Number of capture devices: ";
697   } else if (data_flow == eRender) {
698     str = "Number of render devices: ";
699   } else if (data_flow == eAll) {
700     str = "Total number of devices: ";
701   }
702   RTC_DLOG(LS_INFO) << str << number_of_active_devices;
703   return static_cast<int>(number_of_active_devices);
704 }
705 
GetAudioClientVersion()706 uint32_t GetAudioClientVersion() {
707   uint32_t version = 1;
708   if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::VERSION_WIN10) {
709     version = 3;
710   } else if (rtc::rtc_win::GetVersion() >= rtc::rtc_win::VERSION_WIN8) {
711     version = 2;
712   }
713   return version;
714 }
715 
CreateDeviceEnumerator()716 ComPtr<IMMDeviceEnumerator> CreateDeviceEnumerator() {
717   RTC_DLOG(LS_INFO) << "CreateDeviceEnumerator";
718   return CreateDeviceEnumeratorInternal(true);
719 }
720 
GetDefaultInputDeviceID()721 std::string GetDefaultInputDeviceID() {
722   RTC_DLOG(LS_INFO) << "GetDefaultInputDeviceID";
723   ComPtr<IMMDevice> device(
724       CreateDevice(AudioDeviceName::kDefaultDeviceId, eCapture, eConsole));
725   return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string();
726 }
727 
GetDefaultOutputDeviceID()728 std::string GetDefaultOutputDeviceID() {
729   RTC_DLOG(LS_INFO) << "GetDefaultOutputDeviceID";
730   ComPtr<IMMDevice> device(
731       CreateDevice(AudioDeviceName::kDefaultDeviceId, eRender, eConsole));
732   return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string();
733 }
734 
GetCommunicationsInputDeviceID()735 std::string GetCommunicationsInputDeviceID() {
736   RTC_DLOG(LS_INFO) << "GetCommunicationsInputDeviceID";
737   ComPtr<IMMDevice> device(CreateDevice(AudioDeviceName::kDefaultDeviceId,
738                                         eCapture, eCommunications));
739   return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string();
740 }
741 
GetCommunicationsOutputDeviceID()742 std::string GetCommunicationsOutputDeviceID() {
743   RTC_DLOG(LS_INFO) << "GetCommunicationsOutputDeviceID";
744   ComPtr<IMMDevice> device(CreateDevice(AudioDeviceName::kDefaultDeviceId,
745                                         eRender, eCommunications));
746   return device.Get() ? GetDeviceIdInternal(device.Get()) : std::string();
747 }
748 
CreateDevice(absl::string_view device_id,EDataFlow data_flow,ERole role)749 ComPtr<IMMDevice> CreateDevice(absl::string_view device_id,
750                                EDataFlow data_flow,
751                                ERole role) {
752   RTC_DLOG(LS_INFO) << "CreateDevice";
753   return CreateDeviceInternal(device_id, data_flow, role);
754 }
755 
GetDeviceName(IMMDevice * device)756 AudioDeviceName GetDeviceName(IMMDevice* device) {
757   RTC_DLOG(LS_INFO) << "GetDeviceName";
758   RTC_DCHECK(device);
759   AudioDeviceName device_name(GetDeviceFriendlyNameInternal(device),
760                               GetDeviceIdInternal(device));
761   RTC_DLOG(LS_INFO) << "friendly name: " << device_name.device_name;
762   RTC_DLOG(LS_INFO) << "unique id    : " << device_name.unique_id;
763   return device_name;
764 }
765 
GetFriendlyName(absl::string_view device_id,EDataFlow data_flow,ERole role)766 std::string GetFriendlyName(absl::string_view device_id,
767                             EDataFlow data_flow,
768                             ERole role) {
769   RTC_DLOG(LS_INFO) << "GetFriendlyName";
770   ComPtr<IMMDevice> audio_device = CreateDevice(device_id, data_flow, role);
771   if (!audio_device.Get())
772     return std::string();
773 
774   AudioDeviceName device_name = GetDeviceName(audio_device.Get());
775   return device_name.device_name;
776 }
777 
GetDataFlow(IMMDevice * device)778 EDataFlow GetDataFlow(IMMDevice* device) {
779   RTC_DLOG(LS_INFO) << "GetDataFlow";
780   RTC_DCHECK(device);
781   ComPtr<IMMEndpoint> endpoint;
782   _com_error error = device->QueryInterface(endpoint.GetAddressOf());
783   if (FAILED(error.Error())) {
784     RTC_LOG(LS_ERROR) << "IMMDevice::QueryInterface failed: "
785                       << ErrorToString(error);
786     return eAll;
787   }
788 
789   EDataFlow data_flow;
790   error = endpoint->GetDataFlow(&data_flow);
791   if (FAILED(error.Error())) {
792     RTC_LOG(LS_ERROR) << "IMMEndpoint::GetDataFlow failed: "
793                       << ErrorToString(error);
794     return eAll;
795   }
796   return data_flow;
797 }
798 
GetInputDeviceNames(webrtc::AudioDeviceNames * device_names)799 bool GetInputDeviceNames(webrtc::AudioDeviceNames* device_names) {
800   RTC_DLOG(LS_INFO) << "GetInputDeviceNames";
801   RTC_DCHECK(device_names);
802   RTC_DCHECK(device_names->empty());
803   return GetDeviceNamesInternal(eCapture, device_names);
804 }
805 
GetOutputDeviceNames(webrtc::AudioDeviceNames * device_names)806 bool GetOutputDeviceNames(webrtc::AudioDeviceNames* device_names) {
807   RTC_DLOG(LS_INFO) << "GetOutputDeviceNames";
808   RTC_DCHECK(device_names);
809   RTC_DCHECK(device_names->empty());
810   return GetDeviceNamesInternal(eRender, device_names);
811 }
812 
CreateSessionManager2(IMMDevice * device)813 ComPtr<IAudioSessionManager2> CreateSessionManager2(IMMDevice* device) {
814   RTC_DLOG(LS_INFO) << "CreateSessionManager2";
815   return CreateSessionManager2Internal(device);
816 }
817 
CreateSessionEnumerator(IMMDevice * device)818 Microsoft::WRL::ComPtr<IAudioSessionEnumerator> CreateSessionEnumerator(
819     IMMDevice* device) {
820   RTC_DLOG(LS_INFO) << "CreateSessionEnumerator";
821   return CreateSessionEnumeratorInternal(device);
822 }
823 
NumberOfActiveSessions(IMMDevice * device)824 int NumberOfActiveSessions(IMMDevice* device) {
825   RTC_DLOG(LS_INFO) << "NumberOfActiveSessions";
826   ComPtr<IAudioSessionEnumerator> session_enumerator =
827       CreateSessionEnumerator(device);
828 
829   // Iterate over all audio sessions for the given device.
830   int session_count = 0;
831   _com_error error = session_enumerator->GetCount(&session_count);
832   if (FAILED(error.Error())) {
833     RTC_LOG(LS_ERROR) << "IAudioSessionEnumerator::GetCount failed: "
834                       << ErrorToString(error);
835     return 0;
836   }
837   RTC_DLOG(LS_INFO) << "Total number of audio sessions: " << session_count;
838 
839   int num_active = 0;
840   for (int session = 0; session < session_count; session++) {
841     // Acquire the session control interface.
842     ComPtr<IAudioSessionControl> session_control;
843     error = session_enumerator->GetSession(session, &session_control);
844     if (FAILED(error.Error())) {
845       RTC_LOG(LS_ERROR) << "IAudioSessionEnumerator::GetSession failed: "
846                         << ErrorToString(error);
847       return 0;
848     }
849 
850     // Log the display name of the audio session for debugging purposes.
851     LPWSTR display_name;
852     if (SUCCEEDED(session_control->GetDisplayName(&display_name))) {
853       RTC_DLOG(LS_INFO) << "display name: "
854                         << rtc::ToUtf8(display_name, wcslen(display_name));
855       CoTaskMemFree(display_name);
856     }
857 
858     // Get the current state and check if the state is active or not.
859     AudioSessionState state;
860     error = session_control->GetState(&state);
861     if (FAILED(error.Error())) {
862       RTC_LOG(LS_ERROR) << "IAudioSessionControl::GetState failed: "
863                         << ErrorToString(error);
864       return 0;
865     }
866     if (state == AudioSessionStateActive) {
867       ++num_active;
868     }
869   }
870 
871   RTC_DLOG(LS_INFO) << "Number of active audio sessions: " << num_active;
872   return num_active;
873 }
874 
CreateClient(absl::string_view device_id,EDataFlow data_flow,ERole role)875 ComPtr<IAudioClient> CreateClient(absl::string_view device_id,
876                                   EDataFlow data_flow,
877                                   ERole role) {
878   RTC_DLOG(LS_INFO) << "CreateClient";
879   ComPtr<IMMDevice> device(CreateDevice(device_id, data_flow, role));
880   return CreateClientInternal(device.Get());
881 }
882 
CreateClient2(absl::string_view device_id,EDataFlow data_flow,ERole role)883 ComPtr<IAudioClient2> CreateClient2(absl::string_view device_id,
884                                     EDataFlow data_flow,
885                                     ERole role) {
886   RTC_DLOG(LS_INFO) << "CreateClient2";
887   ComPtr<IMMDevice> device(CreateDevice(device_id, data_flow, role));
888   return CreateClient2Internal(device.Get());
889 }
890 
CreateClient3(absl::string_view device_id,EDataFlow data_flow,ERole role)891 ComPtr<IAudioClient3> CreateClient3(absl::string_view device_id,
892                                     EDataFlow data_flow,
893                                     ERole role) {
894   RTC_DLOG(LS_INFO) << "CreateClient3";
895   ComPtr<IMMDevice> device(CreateDevice(device_id, data_flow, role));
896   return CreateClient3Internal(device.Get());
897 }
898 
SetClientProperties(IAudioClient2 * client)899 HRESULT SetClientProperties(IAudioClient2* client) {
900   RTC_DLOG(LS_INFO) << "SetClientProperties";
901   RTC_DCHECK(client);
902   if (GetAudioClientVersion() < 2) {
903     RTC_LOG(LS_WARNING) << "Requires IAudioClient2 or higher";
904     return AUDCLNT_E_UNSUPPORTED_FORMAT;
905   }
906   AudioClientProperties props = {0};
907   props.cbSize = sizeof(AudioClientProperties);
908   // Real-time VoIP communication.
909   // TODO(henrika): other categories?
910   props.eCategory = AudioCategory_Communications;
911   // Hardware-offloaded audio processing allows the main audio processing tasks
912   // to be performed outside the computer's main CPU. Check support and log the
913   // result but hard-code `bIsOffload` to FALSE for now.
914   // TODO(henrika): evaluate hardware-offloading. Might complicate usage of
915   // IAudioClient::GetMixFormat().
916   BOOL supports_offload = FALSE;
917   _com_error error =
918       client->IsOffloadCapable(props.eCategory, &supports_offload);
919   if (FAILED(error.Error())) {
920     RTC_LOG(LS_ERROR) << "IAudioClient2::IsOffloadCapable failed: "
921                       << ErrorToString(error);
922   }
923   RTC_DLOG(LS_INFO) << "supports_offload: " << supports_offload;
924   props.bIsOffload = false;
925 #if (NTDDI_VERSION < NTDDI_WINBLUE)
926   RTC_DLOG(LS_INFO) << "options: Not supported in this build";
927 #else
928   // TODO(henrika): pros and cons compared with AUDCLNT_STREAMOPTIONS_NONE?
929   props.Options |= AUDCLNT_STREAMOPTIONS_NONE;
930   // Requires System.Devices.AudioDevice.RawProcessingSupported.
931   // The application can choose to *always ignore* the OEM AEC/AGC by setting
932   // the AUDCLNT_STREAMOPTIONS_RAW flag in the call to SetClientProperties.
933   // This flag will preserve the user experience aspect of Communications
934   // streams, but will not insert any OEM provided communications specific
935   // processing in the audio signal path.
936   // props.Options |= AUDCLNT_STREAMOPTIONS_RAW;
937 
938   // If it is important to avoid resampling in the audio engine, set this flag.
939   // AUDCLNT_STREAMOPTIONS_MATCH_FORMAT (or anything in IAudioClient3) is not
940   // an appropriate interface to use for communications scenarios.
941   // This interface is mainly meant for pro audio scenarios.
942   // props.Options |= AUDCLNT_STREAMOPTIONS_MATCH_FORMAT;
943   RTC_DLOG(LS_INFO) << "options: 0x" << rtc::ToHex(props.Options);
944 #endif
945   error = client->SetClientProperties(&props);
946   if (FAILED(error.Error())) {
947     RTC_LOG(LS_ERROR) << "IAudioClient2::SetClientProperties failed: "
948                       << ErrorToString(error);
949   }
950   return error.Error();
951 }
952 
GetBufferSizeLimits(IAudioClient2 * client,const WAVEFORMATEXTENSIBLE * format,REFERENCE_TIME * min_buffer_duration,REFERENCE_TIME * max_buffer_duration)953 HRESULT GetBufferSizeLimits(IAudioClient2* client,
954                             const WAVEFORMATEXTENSIBLE* format,
955                             REFERENCE_TIME* min_buffer_duration,
956                             REFERENCE_TIME* max_buffer_duration) {
957   RTC_DLOG(LS_INFO) << "GetBufferSizeLimits";
958   RTC_DCHECK(client);
959   if (GetAudioClientVersion() < 2) {
960     RTC_LOG(LS_WARNING) << "Requires IAudioClient2 or higher";
961     return AUDCLNT_E_UNSUPPORTED_FORMAT;
962   }
963   REFERENCE_TIME min_duration = 0;
964   REFERENCE_TIME max_duration = 0;
965   _com_error error =
966       client->GetBufferSizeLimits(reinterpret_cast<const WAVEFORMATEX*>(format),
967                                   TRUE, &min_duration, &max_duration);
968   if (error.Error() == AUDCLNT_E_OFFLOAD_MODE_ONLY) {
969     // This API seems to be supported in off-load mode only but it is not
970     // documented as a valid error code. Making a special note about it here.
971     RTC_LOG(LS_ERROR) << "IAudioClient2::GetBufferSizeLimits failed: "
972                          "AUDCLNT_E_OFFLOAD_MODE_ONLY";
973   } else if (FAILED(error.Error())) {
974     RTC_LOG(LS_ERROR) << "IAudioClient2::GetBufferSizeLimits failed: "
975                       << ErrorToString(error);
976   } else {
977     *min_buffer_duration = min_duration;
978     *max_buffer_duration = max_duration;
979     RTC_DLOG(LS_INFO) << "min_buffer_duration: " << min_buffer_duration;
980     RTC_DLOG(LS_INFO) << "max_buffer_duration: " << max_buffer_duration;
981   }
982   return error.Error();
983 }
984 
GetSharedModeMixFormat(IAudioClient * client,WAVEFORMATEXTENSIBLE * format)985 HRESULT GetSharedModeMixFormat(IAudioClient* client,
986                                WAVEFORMATEXTENSIBLE* format) {
987   RTC_DLOG(LS_INFO) << "GetSharedModeMixFormat";
988   RTC_DCHECK(client);
989 
990   // The GetMixFormat method retrieves the stream format that the audio engine
991   // uses for its internal processing of shared-mode streams. The method
992   // allocates the storage for the structure and this memory will be released
993   // when `mix_format` goes out of scope. The GetMixFormat method retrieves a
994   // format descriptor that is in the form of a WAVEFORMATEXTENSIBLE structure
995   // instead of a standalone WAVEFORMATEX structure. The method outputs a
996   // pointer to the WAVEFORMATEX structure that is embedded at the start of
997   // this WAVEFORMATEXTENSIBLE structure.
998   // Note that, crbug/803056 indicates that some devices can return a format
999   // where only the WAVEFORMATEX parts is initialized and we must be able to
1000   // account for that.
1001   ScopedCoMem<WAVEFORMATEXTENSIBLE> mix_format;
1002   _com_error error =
1003       client->GetMixFormat(reinterpret_cast<WAVEFORMATEX**>(&mix_format));
1004   if (FAILED(error.Error())) {
1005     RTC_LOG(LS_ERROR) << "IAudioClient::GetMixFormat failed: "
1006                       << ErrorToString(error);
1007     return error.Error();
1008   }
1009 
1010   // Use a wave format wrapper to make things simpler.
1011   WaveFormatWrapper wrapped_format(mix_format.Get());
1012 
1013   // Verify that the reported format can be mixed by the audio engine in
1014   // shared mode.
1015   if (!wrapped_format.IsPcm() && !wrapped_format.IsFloat()) {
1016     RTC_DLOG(LS_ERROR)
1017         << "Only pure PCM or float audio streams can be mixed in shared mode";
1018     return AUDCLNT_E_UNSUPPORTED_FORMAT;
1019   }
1020 
1021   // Log a warning for the rare case where `mix_format` only contains a
1022   // stand-alone WAVEFORMATEX structure but don't return.
1023   if (!wrapped_format.IsExtensible()) {
1024     RTC_DLOG(LS_WARNING)
1025         << "The returned format contains no extended information. "
1026            "The size is "
1027         << wrapped_format.size() << " bytes.";
1028   }
1029 
1030   // Copy the correct number of bytes into |*format| taking into account if
1031   // the returned structure is correctly extended or not.
1032   RTC_CHECK_LE(wrapped_format.size(), sizeof(WAVEFORMATEXTENSIBLE));
1033   memcpy(format, wrapped_format.get(), wrapped_format.size());
1034   RTC_DLOG(LS_INFO) << WaveFormatToString(format);
1035 
1036   return error.Error();
1037 }
1038 
IsFormatSupported(IAudioClient * client,AUDCLNT_SHAREMODE share_mode,const WAVEFORMATEXTENSIBLE * format)1039 bool IsFormatSupported(IAudioClient* client,
1040                        AUDCLNT_SHAREMODE share_mode,
1041                        const WAVEFORMATEXTENSIBLE* format) {
1042   RTC_DLOG(LS_INFO) << "IsFormatSupported";
1043   RTC_DCHECK(client);
1044   ScopedCoMem<WAVEFORMATEX> closest_match;
1045   // This method provides a way for a client to determine, before calling
1046   // IAudioClient::Initialize, whether the audio engine supports a particular
1047   // stream format or not. In shared mode, the audio engine always supports
1048   // the mix format (see GetSharedModeMixFormat).
1049   // TODO(henrika): verify support for exclusive mode as well?
1050   _com_error error = client->IsFormatSupported(
1051       share_mode, reinterpret_cast<const WAVEFORMATEX*>(format),
1052       &closest_match);
1053   RTC_LOG(LS_INFO) << WaveFormatToString(
1054       const_cast<WAVEFORMATEXTENSIBLE*>(format));
1055   if ((error.Error() == S_OK) && (closest_match == nullptr)) {
1056     RTC_DLOG(LS_INFO)
1057         << "The audio endpoint device supports the specified stream format";
1058   } else if ((error.Error() == S_FALSE) && (closest_match != nullptr)) {
1059     // Call succeeded with a closest match to the specified format. This log can
1060     // only be triggered for shared mode.
1061     RTC_LOG(LS_WARNING)
1062         << "Exact format is not supported, but a closest match exists";
1063     RTC_LOG(LS_INFO) << WaveFormatToString(closest_match.Get());
1064   } else if ((error.Error() == AUDCLNT_E_UNSUPPORTED_FORMAT) &&
1065              (closest_match == nullptr)) {
1066     // The audio engine does not support the caller-specified format or any
1067     // similar format.
1068     RTC_DLOG(LS_INFO) << "The audio endpoint device does not support the "
1069                          "specified stream format";
1070   } else {
1071     RTC_LOG(LS_ERROR) << "IAudioClient::IsFormatSupported failed: "
1072                       << ErrorToString(error);
1073   }
1074 
1075   return (error.Error() == S_OK);
1076 }
1077 
GetDevicePeriod(IAudioClient * client,AUDCLNT_SHAREMODE share_mode,REFERENCE_TIME * device_period)1078 HRESULT GetDevicePeriod(IAudioClient* client,
1079                         AUDCLNT_SHAREMODE share_mode,
1080                         REFERENCE_TIME* device_period) {
1081   RTC_DLOG(LS_INFO) << "GetDevicePeriod";
1082   RTC_DCHECK(client);
1083   // The `default_period` parameter specifies the default scheduling period
1084   // for a shared-mode stream. The `minimum_period` parameter specifies the
1085   // minimum scheduling period for an exclusive-mode stream.
1086   // The time is expressed in 100-nanosecond units.
1087   REFERENCE_TIME default_period = 0;
1088   REFERENCE_TIME minimum_period = 0;
1089   _com_error error = client->GetDevicePeriod(&default_period, &minimum_period);
1090   if (FAILED(error.Error())) {
1091     RTC_LOG(LS_ERROR) << "IAudioClient::GetDevicePeriod failed: "
1092                       << ErrorToString(error);
1093     return error.Error();
1094   }
1095 
1096   *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period
1097                                                             : minimum_period;
1098   RTC_LOG(LS_INFO) << "device_period: "
1099                    << ReferenceTimeToTimeDelta(*device_period).ms() << " [ms]";
1100   RTC_LOG(LS_INFO) << "minimum_period: "
1101                    << ReferenceTimeToTimeDelta(minimum_period).ms() << " [ms]";
1102   return error.Error();
1103 }
1104 
GetSharedModeEnginePeriod(IAudioClient3 * client3,const WAVEFORMATEXTENSIBLE * format,uint32_t * default_period_in_frames,uint32_t * fundamental_period_in_frames,uint32_t * min_period_in_frames,uint32_t * max_period_in_frames)1105 HRESULT GetSharedModeEnginePeriod(IAudioClient3* client3,
1106                                   const WAVEFORMATEXTENSIBLE* format,
1107                                   uint32_t* default_period_in_frames,
1108                                   uint32_t* fundamental_period_in_frames,
1109                                   uint32_t* min_period_in_frames,
1110                                   uint32_t* max_period_in_frames) {
1111   RTC_DLOG(LS_INFO) << "GetSharedModeEnginePeriod";
1112   RTC_DCHECK(client3);
1113 
1114   UINT32 default_period = 0;
1115   UINT32 fundamental_period = 0;
1116   UINT32 min_period = 0;
1117   UINT32 max_period = 0;
1118   _com_error error = client3->GetSharedModeEnginePeriod(
1119       reinterpret_cast<const WAVEFORMATEX*>(format), &default_period,
1120       &fundamental_period, &min_period, &max_period);
1121   if (FAILED(error.Error())) {
1122     RTC_LOG(LS_ERROR) << "IAudioClient3::GetSharedModeEnginePeriod failed: "
1123                       << ErrorToString(error);
1124     return error.Error();
1125   }
1126 
1127   WAVEFORMATEX format_ex = format->Format;
1128   const WORD sample_rate = format_ex.nSamplesPerSec;
1129   RTC_LOG(LS_INFO) << "default_period_in_frames: " << default_period << " ("
1130                    << FramesToMilliseconds(default_period, sample_rate)
1131                    << " ms)";
1132   RTC_LOG(LS_INFO) << "fundamental_period_in_frames: " << fundamental_period
1133                    << " ("
1134                    << FramesToMilliseconds(fundamental_period, sample_rate)
1135                    << " ms)";
1136   RTC_LOG(LS_INFO) << "min_period_in_frames: " << min_period << " ("
1137                    << FramesToMilliseconds(min_period, sample_rate) << " ms)";
1138   RTC_LOG(LS_INFO) << "max_period_in_frames: " << max_period << " ("
1139                    << FramesToMilliseconds(max_period, sample_rate) << " ms)";
1140   *default_period_in_frames = default_period;
1141   *fundamental_period_in_frames = fundamental_period;
1142   *min_period_in_frames = min_period;
1143   *max_period_in_frames = max_period;
1144   return error.Error();
1145 }
1146 
GetPreferredAudioParameters(IAudioClient * client,AudioParameters * params)1147 HRESULT GetPreferredAudioParameters(IAudioClient* client,
1148                                     AudioParameters* params) {
1149   RTC_DLOG(LS_INFO) << "GetPreferredAudioParameters";
1150   RTC_DCHECK(client);
1151   return GetPreferredAudioParametersInternal(client, params, -1);
1152 }
1153 
GetPreferredAudioParameters(IAudioClient * client,webrtc::AudioParameters * params,uint32_t sample_rate)1154 HRESULT GetPreferredAudioParameters(IAudioClient* client,
1155                                     webrtc::AudioParameters* params,
1156                                     uint32_t sample_rate) {
1157   RTC_DLOG(LS_INFO) << "GetPreferredAudioParameters: " << sample_rate;
1158   RTC_DCHECK(client);
1159   return GetPreferredAudioParametersInternal(client, params, sample_rate);
1160 }
1161 
SharedModeInitialize(IAudioClient * client,const WAVEFORMATEXTENSIBLE * format,HANDLE event_handle,REFERENCE_TIME buffer_duration,bool auto_convert_pcm,uint32_t * endpoint_buffer_size)1162 HRESULT SharedModeInitialize(IAudioClient* client,
1163                              const WAVEFORMATEXTENSIBLE* format,
1164                              HANDLE event_handle,
1165                              REFERENCE_TIME buffer_duration,
1166                              bool auto_convert_pcm,
1167                              uint32_t* endpoint_buffer_size) {
1168   RTC_DLOG(LS_INFO) << "SharedModeInitialize: buffer_duration="
1169                     << buffer_duration
1170                     << ", auto_convert_pcm=" << auto_convert_pcm;
1171   RTC_DCHECK(client);
1172   RTC_DCHECK_GE(buffer_duration, 0);
1173   if (buffer_duration != 0) {
1174     RTC_DLOG(LS_WARNING) << "Non-default buffer size is used";
1175   }
1176   if (auto_convert_pcm) {
1177     RTC_DLOG(LS_WARNING) << "Sample rate converter can be utilized";
1178   }
1179   // The AUDCLNT_STREAMFLAGS_NOPERSIST flag disables persistence of the volume
1180   // and mute settings for a session that contains rendering streams.
1181   // By default, the volume level and muting state for a rendering session are
1182   // persistent across system restarts. The volume level and muting state for a
1183   // capture session are never persistent.
1184   DWORD stream_flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
1185 
1186   // Enable event-driven streaming if a valid event handle is provided.
1187   // After the stream starts, the audio engine will signal the event handle
1188   // to notify the client each time a buffer becomes ready to process.
1189   // Event-driven buffering is supported for both rendering and capturing.
1190   // Both shared-mode and exclusive-mode streams can use event-driven buffering.
1191   bool use_event =
1192       (event_handle != nullptr && event_handle != INVALID_HANDLE_VALUE);
1193   if (use_event) {
1194     stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
1195     RTC_DLOG(LS_INFO) << "The stream is initialized to be event driven";
1196   }
1197 
1198   // Check if sample-rate conversion is requested.
1199   if (auto_convert_pcm) {
1200     // Add channel matrixer (not utilized here) and rate converter to convert
1201     // from our (the client's) format to the audio engine mix format.
1202     // Currently only supported for testing, i.e., not possible to enable using
1203     // public APIs.
1204     RTC_DLOG(LS_INFO) << "The stream is initialized to support rate conversion";
1205     stream_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
1206     stream_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
1207   }
1208   RTC_DLOG(LS_INFO) << "stream_flags: 0x" << rtc::ToHex(stream_flags);
1209 
1210   // Initialize the shared mode client for minimal delay if `buffer_duration`
1211   // is 0 or possibly a higher delay (more robust) if `buffer_duration` is
1212   // larger than 0. The actual size is given by IAudioClient::GetBufferSize().
1213   _com_error error = client->Initialize(
1214       AUDCLNT_SHAREMODE_SHARED, stream_flags, buffer_duration, 0,
1215       reinterpret_cast<const WAVEFORMATEX*>(format), nullptr);
1216   if (FAILED(error.Error())) {
1217     RTC_LOG(LS_ERROR) << "IAudioClient::Initialize failed: "
1218                       << ErrorToString(error);
1219     return error.Error();
1220   }
1221 
1222   // If a stream is initialized to be event driven and in shared mode, the
1223   // associated application must also obtain a handle by making a call to
1224   // IAudioClient::SetEventHandle.
1225   if (use_event) {
1226     error = client->SetEventHandle(event_handle);
1227     if (FAILED(error.Error())) {
1228       RTC_LOG(LS_ERROR) << "IAudioClient::SetEventHandle failed: "
1229                         << ErrorToString(error);
1230       return error.Error();
1231     }
1232   }
1233 
1234   UINT32 buffer_size_in_frames = 0;
1235   // Retrieves the size (maximum capacity) of the endpoint buffer. The size is
1236   // expressed as the number of audio frames the buffer can hold.
1237   // For rendering clients, the buffer length determines the maximum amount of
1238   // rendering data that the application can write to the endpoint buffer
1239   // during a single processing pass. For capture clients, the buffer length
1240   // determines the maximum amount of capture data that the audio engine can
1241   // read from the endpoint buffer during a single processing pass.
1242   error = client->GetBufferSize(&buffer_size_in_frames);
1243   if (FAILED(error.Error())) {
1244     RTC_LOG(LS_ERROR) << "IAudioClient::GetBufferSize failed: "
1245                       << ErrorToString(error);
1246     return error.Error();
1247   }
1248 
1249   *endpoint_buffer_size = buffer_size_in_frames;
1250   RTC_DLOG(LS_INFO) << "endpoint buffer size: " << buffer_size_in_frames
1251                     << " [audio frames]";
1252   const double size_in_ms = static_cast<double>(buffer_size_in_frames) /
1253                             (format->Format.nSamplesPerSec / 1000.0);
1254   RTC_DLOG(LS_INFO) << "endpoint buffer size: "
1255                     << static_cast<int>(size_in_ms + 0.5) << " [ms]";
1256   RTC_DLOG(LS_INFO) << "bytes per audio frame: " << format->Format.nBlockAlign;
1257   RTC_DLOG(LS_INFO) << "endpoint buffer size: "
1258                     << buffer_size_in_frames * format->Format.nChannels *
1259                            (format->Format.wBitsPerSample / 8)
1260                     << " [bytes]";
1261 
1262   // TODO(henrika): utilize when delay measurements are added.
1263   REFERENCE_TIME latency = 0;
1264   error = client->GetStreamLatency(&latency);
1265   RTC_DLOG(LS_INFO) << "stream latency: "
1266                     << ReferenceTimeToTimeDelta(latency).ms() << " [ms]";
1267   return error.Error();
1268 }
1269 
SharedModeInitializeLowLatency(IAudioClient3 * client,const WAVEFORMATEXTENSIBLE * format,HANDLE event_handle,uint32_t period_in_frames,bool auto_convert_pcm,uint32_t * endpoint_buffer_size)1270 HRESULT SharedModeInitializeLowLatency(IAudioClient3* client,
1271                                        const WAVEFORMATEXTENSIBLE* format,
1272                                        HANDLE event_handle,
1273                                        uint32_t period_in_frames,
1274                                        bool auto_convert_pcm,
1275                                        uint32_t* endpoint_buffer_size) {
1276   RTC_DLOG(LS_INFO) << "SharedModeInitializeLowLatency: period_in_frames="
1277                     << period_in_frames
1278                     << ", auto_convert_pcm=" << auto_convert_pcm;
1279   RTC_DCHECK(client);
1280   RTC_DCHECK_GT(period_in_frames, 0);
1281   if (auto_convert_pcm) {
1282     RTC_DLOG(LS_WARNING) << "Sample rate converter is enabled";
1283   }
1284 
1285   // Define stream flags.
1286   DWORD stream_flags = AUDCLNT_STREAMFLAGS_NOPERSIST;
1287   bool use_event =
1288       (event_handle != nullptr && event_handle != INVALID_HANDLE_VALUE);
1289   if (use_event) {
1290     stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
1291     RTC_DLOG(LS_INFO) << "The stream is initialized to be event driven";
1292   }
1293   if (auto_convert_pcm) {
1294     stream_flags |= AUDCLNT_STREAMFLAGS_AUTOCONVERTPCM;
1295     stream_flags |= AUDCLNT_STREAMFLAGS_SRC_DEFAULT_QUALITY;
1296   }
1297   RTC_DLOG(LS_INFO) << "stream_flags: 0x" << rtc::ToHex(stream_flags);
1298 
1299   // Initialize the shared mode client for lowest possible latency.
1300   // It is assumed that GetSharedModeEnginePeriod() has been used to query the
1301   // smallest possible engine period and that it is given by `period_in_frames`.
1302   _com_error error = client->InitializeSharedAudioStream(
1303       stream_flags, period_in_frames,
1304       reinterpret_cast<const WAVEFORMATEX*>(format), nullptr);
1305   if (FAILED(error.Error())) {
1306     RTC_LOG(LS_ERROR) << "IAudioClient3::InitializeSharedAudioStream failed: "
1307                       << ErrorToString(error);
1308     return error.Error();
1309   }
1310 
1311   // Set the event handle.
1312   if (use_event) {
1313     error = client->SetEventHandle(event_handle);
1314     if (FAILED(error.Error())) {
1315       RTC_LOG(LS_ERROR) << "IAudioClient::SetEventHandle failed: "
1316                         << ErrorToString(error);
1317       return error.Error();
1318     }
1319   }
1320 
1321   UINT32 buffer_size_in_frames = 0;
1322   // Retrieve the size (maximum capacity) of the endpoint buffer.
1323   error = client->GetBufferSize(&buffer_size_in_frames);
1324   if (FAILED(error.Error())) {
1325     RTC_LOG(LS_ERROR) << "IAudioClient::GetBufferSize failed: "
1326                       << ErrorToString(error);
1327     return error.Error();
1328   }
1329 
1330   *endpoint_buffer_size = buffer_size_in_frames;
1331   RTC_DLOG(LS_INFO) << "endpoint buffer size: " << buffer_size_in_frames
1332                     << " [audio frames]";
1333   const double size_in_ms = static_cast<double>(buffer_size_in_frames) /
1334                             (format->Format.nSamplesPerSec / 1000.0);
1335   RTC_DLOG(LS_INFO) << "endpoint buffer size: "
1336                     << static_cast<int>(size_in_ms + 0.5) << " [ms]";
1337   RTC_DLOG(LS_INFO) << "bytes per audio frame: " << format->Format.nBlockAlign;
1338   RTC_DLOG(LS_INFO) << "endpoint buffer size: "
1339                     << buffer_size_in_frames * format->Format.nChannels *
1340                            (format->Format.wBitsPerSample / 8)
1341                     << " [bytes]";
1342 
1343   // TODO(henrika): utilize when delay measurements are added.
1344   REFERENCE_TIME latency = 0;
1345   error = client->GetStreamLatency(&latency);
1346   if (FAILED(error.Error())) {
1347     RTC_LOG(LS_WARNING) << "IAudioClient::GetStreamLatency failed: "
1348                         << ErrorToString(error);
1349   } else {
1350     RTC_DLOG(LS_INFO) << "stream latency: "
1351                       << ReferenceTimeToTimeDelta(latency).ms() << " [ms]";
1352   }
1353   return error.Error();
1354 }
1355 
CreateRenderClient(IAudioClient * client)1356 ComPtr<IAudioRenderClient> CreateRenderClient(IAudioClient* client) {
1357   RTC_DLOG(LS_INFO) << "CreateRenderClient";
1358   RTC_DCHECK(client);
1359   // Get access to the IAudioRenderClient interface. This interface
1360   // enables us to write output data to a rendering endpoint buffer.
1361   ComPtr<IAudioRenderClient> audio_render_client;
1362   _com_error error = client->GetService(IID_PPV_ARGS(&audio_render_client));
1363   if (FAILED(error.Error())) {
1364     RTC_LOG(LS_ERROR)
1365         << "IAudioClient::GetService(IID_IAudioRenderClient) failed: "
1366         << ErrorToString(error);
1367     return ComPtr<IAudioRenderClient>();
1368   }
1369   return audio_render_client;
1370 }
1371 
CreateCaptureClient(IAudioClient * client)1372 ComPtr<IAudioCaptureClient> CreateCaptureClient(IAudioClient* client) {
1373   RTC_DLOG(LS_INFO) << "CreateCaptureClient";
1374   RTC_DCHECK(client);
1375   // Get access to the IAudioCaptureClient interface. This interface
1376   // enables us to read input data from a capturing endpoint buffer.
1377   ComPtr<IAudioCaptureClient> audio_capture_client;
1378   _com_error error = client->GetService(IID_PPV_ARGS(&audio_capture_client));
1379   if (FAILED(error.Error())) {
1380     RTC_LOG(LS_ERROR)
1381         << "IAudioClient::GetService(IID_IAudioCaptureClient) failed: "
1382         << ErrorToString(error);
1383     return ComPtr<IAudioCaptureClient>();
1384   }
1385   return audio_capture_client;
1386 }
1387 
CreateAudioClock(IAudioClient * client)1388 ComPtr<IAudioClock> CreateAudioClock(IAudioClient* client) {
1389   RTC_DLOG(LS_INFO) << "CreateAudioClock";
1390   RTC_DCHECK(client);
1391   // Get access to the IAudioClock interface. This interface enables us to
1392   // monitor a stream's data rate and the current position in the stream.
1393   ComPtr<IAudioClock> audio_clock;
1394   _com_error error = client->GetService(IID_PPV_ARGS(&audio_clock));
1395   if (FAILED(error.Error())) {
1396     RTC_LOG(LS_ERROR) << "IAudioClient::GetService(IID_IAudioClock) failed: "
1397                       << ErrorToString(error);
1398     return ComPtr<IAudioClock>();
1399   }
1400   return audio_clock;
1401 }
1402 
CreateAudioSessionControl(IAudioClient * client)1403 ComPtr<IAudioSessionControl> CreateAudioSessionControl(IAudioClient* client) {
1404   RTC_DLOG(LS_INFO) << "CreateAudioSessionControl";
1405   RTC_DCHECK(client);
1406   ComPtr<IAudioSessionControl> audio_session_control;
1407   _com_error error = client->GetService(IID_PPV_ARGS(&audio_session_control));
1408   if (FAILED(error.Error())) {
1409     RTC_LOG(LS_ERROR) << "IAudioClient::GetService(IID_IAudioControl) failed: "
1410                       << ErrorToString(error);
1411     return ComPtr<IAudioSessionControl>();
1412   }
1413   return audio_session_control;
1414 }
1415 
CreateSimpleAudioVolume(IAudioClient * client)1416 ComPtr<ISimpleAudioVolume> CreateSimpleAudioVolume(IAudioClient* client) {
1417   RTC_DLOG(LS_INFO) << "CreateSimpleAudioVolume";
1418   RTC_DCHECK(client);
1419   // Get access to the ISimpleAudioVolume interface. This interface enables a
1420   // client to control the master volume level of an audio session.
1421   ComPtr<ISimpleAudioVolume> simple_audio_volume;
1422   _com_error error = client->GetService(IID_PPV_ARGS(&simple_audio_volume));
1423   if (FAILED(error.Error())) {
1424     RTC_LOG(LS_ERROR)
1425         << "IAudioClient::GetService(IID_ISimpleAudioVolume) failed: "
1426         << ErrorToString(error);
1427     return ComPtr<ISimpleAudioVolume>();
1428   }
1429   return simple_audio_volume;
1430 }
1431 
FillRenderEndpointBufferWithSilence(IAudioClient * client,IAudioRenderClient * render_client)1432 bool FillRenderEndpointBufferWithSilence(IAudioClient* client,
1433                                          IAudioRenderClient* render_client) {
1434   RTC_DLOG(LS_INFO) << "FillRenderEndpointBufferWithSilence";
1435   RTC_DCHECK(client);
1436   RTC_DCHECK(render_client);
1437   UINT32 endpoint_buffer_size = 0;
1438   _com_error error = client->GetBufferSize(&endpoint_buffer_size);
1439   if (FAILED(error.Error())) {
1440     RTC_LOG(LS_ERROR) << "IAudioClient::GetBufferSize failed: "
1441                       << ErrorToString(error);
1442     return false;
1443   }
1444 
1445   UINT32 num_queued_frames = 0;
1446   // Get number of audio frames that are queued up to play in the endpoint
1447   // buffer.
1448   error = client->GetCurrentPadding(&num_queued_frames);
1449   if (FAILED(error.Error())) {
1450     RTC_LOG(LS_ERROR) << "IAudioClient::GetCurrentPadding failed: "
1451                       << ErrorToString(error);
1452     return false;
1453   }
1454   RTC_DLOG(LS_INFO) << "num_queued_frames: " << num_queued_frames;
1455 
1456   BYTE* data = nullptr;
1457   int num_frames_to_fill = endpoint_buffer_size - num_queued_frames;
1458   RTC_DLOG(LS_INFO) << "num_frames_to_fill: " << num_frames_to_fill;
1459   error = render_client->GetBuffer(num_frames_to_fill, &data);
1460   if (FAILED(error.Error())) {
1461     RTC_LOG(LS_ERROR) << "IAudioRenderClient::GetBuffer failed: "
1462                       << ErrorToString(error);
1463     return false;
1464   }
1465 
1466   // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to
1467   // explicitly write silence data to the rendering buffer.
1468   error = render_client->ReleaseBuffer(num_frames_to_fill,
1469                                        AUDCLNT_BUFFERFLAGS_SILENT);
1470   if (FAILED(error.Error())) {
1471     RTC_LOG(LS_ERROR) << "IAudioRenderClient::ReleaseBuffer failed: "
1472                       << ErrorToString(error);
1473     return false;
1474   }
1475 
1476   return true;
1477 }
1478 
WaveFormatToString(const WaveFormatWrapper format)1479 std::string WaveFormatToString(const WaveFormatWrapper format) {
1480   char ss_buf[1024];
1481   rtc::SimpleStringBuilder ss(ss_buf);
1482   // Start with the WAVEFORMATEX part (which always exists).
1483   ss.AppendFormat("wFormatTag: %s (0x%X)",
1484                   WaveFormatTagToString(format->wFormatTag),
1485                   format->wFormatTag);
1486   ss.AppendFormat(", nChannels: %d", format->nChannels);
1487   ss.AppendFormat(", nSamplesPerSec: %d", format->nSamplesPerSec);
1488   ss.AppendFormat(", nAvgBytesPerSec: %d", format->nAvgBytesPerSec);
1489   ss.AppendFormat(", nBlockAlign: %d", format->nBlockAlign);
1490   ss.AppendFormat(", wBitsPerSample: %d", format->wBitsPerSample);
1491   ss.AppendFormat(", cbSize: %d", format->cbSize);
1492   if (!format.IsExtensible())
1493     return ss.str();
1494 
1495   // Append the WAVEFORMATEXTENSIBLE part (which we know exists).
1496   ss.AppendFormat(
1497       " [+] wValidBitsPerSample: %d, dwChannelMask: %s",
1498       format.GetExtensible()->Samples.wValidBitsPerSample,
1499       ChannelMaskToString(format.GetExtensible()->dwChannelMask).c_str());
1500   if (format.IsPcm()) {
1501     ss.AppendFormat("%s", ", SubFormat: KSDATAFORMAT_SUBTYPE_PCM");
1502   } else if (format.IsFloat()) {
1503     ss.AppendFormat("%s", ", SubFormat: KSDATAFORMAT_SUBTYPE_IEEE_FLOAT");
1504   } else {
1505     ss.AppendFormat("%s", ", SubFormat: NOT_SUPPORTED");
1506   }
1507   return ss.str();
1508 }
1509 
ReferenceTimeToTimeDelta(REFERENCE_TIME time)1510 webrtc::TimeDelta ReferenceTimeToTimeDelta(REFERENCE_TIME time) {
1511   // Each unit of reference time is 100 nanoseconds <=> 0.1 microsecond.
1512   return webrtc::TimeDelta::Micros(0.1 * time + 0.5);
1513 }
1514 
FramesToMilliseconds(uint32_t num_frames,uint16_t sample_rate)1515 double FramesToMilliseconds(uint32_t num_frames, uint16_t sample_rate) {
1516   // Convert the current period in frames into milliseconds.
1517   return static_cast<double>(num_frames) / (sample_rate / 1000.0);
1518 }
1519 
ErrorToString(const _com_error & error)1520 std::string ErrorToString(const _com_error& error) {
1521   char ss_buf[1024];
1522   rtc::SimpleStringBuilder ss(ss_buf);
1523   ss.AppendFormat("(HRESULT: 0x%08X)", error.Error());
1524   return ss.str();
1525 }
1526 
1527 }  // namespace core_audio_utility
1528 }  // namespace webrtc_win
1529 }  // namespace webrtc
1530