2 * Copyright (C) 2008 Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>
3 * Copyright (C) 2018 Centricular Ltd.
4 * Author: Nirbheek Chauhan <nirbheek@centricular.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
25 #include "gstwasapiutil.h"
26 #include "gstwasapidevice.h"
28 GST_DEBUG_CATEGORY_EXTERN (gst_wasapi_debug);
29 #define GST_CAT_DEFAULT gst_wasapi_debug
31 /* This was only added to MinGW in ~2015 and our Cerbero toolchain is too old */
33 #include <functiondiscoverykeys_devpkey.h>
34 #elif !defined(PKEY_Device_FriendlyName)
37 DEFINE_PROPERTYKEY (PKEY_Device_FriendlyName, 0xa45c254e, 0xdf1c, 0x4efd, 0x80,
38 0x20, 0x67, 0xd1, 0x46, 0xa8, 0x50, 0xe0, 14);
39 DEFINE_PROPERTYKEY (PKEY_AudioEngine_DeviceFormat, 0xf19f064d, 0x82c, 0x4e27,
40 0xbc, 0x73, 0x68, 0x82, 0xa1, 0xbb, 0x8e, 0x4c, 0);
45 const CLSID CLSID_MMDeviceEnumerator = __uuidof (MMDeviceEnumerator);
46 const IID IID_IMMDeviceEnumerator = __uuidof (IMMDeviceEnumerator);
47 const IID IID_IMMEndpoint = __uuidof (IMMEndpoint);
48 const IID IID_IAudioClient = __uuidof (IAudioClient);
49 const IID IID_IAudioRenderClient = __uuidof (IAudioRenderClient);
50 const IID IID_IAudioCaptureClient = __uuidof (IAudioCaptureClient);
51 const IID IID_IAudioClock = __uuidof (IAudioClock);
53 /* __uuidof is not implemented in our Cerbero's ancient MinGW toolchain so we
54 * hard-code the GUID values for all these. This is ok because these are ABI. */
55 const CLSID CLSID_MMDeviceEnumerator = { 0xbcde0395, 0xe52f, 0x467c,
56 {0x8e, 0x3d, 0xc4, 0x57, 0x92, 0x91, 0x69, 0x2e}
59 const IID IID_IMMDeviceEnumerator = { 0xa95664d2, 0x9614, 0x4f35,
60 {0xa7, 0x46, 0xde, 0x8d, 0xb6, 0x36, 0x17, 0xe6}
63 const IID IID_IMMEndpoint = { 0x1be09788, 0x6894, 0x4089,
64 {0x85, 0x86, 0x9a, 0x2a, 0x6c, 0x26, 0x5a, 0xc5}
67 const IID IID_IAudioClient = { 0x1cb9ad4c, 0xdbfa, 0x4c32,
68 {0xb1, 0x78, 0xc2, 0xf5, 0x68, 0xa7, 0x03, 0xb2}
71 const IID IID_IAudioClock = { 0xcd63314f, 0x3fba, 0x4a1b,
72 {0x81, 0x2c, 0xef, 0x96, 0x35, 0x87, 0x28, 0xe7}
75 const IID IID_IAudioCaptureClient = { 0xc8adbd64, 0xe71e, 0x48a0,
76 {0xa4, 0xde, 0x18, 0x5c, 0x39, 0x5c, 0xd3, 0x17}
79 const IID IID_IAudioRenderClient = { 0xf294acfc, 0x3146, 0x4483,
80 {0xa7, 0xbf, 0xad, 0xdc, 0xa7, 0xc2, 0x60, 0xe2}
87 GstAudioChannelPosition gst_pos;
88 } wasapi_to_gst_pos[] = {
89 {SPEAKER_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT},
90 {SPEAKER_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT},
91 {SPEAKER_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_FRONT_CENTER},
92 {SPEAKER_LOW_FREQUENCY, GST_AUDIO_CHANNEL_POSITION_LFE1},
93 {SPEAKER_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_REAR_LEFT},
94 {SPEAKER_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_REAR_RIGHT},
95 {SPEAKER_FRONT_LEFT_OF_CENTER,
96 GST_AUDIO_CHANNEL_POSITION_FRONT_LEFT_OF_CENTER},
97 {SPEAKER_FRONT_RIGHT_OF_CENTER,
98 GST_AUDIO_CHANNEL_POSITION_FRONT_RIGHT_OF_CENTER},
99 {SPEAKER_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_REAR_CENTER},
100 /* Enum values diverge from this point onwards */
101 {SPEAKER_SIDE_LEFT, GST_AUDIO_CHANNEL_POSITION_SIDE_LEFT},
102 {SPEAKER_SIDE_RIGHT, GST_AUDIO_CHANNEL_POSITION_SIDE_RIGHT},
103 {SPEAKER_TOP_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_CENTER},
104 {SPEAKER_TOP_FRONT_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_LEFT},
105 {SPEAKER_TOP_FRONT_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_CENTER},
106 {SPEAKER_TOP_FRONT_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_FRONT_RIGHT},
107 {SPEAKER_TOP_BACK_LEFT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_LEFT},
108 {SPEAKER_TOP_BACK_CENTER, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_CENTER},
109 {SPEAKER_TOP_BACK_RIGHT, GST_AUDIO_CHANNEL_POSITION_TOP_REAR_RIGHT}
113 gst_wasapi_device_role_get_type (void)
115 static const GEnumValue values[] = {
116 {GST_WASAPI_DEVICE_ROLE_CONSOLE,
117 "Games, system notifications, voice commands", "console"},
118 {GST_WASAPI_DEVICE_ROLE_MULTIMEDIA, "Music, movies, recorded media",
120 {GST_WASAPI_DEVICE_ROLE_COMMS, "Voice communications", "comms"},
123 static volatile GType id = 0;
125 if (g_once_init_enter ((gsize *) & id)) {
128 _id = g_enum_register_static ("GstWasapiDeviceRole", values);
130 g_once_init_leave ((gsize *) & id, _id);
137 gst_wasapi_device_role_to_erole (gint role)
140 case GST_WASAPI_DEVICE_ROLE_CONSOLE:
142 case GST_WASAPI_DEVICE_ROLE_MULTIMEDIA:
144 case GST_WASAPI_DEVICE_ROLE_COMMS:
145 return eCommunications;
147 g_assert_not_reached ();
152 gst_wasapi_erole_to_device_role (gint erole)
156 return GST_WASAPI_DEVICE_ROLE_CONSOLE;
158 return GST_WASAPI_DEVICE_ROLE_MULTIMEDIA;
159 case eCommunications:
160 return GST_WASAPI_DEVICE_ROLE_COMMS;
162 g_assert_not_reached ();
167 hresult_to_string_fallback (HRESULT hr)
169 const gchar *s = "unknown error";
172 case AUDCLNT_E_NOT_INITIALIZED:
173 s = "AUDCLNT_E_NOT_INITIALIZED";
175 case AUDCLNT_E_ALREADY_INITIALIZED:
176 s = "AUDCLNT_E_ALREADY_INITIALIZED";
178 case AUDCLNT_E_WRONG_ENDPOINT_TYPE:
179 s = "AUDCLNT_E_WRONG_ENDPOINT_TYPE";
181 case AUDCLNT_E_DEVICE_INVALIDATED:
182 s = "AUDCLNT_E_DEVICE_INVALIDATED";
184 case AUDCLNT_E_NOT_STOPPED:
185 s = "AUDCLNT_E_NOT_STOPPED";
187 case AUDCLNT_E_BUFFER_TOO_LARGE:
188 s = "AUDCLNT_E_BUFFER_TOO_LARGE";
190 case AUDCLNT_E_OUT_OF_ORDER:
191 s = "AUDCLNT_E_OUT_OF_ORDER";
193 case AUDCLNT_E_UNSUPPORTED_FORMAT:
194 s = "AUDCLNT_E_UNSUPPORTED_FORMAT";
196 case AUDCLNT_E_INVALID_DEVICE_PERIOD:
197 s = "AUDCLNT_E_INVALID_DEVICE_PERIOD";
199 case AUDCLNT_E_INVALID_SIZE:
200 s = "AUDCLNT_E_INVALID_SIZE";
202 case AUDCLNT_E_DEVICE_IN_USE:
203 s = "AUDCLNT_E_DEVICE_IN_USE";
205 case AUDCLNT_E_BUFFER_OPERATION_PENDING:
206 s = "AUDCLNT_E_BUFFER_OPERATION_PENDING";
208 case AUDCLNT_E_BUFFER_SIZE_ERROR:
209 s = "AUDCLNT_E_BUFFER_SIZE_ERROR";
211 case AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED:
212 s = "AUDCLNT_E_BUFFER_SIZE_NOT_ALIGNED";
214 case AUDCLNT_E_THREAD_NOT_REGISTERED:
215 s = "AUDCLNT_E_THREAD_NOT_REGISTERED";
217 case AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED:
218 s = "AUDCLNT_E_EXCLUSIVE_MODE_NOT_ALLOWED";
220 case AUDCLNT_E_ENDPOINT_CREATE_FAILED:
221 s = "AUDCLNT_E_ENDPOINT_CREATE_FAILED";
223 case AUDCLNT_E_SERVICE_NOT_RUNNING:
224 s = "AUDCLNT_E_SERVICE_NOT_RUNNING";
226 case AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED:
227 s = "AUDCLNT_E_EVENTHANDLE_NOT_EXPECTED";
229 case AUDCLNT_E_EXCLUSIVE_MODE_ONLY:
230 s = "AUDCLNT_E_EXCLUSIVE_MODE_ONLY";
232 case AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL:
233 s = "AUDCLNT_E_BUFDURATION_PERIOD_NOT_EQUAL";
235 case AUDCLNT_E_EVENTHANDLE_NOT_SET:
236 s = "AUDCLNT_E_EVENTHANDLE_NOT_SET";
238 case AUDCLNT_E_INCORRECT_BUFFER_SIZE:
239 s = "AUDCLNT_E_INCORRECT_BUFFER_SIZE";
241 case AUDCLNT_E_CPUUSAGE_EXCEEDED:
242 s = "AUDCLNT_E_CPUUSAGE_EXCEEDED";
244 case AUDCLNT_S_BUFFER_EMPTY:
245 s = "AUDCLNT_S_BUFFER_EMPTY";
247 case AUDCLNT_S_THREAD_ALREADY_REGISTERED:
248 s = "AUDCLNT_S_THREAD_ALREADY_REGISTERED";
250 case AUDCLNT_S_POSITION_STALLED:
251 s = "AUDCLNT_S_POSITION_STALLED";
265 gst_wasapi_util_hresult_to_string (HRESULT hr)
269 LPTSTR error_text = NULL;
271 flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_ALLOCATE_BUFFER
272 | FORMAT_MESSAGE_IGNORE_INSERTS;
273 FormatMessage (flags, NULL, hr, MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
274 (LPTSTR) & error_text, 0, NULL);
276 /* If we couldn't get the error msg, try the fallback switch statement */
277 if (error_text == NULL)
278 return g_strdup (hresult_to_string_fallback (hr));
281 /* If UNICODE is defined, LPTSTR is LPWSTR which is UTF-16 */
282 ret_text = g_utf16_to_utf8 (error_text, 0, NULL, NULL, NULL);
284 ret_text = g_strdup (error_text);
287 LocalFree (error_text);
291 static IMMDeviceEnumerator *
292 gst_wasapi_util_get_device_enumerator (GstElement * self)
295 IMMDeviceEnumerator *enumerator = NULL;
297 hr = CoCreateInstance (&CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL,
298 &IID_IMMDeviceEnumerator, (void **) &enumerator);
299 HR_FAILED_RET (hr, CoCreateInstance (MMDeviceEnumerator), NULL);
305 gst_wasapi_util_get_devices (GstElement * self, gboolean active,
308 gboolean res = FALSE;
309 static GstStaticCaps scaps = GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS);
310 DWORD dwStateMask = active ? DEVICE_STATE_ACTIVE : DEVICE_STATEMASK_ALL;
311 IMMDeviceCollection *device_collection = NULL;
312 IMMDeviceEnumerator *enumerator = NULL;
313 const gchar *device_class, *element_name;
319 enumerator = gst_wasapi_util_get_device_enumerator (self);
323 hr = IMMDeviceEnumerator_EnumAudioEndpoints (enumerator, eAll, dwStateMask,
325 HR_FAILED_GOTO (hr, IMMDeviceEnumerator::EnumAudioEndpoints, err);
327 hr = IMMDeviceCollection_GetCount (device_collection, &count);
328 HR_FAILED_GOTO (hr, IMMDeviceCollection::GetCount, err);
330 /* Create a GList of GstDevices* to return */
331 for (ii = 0; ii < count; ii++) {
332 IMMDevice *item = NULL;
333 IMMEndpoint *endpoint = NULL;
334 IAudioClient *client = NULL;
335 IPropertyStore *prop_store = NULL;
336 WAVEFORMATEX *format = NULL;
337 gchar *description = NULL;
346 hr = IMMDeviceCollection_Item (device_collection, ii, &item);
350 hr = IMMDevice_QueryInterface (item, &IID_IMMEndpoint, (void **) &endpoint);
354 hr = IMMEndpoint_GetDataFlow (endpoint, &dataflow);
358 if (dataflow == eRender) {
359 device_class = "Audio/Sink";
360 element_name = "wasapisink";
362 device_class = "Audio/Source";
363 element_name = "wasapisrc";
366 PropVariantInit (&var);
368 hr = IMMDevice_GetId (item, &wstrid);
371 strid = g_utf16_to_utf8 (wstrid, -1, NULL, NULL, NULL);
372 CoTaskMemFree (wstrid);
374 hr = IMMDevice_OpenPropertyStore (item, STGM_READ, &prop_store);
378 /* NOTE: More properties can be added as needed from here:
379 * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370794(v=vs.85).aspx */
380 hr = IPropertyStore_GetValue (prop_store, &PKEY_Device_FriendlyName, &var);
383 description = g_utf16_to_utf8 (var.pwszVal, -1, NULL, NULL, NULL);
384 PropVariantClear (&var);
386 /* Get the audio client so we can fetch the mix format for shared mode
387 * to get the device format for exclusive mode (or something close to that)
388 * fetch PKEY_AudioEngine_DeviceFormat from the property store. */
389 hr = IMMDevice_Activate (item, &IID_IAudioClient, CLSCTX_ALL, NULL,
392 gchar *msg = gst_wasapi_util_hresult_to_string (hr);
393 GST_ERROR_OBJECT (self, "IMMDevice::Activate (IID_IAudioClient) failed"
394 "on %s: %s", strid, msg);
399 hr = IAudioClient_GetMixFormat (client, &format);
400 if (hr != S_OK || format == NULL) {
401 gchar *msg = gst_wasapi_util_hresult_to_string (hr);
402 GST_ERROR_OBJECT ("GetMixFormat failed on %s: %s", strid, msg);
407 if (!gst_wasapi_util_parse_waveformatex ((WAVEFORMATEXTENSIBLE *) format,
408 gst_static_caps_get (&scaps), &caps, NULL))
411 /* Set some useful properties */
412 props = gst_structure_new ("wasapi-proplist",
413 "device.api", G_TYPE_STRING, "wasapi",
414 "device.strid", G_TYPE_STRING, GST_STR_NULL (strid),
415 "wasapi.device.description", G_TYPE_STRING, description, NULL);
417 device = g_object_new (GST_TYPE_WASAPI_DEVICE, "device", strid,
418 "display-name", description, "caps", caps,
419 "device-class", device_class, "properties", props, NULL);
420 GST_WASAPI_DEVICE (device)->element = element_name;
422 gst_structure_free (props);
423 gst_caps_unref (caps);
424 *devices = g_list_prepend (*devices, device);
427 PropVariantClear (&var);
429 IUnknown_Release (prop_store);
431 IUnknown_Release (endpoint);
433 IUnknown_Release (client);
435 IUnknown_Release (item);
437 g_free (description);
446 IUnknown_Release (enumerator);
447 if (device_collection)
448 IUnknown_Release (device_collection);
453 gst_wasapi_util_get_device_format (GstElement * self,
454 gint device_mode, IMMDevice * device, IAudioClient * client,
455 WAVEFORMATEX ** ret_format)
457 WAVEFORMATEX *format;
462 hr = IAudioClient_GetMixFormat (client, &format);
463 HR_FAILED_RET (hr, IAudioClient::GetMixFormat, FALSE);
465 /* WASAPI always accepts the format returned by GetMixFormat in shared mode */
466 if (device_mode == AUDCLNT_SHAREMODE_SHARED)
469 /* WASAPI may or may not support this format in exclusive mode */
470 hr = IAudioClient_IsFormatSupported (client, AUDCLNT_SHAREMODE_EXCLUSIVE,
475 CoTaskMemFree (format);
477 /* Open the device property store, and get the format that WASAPI has been
478 * using for sending data to the device */
481 IPropertyStore *prop_store = NULL;
483 hr = IMMDevice_OpenPropertyStore (device, STGM_READ, &prop_store);
484 HR_FAILED_RET (hr, IMMDevice::OpenPropertyStore, FALSE);
486 hr = IPropertyStore_GetValue (prop_store, &PKEY_AudioEngine_DeviceFormat,
489 gchar *msg = gst_wasapi_util_hresult_to_string (hr);
490 GST_ERROR_OBJECT (self, "GetValue failed: %s", msg);
492 IUnknown_Release (prop_store);
496 format = malloc (var.blob.cbSize);
497 memcpy (format, var.blob.pBlobData, var.blob.cbSize);
499 PropVariantClear (&var);
500 IUnknown_Release (prop_store);
503 /* WASAPI may or may not support this format in exclusive mode */
504 hr = IAudioClient_IsFormatSupported (client, AUDCLNT_SHAREMODE_EXCLUSIVE,
509 GST_ERROR_OBJECT (self, "AudioEngine DeviceFormat not supported");
514 *ret_format = format;
519 gst_wasapi_util_get_device_client (GstElement * self,
520 gboolean capture, gint role, const wchar_t * device_strid,
521 IMMDevice ** ret_device, IAudioClient ** ret_client)
523 gboolean res = FALSE;
525 IMMDeviceEnumerator *enumerator = NULL;
526 IMMDevice *device = NULL;
527 IAudioClient *client = NULL;
529 if (!(enumerator = gst_wasapi_util_get_device_enumerator (self)))
533 hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator,
534 capture ? eCapture : eRender, role, &device);
535 HR_FAILED_GOTO (hr, IMMDeviceEnumerator::GetDefaultAudioEndpoint, beach);
537 hr = IMMDeviceEnumerator_GetDevice (enumerator, device_strid, &device);
539 gchar *msg = gst_wasapi_util_hresult_to_string (hr);
540 GST_ERROR_OBJECT (self, "IMMDeviceEnumerator::GetDevice (%S) failed"
541 ": %s", device_strid, msg);
547 hr = IMMDevice_Activate (device, &IID_IAudioClient, CLSCTX_ALL, NULL,
549 HR_FAILED_GOTO (hr, IMMDevice::Activate (IID_IAudioClient), beach);
551 IUnknown_AddRef (client);
552 IUnknown_AddRef (device);
553 *ret_client = client;
554 *ret_device = device;
560 IUnknown_Release (client);
563 IUnknown_Release (device);
565 if (enumerator != NULL)
566 IUnknown_Release (enumerator);
572 gst_wasapi_util_get_render_client (GstElement * self, IAudioClient * client,
573 IAudioRenderClient ** ret_render_client)
575 gboolean res = FALSE;
577 IAudioRenderClient *render_client = NULL;
579 hr = IAudioClient_GetService (client, &IID_IAudioRenderClient,
580 (void **) &render_client);
581 HR_FAILED_GOTO (hr, IAudioClient::GetService, beach);
583 *ret_render_client = render_client;
591 gst_wasapi_util_get_capture_client (GstElement * self, IAudioClient * client,
592 IAudioCaptureClient ** ret_capture_client)
594 gboolean res = FALSE;
596 IAudioCaptureClient *capture_client = NULL;
598 hr = IAudioClient_GetService (client, &IID_IAudioCaptureClient,
599 (void **) &capture_client);
600 HR_FAILED_GOTO (hr, IAudioClient::GetService, beach);
602 *ret_capture_client = capture_client;
610 gst_wasapi_util_get_clock (GstElement * self, IAudioClient * client,
611 IAudioClock ** ret_clock)
613 gboolean res = FALSE;
615 IAudioClock *clock = NULL;
617 hr = IAudioClient_GetService (client, &IID_IAudioClock, (void **) &clock);
618 HR_FAILED_GOTO (hr, IAudioClient::GetService, beach);
628 gst_waveformatex_to_audio_format (WAVEFORMATEXTENSIBLE * format)
630 const gchar *fmt_str = NULL;
631 GstAudioFormat fmt = GST_AUDIO_FORMAT_UNKNOWN;
633 if (format->Format.wFormatTag == WAVE_FORMAT_PCM) {
634 fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN,
635 format->Format.wBitsPerSample, format->Format.wBitsPerSample);
636 } else if (format->Format.wFormatTag == WAVE_FORMAT_IEEE_FLOAT) {
637 if (format->Format.wBitsPerSample == 32)
638 fmt = GST_AUDIO_FORMAT_F32LE;
639 else if (format->Format.wBitsPerSample == 64)
640 fmt = GST_AUDIO_FORMAT_F64LE;
641 } else if (format->Format.wFormatTag == WAVE_FORMAT_EXTENSIBLE) {
642 if (IsEqualGUID (&format->SubFormat, &KSDATAFORMAT_SUBTYPE_PCM)) {
643 fmt = gst_audio_format_build_integer (TRUE, G_LITTLE_ENDIAN,
644 format->Format.wBitsPerSample, format->Samples.wValidBitsPerSample);
645 } else if (IsEqualGUID (&format->SubFormat,
646 &KSDATAFORMAT_SUBTYPE_IEEE_FLOAT)) {
647 if (format->Format.wBitsPerSample == 32
648 && format->Samples.wValidBitsPerSample == 32)
649 fmt = GST_AUDIO_FORMAT_F32LE;
650 else if (format->Format.wBitsPerSample == 64 &&
651 format->Samples.wValidBitsPerSample == 64)
652 fmt = GST_AUDIO_FORMAT_F64LE;
656 if (fmt != GST_AUDIO_FORMAT_UNKNOWN)
657 fmt_str = gst_audio_format_to_string (fmt);
663 gst_wasapi_util_channel_position_all_none (guint channels,
664 GstAudioChannelPosition * position)
667 for (ii = 0; ii < channels; ii++)
668 position[ii] = GST_AUDIO_CHANNEL_POSITION_NONE;
671 /* Parse WAVEFORMATEX to get the gstreamer channel mask, and the wasapi channel
672 * positions so GstAudioRingbuffer can reorder the audio data to match the
673 * gstreamer channel order. */
675 gst_wasapi_util_waveformatex_to_channel_mask (WAVEFORMATEXTENSIBLE * format,
676 GstAudioChannelPosition ** out_position)
680 WORD nChannels = format->Format.nChannels;
681 DWORD dwChannelMask = format->dwChannelMask;
682 GstAudioChannelPosition *pos = NULL;
684 pos = g_new (GstAudioChannelPosition, nChannels);
685 gst_wasapi_util_channel_position_all_none (nChannels, pos);
687 /* Too many channels, have to assume that they are all non-positional */
688 if (nChannels > G_N_ELEMENTS (wasapi_to_gst_pos)) {
689 GST_INFO ("wasapi: got too many (%i) channels, assuming non-positional",
694 /* Too many bits in the channel mask, and the bits don't match nChannels */
695 if (dwChannelMask >> (G_N_ELEMENTS (wasapi_to_gst_pos) + 1) != 0) {
696 GST_WARNING ("wasapi: too many bits in channel mask (%lu), assuming "
697 "non-positional", dwChannelMask);
701 /* Map WASAPI's channel mask to Gstreamer's channel mask and positions.
702 * If the no. of bits in the mask > nChannels, we will ignore the extra. */
703 for (ii = 0; ii < nChannels; ii++) {
704 if (!(dwChannelMask & wasapi_to_gst_pos[ii].wasapi_pos))
705 /* Non-positional or unknown position, warn? */
707 mask |= G_GUINT64_CONSTANT (1) << wasapi_to_gst_pos[ii].gst_pos;
708 pos[ii] = wasapi_to_gst_pos[ii].gst_pos;
718 gst_wasapi_util_parse_waveformatex (WAVEFORMATEXTENSIBLE * format,
719 GstCaps * template_caps, GstCaps ** out_caps,
720 GstAudioChannelPosition ** out_positions)
724 guint64 channel_mask;
728 /* TODO: handle SPDIF and other encoded formats */
730 /* 1 or 2 channels <= 16 bits sample size OR
731 * 1 or 2 channels > 16 bits sample size or >2 channels */
732 if (format->Format.wFormatTag != WAVE_FORMAT_PCM &&
733 format->Format.wFormatTag != WAVE_FORMAT_IEEE_FLOAT &&
734 format->Format.wFormatTag != WAVE_FORMAT_EXTENSIBLE)
735 /* Unhandled format tag */
738 /* WASAPI can only tell us one canonical mix format that it will accept. The
739 * alternative is calling IsFormatSupported on all combinations of formats.
740 * Instead, it's simpler and faster to require conversion inside gstreamer */
741 afmt = gst_waveformatex_to_audio_format (format);
745 *out_caps = gst_caps_copy (template_caps);
747 /* This will always return something that might be usable */
749 gst_wasapi_util_waveformatex_to_channel_mask (format, out_positions);
751 for (ii = 0; ii < gst_caps_get_size (*out_caps); ii++) {
752 GstStructure *s = gst_caps_get_structure (*out_caps, ii);
754 gst_structure_set (s,
755 "format", G_TYPE_STRING, afmt,
756 "channels", G_TYPE_INT, format->Format.nChannels,
757 "rate", G_TYPE_INT, format->Format.nSamplesPerSec,
758 "channel-mask", GST_TYPE_BITMASK, channel_mask, NULL);
765 gst_wasapi_util_get_best_buffer_sizes (GstAudioRingBufferSpec * spec,
766 gboolean exclusive, REFERENCE_TIME default_period,
767 REFERENCE_TIME min_period, REFERENCE_TIME * ret_period,
768 REFERENCE_TIME * ret_buffer_duration)
770 REFERENCE_TIME use_period, use_buffer;
772 /* Figure out what integral device period to use as the base */
774 /* Exclusive mode can run at multiples of either the minimum period or the
775 * default period; these are on the hardware ringbuffer */
776 if (spec->latency_time * 10 > default_period)
777 use_period = default_period;
779 use_period = min_period;
781 /* Shared mode always runs at the default period, so if we want a larger
782 * period (for lower CPU usage), we do it as a multiple of that */
783 use_period = default_period;
786 /* Ensure that the period (latency_time) used is an integral multiple of
787 * either the default period or the minimum period */
788 use_period = use_period * MAX ((spec->latency_time * 10) / use_period, 1);
791 /* Buffer duration is the same as the period in exclusive mode. The
792 * hardware is always writing out one buffer (of size *ret_period), and
793 * we're writing to the other one. */
794 use_buffer = use_period;
796 /* Ask WASAPI to create a software ringbuffer of at least this size; it may
797 * be larger so the actual buffer time may be different, which is why after
798 * initialization we read the buffer duration actually in-use and set
799 * segsize/segtotal from that. */
800 use_buffer = spec->buffer_time * 10;
801 /* Has to be at least twice the period */
802 if (use_buffer < 2 * use_period)
803 use_buffer = 2 * use_period;
806 *ret_period = use_period;
807 *ret_buffer_duration = use_buffer;