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