diff --git a/sys/wasapi/gstwasapisink.c b/sys/wasapi/gstwasapisink.c index a124ba30a5..0f0d299d48 100644 --- a/sys/wasapi/gstwasapisink.c +++ b/sys/wasapi/gstwasapisink.c @@ -40,8 +40,6 @@ #include "gstwasapisink.h" -#include - GST_DEBUG_CATEGORY_STATIC (gst_wasapi_sink_debug); #define GST_CAT_DEFAULT gst_wasapi_sink_debug @@ -50,15 +48,17 @@ static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink", GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS)); -#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE -#define DEFAULT_MUTE FALSE +#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE +#define DEFAULT_MUTE FALSE +#define DEFAULT_EXCLUSIVE FALSE enum { PROP_0, PROP_ROLE, PROP_MUTE, - PROP_DEVICE + PROP_DEVICE, + PROP_EXCLUSIVE }; static void gst_wasapi_sink_dispose (GObject * object); @@ -116,6 +116,12 @@ gst_wasapi_sink_class_init (GstWasapiSinkClass * klass) "WASAPI playback device as a GUID string", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_EXCLUSIVE, + g_param_spec_boolean ("exclusive", "Exclusive mode", + "Open the device in exclusive mode", + DEFAULT_EXCLUSIVE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_add_static_pad_template (gstelement_class, &sink_template); gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc", "Sink/Audio", @@ -209,6 +215,10 @@ gst_wasapi_sink_set_property (GObject * object, guint prop_id, device ? g_utf8_to_utf16 (device, -1, NULL, NULL, NULL) : NULL; break; } + case PROP_EXCLUSIVE: + self->sharemode = g_value_get_boolean (value) + ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED; + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -232,6 +242,10 @@ gst_wasapi_sink_get_property (GObject * object, guint prop_id, g_value_take_string (value, self->device_strid ? g_utf16_to_utf8 (self->device_strid, -1, NULL, NULL, NULL) : NULL); break; + case PROP_EXCLUSIVE: + g_value_set_boolean (value, + self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -244,7 +258,6 @@ gst_wasapi_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) GstWasapiSink *self = GST_WASAPI_SINK (bsink); WAVEFORMATEX *format = NULL; GstCaps *caps = NULL; - HRESULT hr; GST_DEBUG_OBJECT (self, "entering get caps"); @@ -252,16 +265,18 @@ gst_wasapi_sink_get_caps (GstBaseSink * bsink, GstCaps * filter) caps = gst_caps_ref (self->cached_caps); } else { GstCaps *template_caps; + gboolean ret; template_caps = gst_pad_get_pad_template_caps (bsink->sinkpad); if (!self->client) gst_wasapi_sink_open (GST_AUDIO_SINK (bsink)); - hr = IAudioClient_GetMixFormat (self->client, &format); - if (hr != S_OK || format == NULL) { + ret = gst_wasapi_util_get_device_format (GST_ELEMENT (self), + self->sharemode, self->device, self->client, &format); + if (!ret) { GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), - ("GetMixFormat failed: %s", gst_wasapi_util_hresult_to_string (hr))); + ("failed to detect format")); goto out; } @@ -302,6 +317,7 @@ gst_wasapi_sink_open (GstAudioSink * asink) { GstWasapiSink *self = GST_WASAPI_SINK (asink); gboolean res = FALSE; + IMMDevice *device = NULL; IAudioClient *client = NULL; GST_DEBUG_OBJECT (self, "opening device"); @@ -314,7 +330,7 @@ gst_wasapi_sink_open (GstAudioSink * asink) * For example, perhaps we should automatically switch to the new device if * the default device is changed and a device isn't explicitly selected. */ if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), FALSE, - self->role, self->device_strid, &client)) { + self->role, self->device_strid, &device, &client)) { if (!self->device_strid) GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, (NULL), ("Failed to get default device")); @@ -325,6 +341,7 @@ gst_wasapi_sink_open (GstAudioSink * asink) } self->client = client; + self->device = device; res = TRUE; beach: @@ -337,6 +354,11 @@ gst_wasapi_sink_close (GstAudioSink * asink) { GstWasapiSink *self = GST_WASAPI_SINK (asink); + if (self->device != NULL) { + IUnknown_Release (self->device); + self->device = NULL; + } + if (self->client != NULL) { IUnknown_Release (self->client); self->client = NULL; @@ -352,7 +374,7 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec) gboolean res = FALSE; REFERENCE_TIME latency_rt; IAudioRenderClient *render_client = NULL; - gint64 default_period, min_period; + gint64 default_period, min_period, use_period; guint bpf, rate; HRESULT hr; @@ -364,15 +386,27 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec) GST_INFO_OBJECT (self, "wasapi default period: %" G_GINT64_FORMAT ", min period: %" G_GINT64_FORMAT, default_period, min_period); - /* Set hnsBufferDuration to 0, which should, in theory, tell the device to - * create a buffer with the smallest latency possible. In practice, this is - * usually 2 * default_period. See: - * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370871(v=vs.85).aspx - * - * NOTE: min_period is a lie, and I have never seen WASAPI use it as the - * current period */ - hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL); + if (self->sharemode == AUDCLNT_SHAREMODE_SHARED) { + use_period = default_period; + /* Set hnsBufferDuration to 0, which should, in theory, tell the device to + * create a buffer with the smallest latency possible. In practice, this is + * usually 2 * default_period. See: + * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370871(v=vs.85).aspx + * + * NOTE: min_period is a lie, and I have never seen WASAPI use it as the + * current period */ + hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL); + } else { + use_period = min_period; + /* For some reason, we need to call this another time for exclusive mode */ + CoInitialize (NULL); + /* FIXME: We should be able to use min_period as the device buffer size, + * but I'm hitting a problem in GStreamer. */ + hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, use_period, use_period, + self->mix_format, NULL); + } if (hr != S_OK) { GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL), ("IAudioClient::Initialize () failed: %s", @@ -394,8 +428,10 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec) /* Actual latency-time/buffer-time are different now */ spec->segsize = gst_util_uint64_scale_int_round (rate * bpf, - default_period * 100, GST_SECOND); - spec->segtotal = (self->buffer_frame_count * bpf) / spec->segsize; + use_period * 100, GST_SECOND); + + /* We need a minimum of 2 segments to ensure glitch-free playback */ + spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2); GST_INFO_OBJECT (self, "segsize is %i, segtotal is %i", spec->segsize, spec->segtotal); @@ -450,6 +486,9 @@ gst_wasapi_sink_unprepare (GstAudioSink * asink) { GstWasapiSink *self = GST_WASAPI_SINK (asink); + if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) + CoUninitialize (); + if (self->client != NULL) { IAudioClient_Stop (self->client); } @@ -471,33 +510,37 @@ gst_wasapi_sink_write (GstAudioSink * asink, gpointer data, guint length) guint pending = length; while (pending > 0) { - guint have_frames, can_frames, n_frames, n_frames_padding, write_len; - - /* We have N frames to be written out */ - have_frames = pending / (self->mix_format->nBlockAlign); + guint n_frames, write_len; WaitForSingleObject (self->event_handle, INFINITE); - /* Frames the card hasn't rendered yet */ - hr = IAudioClient_GetCurrentPadding (self->client, &n_frames_padding); - if (hr != S_OK) { - GST_ERROR_OBJECT (self, "IAudioClient::GetCurrentPadding failed: %s", - gst_wasapi_util_hresult_to_string (hr)); - length = 0; - goto beach; + if (self->sharemode == AUDCLNT_SHAREMODE_SHARED) { + guint have_frames, can_frames, n_frames_padding; + + /* Frames the card hasn't rendered yet */ + hr = IAudioClient_GetCurrentPadding (self->client, &n_frames_padding); + if (hr != S_OK) { + GST_ERROR_OBJECT (self, "IAudioClient::GetCurrentPadding failed: %s", + gst_wasapi_util_hresult_to_string (hr)); + length = 0; + goto beach; + } + /* We have N frames to be written out */ + have_frames = pending / (self->mix_format->nBlockAlign); + /* We can write out these many frames */ + can_frames = self->buffer_frame_count - n_frames_padding; + /* We will write out these many frames, and this much length */ + n_frames = MIN (can_frames, have_frames); + + GST_TRACE_OBJECT (self, "total: %i, unread: %i, have: %i (%i bytes), " + "will write: %i", self->buffer_frame_count, n_frames_padding, + have_frames, pending, n_frames); + } else { + n_frames = self->buffer_frame_count; } - /* We can write out these many frames */ - can_frames = self->buffer_frame_count - n_frames_padding; - - /* We will write out these many frames, and this much length */ - n_frames = MIN (can_frames, have_frames); write_len = n_frames * self->mix_format->nBlockAlign; - GST_TRACE_OBJECT (self, "total: %i, unread: %i, have: %i (%i bytes), " - "will write: %i (%i bytes)", self->buffer_frame_count, n_frames_padding, - have_frames, pending, n_frames, write_len); - hr = IAudioRenderClient_GetBuffer (self->render_client, n_frames, (BYTE **) & dst); if (hr != S_OK) { diff --git a/sys/wasapi/gstwasapisink.h b/sys/wasapi/gstwasapisink.h index 37ba888d59..882f46e2c6 100644 --- a/sys/wasapi/gstwasapisink.h +++ b/sys/wasapi/gstwasapisink.h @@ -40,6 +40,7 @@ struct _GstWasapiSink { GstAudioSink parent; + IMMDevice *device; IAudioClient *client; IAudioRenderClient *render_client; HANDLE event_handle; @@ -57,6 +58,7 @@ struct _GstWasapiSink /* properties */ gint role; + gint sharemode; gboolean mute; wchar_t *device_strid; }; diff --git a/sys/wasapi/gstwasapisrc.c b/sys/wasapi/gstwasapisrc.c index 3eb19cc6c5..dec93f8293 100644 --- a/sys/wasapi/gstwasapisrc.c +++ b/sys/wasapi/gstwasapisrc.c @@ -38,8 +38,6 @@ #include "gstwasapisrc.h" -#include - GST_DEBUG_CATEGORY_STATIC (gst_wasapi_src_debug); #define GST_CAT_DEFAULT gst_wasapi_src_debug @@ -48,13 +46,15 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_PAD_ALWAYS, GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS)); -#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE +#define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE +#define DEFAULT_EXCLUSIVE FALSE enum { PROP_0, PROP_ROLE, - PROP_DEVICE + PROP_DEVICE, + PROP_EXCLUSIVE }; static void gst_wasapi_src_dispose (GObject * object); @@ -108,6 +108,12 @@ gst_wasapi_src_class_init (GstWasapiSrcClass * klass) "WASAPI playback device as a GUID string", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + g_object_class_install_property (gobject_class, + PROP_EXCLUSIVE, + g_param_spec_boolean ("exclusive", "Exclusive mode", + "Open the device in exclusive mode", + DEFAULT_EXCLUSIVE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); + gst_element_class_add_static_pad_template (gstelement_class, &src_template); gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc", "Source/Audio", @@ -206,6 +212,10 @@ gst_wasapi_src_set_property (GObject * object, guint prop_id, device ? g_utf8_to_utf16 (device, -1, NULL, NULL, NULL) : NULL; break; } + case PROP_EXCLUSIVE: + self->sharemode = g_value_get_boolean (value) + ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED; + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -226,6 +236,10 @@ gst_wasapi_src_get_property (GObject * object, guint prop_id, g_value_take_string (value, self->device_strid ? g_utf16_to_utf8 (self->device_strid, -1, NULL, NULL, NULL) : NULL); break; + case PROP_EXCLUSIVE: + g_value_set_boolean (value, + self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -238,7 +252,6 @@ gst_wasapi_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) GstWasapiSrc *self = GST_WASAPI_SRC (bsrc); WAVEFORMATEX *format = NULL; GstCaps *caps = NULL; - HRESULT hr; GST_DEBUG_OBJECT (self, "entering get caps"); @@ -246,16 +259,18 @@ gst_wasapi_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter) caps = gst_caps_ref (self->cached_caps); } else { GstCaps *template_caps; + gboolean ret; template_caps = gst_pad_get_pad_template_caps (bsrc->srcpad); if (!self->client) gst_wasapi_src_open (GST_AUDIO_SRC (bsrc)); - hr = IAudioClient_GetMixFormat (self->client, &format); - if (hr != S_OK || format == NULL) { + ret = gst_wasapi_util_get_device_format (GST_ELEMENT (self), + self->sharemode, self->device, self->client, &format); + if (!ret) { GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), - ("GetMixFormat failed: %s", gst_wasapi_util_hresult_to_string (hr))); + ("failed to detect format")); goto out; } @@ -297,6 +312,7 @@ gst_wasapi_src_open (GstAudioSrc * asrc) GstWasapiSrc *self = GST_WASAPI_SRC (asrc); gboolean res = FALSE; IAudioClient *client = NULL; + IMMDevice *device = NULL; if (self->client) return TRUE; @@ -306,7 +322,7 @@ gst_wasapi_src_open (GstAudioSrc * asrc) * For example, perhaps we should automatically switch to the new device if * the default device is changed and a device isn't explicitly selected. */ if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), TRUE, - self->role, self->device_strid, &client)) { + self->role, self->device_strid, &device, &client)) { if (!self->device_strid) GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL), ("Failed to get default device")); @@ -317,6 +333,7 @@ gst_wasapi_src_open (GstAudioSrc * asrc) } self->client = client; + self->device = device; res = TRUE; beach: @@ -329,6 +346,11 @@ gst_wasapi_src_close (GstAudioSrc * asrc) { GstWasapiSrc *self = GST_WASAPI_SRC (asrc); + if (self->device != NULL) { + IUnknown_Release (self->device); + self->device = NULL; + } + if (self->client != NULL) { IUnknown_Release (self->client); self->client = NULL; @@ -346,7 +368,7 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec) guint64 client_clock_freq = 0; IAudioCaptureClient *capture_client = NULL; REFERENCE_TIME latency_rt; - gint64 default_period, min_period; + gint64 default_period, min_period, use_period; guint bpf, rate, buffer_frames; HRESULT hr; @@ -359,18 +381,30 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec) GST_INFO_OBJECT (self, "wasapi default period: %" G_GINT64_FORMAT ", min period: %" G_GINT64_FORMAT, default_period, min_period); - /* Set hnsBufferDuration to 0, which should, in theory, tell the device to - * create a buffer with the smallest latency possible. In practice, this is - * usually 2 * default_period. See: - * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370871(v=vs.85).aspx - * - * NOTE: min_period is a lie, and I have never seen WASAPI use it as the - * current period */ - hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL); + if (self->sharemode == AUDCLNT_SHAREMODE_SHARED) { + use_period = default_period; + /* Set hnsBufferDuration to 0, which should, in theory, tell the device to + * create a buffer with the smallest latency possible. In practice, this is + * usually 2 * default_period. See: + * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370871(v=vs.85).aspx + * + * NOTE: min_period is a lie, and I have never seen WASAPI use it as the + * current period */ + hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_SHARED, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, self->mix_format, NULL); + } else { + use_period = default_period; + /* For some reason, we need to call this another time for exclusive mode */ + CoInitialize (NULL); + /* FIXME: We should be able to use min_period as the device buffer size, + * but I'm hitting a problem in GStreamer. */ + hr = IAudioClient_Initialize (self->client, AUDCLNT_SHAREMODE_EXCLUSIVE, + AUDCLNT_STREAMFLAGS_EVENTCALLBACK, use_period, use_period, + self->mix_format, NULL); + } if (hr != S_OK) { GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL), - ("IAudioClient::Initialize failed: %s", + ("IAudioClient::Initialize () failed: %s", gst_wasapi_util_hresult_to_string (hr))); goto beach; } @@ -388,8 +422,10 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec) "rate is %i Hz", buffer_frames, bpf, rate); spec->segsize = gst_util_uint64_scale_int_round (rate * bpf, - default_period * 100, GST_SECOND); - spec->segtotal = (buffer_frames * bpf) / spec->segsize; + use_period * 100, GST_SECOND); + + /* We need a minimum of 2 segments to ensure glitch-free playback */ + spec->segtotal = MAX (self->buffer_frame_count * bpf / spec->segsize, 2); GST_INFO_OBJECT (self, "segsize is %i, segtotal is %i", spec->segsize, spec->segtotal); @@ -460,6 +496,9 @@ gst_wasapi_src_unprepare (GstAudioSrc * asrc) { GstWasapiSrc *self = GST_WASAPI_SRC (asrc); + if (self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) + CoUninitialize (); + if (self->client != NULL) { IAudioClient_Stop (self->client); } diff --git a/sys/wasapi/gstwasapisrc.h b/sys/wasapi/gstwasapisrc.h index 95087bd262..fc89391a9b 100644 --- a/sys/wasapi/gstwasapisrc.h +++ b/sys/wasapi/gstwasapisrc.h @@ -40,6 +40,7 @@ struct _GstWasapiSrc { GstAudioSrc parent; + IMMDevice *device; IAudioClient *client; IAudioClock *client_clock; guint64 client_clock_freq; @@ -59,6 +60,7 @@ struct _GstWasapiSrc /* properties */ gint role; + gint sharemode; wchar_t *device_strid; }; diff --git a/sys/wasapi/gstwasapiutil.c b/sys/wasapi/gstwasapiutil.c index 1ad9ff6a8e..9fcbd33d16 100644 --- a/sys/wasapi/gstwasapiutil.c +++ b/sys/wasapi/gstwasapiutil.c @@ -25,8 +25,6 @@ #include "gstwasapiutil.h" #include "gstwasapidevice.h" -#include - /* This was only added to MinGW in ~2015 and our Cerbero toolchain is too old */ #if defined(_MSC_VER) #include @@ -427,10 +425,83 @@ err: return ret; } +gboolean +gst_wasapi_util_get_device_format (GstElement * element, + gint device_mode, IMMDevice * device, IAudioClient * client, + WAVEFORMATEX ** ret_format) +{ + WAVEFORMATEX *format; + HRESULT hr; + + *ret_format = NULL; + + hr = IAudioClient_GetMixFormat (client, &format); + if (hr != S_OK || format == NULL) { + GST_ERROR_OBJECT (element, "GetMixFormat failed: %s", + gst_wasapi_util_hresult_to_string (hr)); + return FALSE; + } + + /* WASAPI always accepts the format returned by GetMixFormat in shared mode */ + if (device_mode == AUDCLNT_SHAREMODE_SHARED) + goto out; + + /* WASAPI may or may not support this format in exclusive mode */ + hr = IAudioClient_IsFormatSupported (client, AUDCLNT_SHAREMODE_EXCLUSIVE, + format, NULL); + if (hr == S_OK) + goto out; + + CoTaskMemFree (format); + + /* Open the device property store, and get the format that WASAPI has been + * using for sending data to the device */ + { + PROPVARIANT var; + IPropertyStore *prop_store = NULL; + + hr = IMMDevice_OpenPropertyStore (device, STGM_READ, &prop_store); + if (hr != S_OK) { + GST_ERROR_OBJECT (element, "OpenPropertyStore failed: %s", + gst_wasapi_util_hresult_to_string (hr)); + return FALSE; + } + + hr = IPropertyStore_GetValue (prop_store, &PKEY_AudioEngine_DeviceFormat, + &var); + if (hr != S_OK) { + GST_ERROR_OBJECT (element, "GetValue failed: %s", + gst_wasapi_util_hresult_to_string (hr)); + IUnknown_Release (prop_store); + return FALSE; + } + + format = malloc (var.blob.cbSize); + memcpy (format, var.blob.pBlobData, var.blob.cbSize); + + PropVariantClear (&var); + IUnknown_Release (prop_store); + } + + /* WASAPI may or may not support this format in exclusive mode */ + hr = IAudioClient_IsFormatSupported (client, AUDCLNT_SHAREMODE_EXCLUSIVE, + format, NULL); + if (hr == S_OK) + goto out; + + GST_ERROR_OBJECT (element, "AudioEngine DeviceFormat not supported"); + free (format); + return FALSE; + +out: + *ret_format = format; + return TRUE; +} + gboolean gst_wasapi_util_get_device_client (GstElement * element, gboolean capture, gint role, const wchar_t * device_strid, - IAudioClient ** ret_client) + IMMDevice ** ret_device, IAudioClient ** ret_client) { gboolean res = FALSE; HRESULT hr; @@ -468,7 +539,9 @@ gst_wasapi_util_get_device_client (GstElement * element, } IUnknown_AddRef (client); + IUnknown_AddRef (device); *ret_client = client; + *ret_device = device; res = TRUE; diff --git a/sys/wasapi/gstwasapiutil.h b/sys/wasapi/gstwasapiutil.h index af44895a63..b8337de1ae 100644 --- a/sys/wasapi/gstwasapiutil.h +++ b/sys/wasapi/gstwasapiutil.h @@ -25,6 +25,7 @@ #include #include +#include #include /* Static Caps shared between source, sink, and device provider */ @@ -57,7 +58,11 @@ gboolean gst_wasapi_util_get_devices (GstElement * element, gboolean active, gboolean gst_wasapi_util_get_device_client (GstElement * element, gboolean capture, gint role, const wchar_t * device_strid, - IAudioClient ** ret_client); + IMMDevice ** ret_device, IAudioClient ** ret_client); + +gboolean gst_wasapi_util_get_device_format (GstElement * element, + gint device_mode, IMMDevice * device, IAudioClient * client, + WAVEFORMATEX ** ret_format); gboolean gst_wasapi_util_get_render_client (GstElement * element, IAudioClient * client, IAudioRenderClient ** ret_render_client);