From affb0182c636948a69a309f07ea0c238b5c1bfa4 Mon Sep 17 00:00:00 2001 From: Nirbheek Chauhan Date: Wed, 4 Apr 2018 01:07:14 +0530 Subject: [PATCH] wasapisrc: Implement loopback recording Now, when you set loopback=true on wasapisrc, the `device` property should refer to a sink (render) device for loopback recording. If the `device` property is not set, the default sink device is used. --- sys/wasapi/gstwasapisink.c | 6 +++--- sys/wasapi/gstwasapisrc.c | 24 ++++++++++++++++++++---- sys/wasapi/gstwasapisrc.h | 1 + sys/wasapi/gstwasapiutil.c | 35 ++++++++++++++++++++++------------- sys/wasapi/gstwasapiutil.h | 7 ++++--- 5 files changed, 50 insertions(+), 23 deletions(-) diff --git a/sys/wasapi/gstwasapisink.c b/sys/wasapi/gstwasapisink.c index c0f775a..1b86f23 100644 --- a/sys/wasapi/gstwasapisink.c +++ b/sys/wasapi/gstwasapisink.c @@ -378,7 +378,7 @@ gst_wasapi_sink_open (GstAudioSink * asink) * even if the old device was unplugged. We need to handle this somehow. * 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, + if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), eRender, self->role, self->device_strid, &device, &client)) { if (!self->device_strid) GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, (NULL), @@ -452,12 +452,12 @@ gst_wasapi_sink_prepare (GstAudioSink * asink, GstAudioRingBufferSpec * spec) if (gst_wasapi_sink_can_audioclient3 (self)) { if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec, (IAudioClient3 *) self->client, self->mix_format, self->low_latency, - &devicep_frames)) + FALSE, &devicep_frames)) goto beach; } else { if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec, self->client, self->mix_format, self->sharemode, self->low_latency, - &devicep_frames)) + FALSE, &devicep_frames)) goto beach; } diff --git a/sys/wasapi/gstwasapisrc.c b/sys/wasapi/gstwasapisrc.c index 7060e78..b17485b 100644 --- a/sys/wasapi/gstwasapisrc.c +++ b/sys/wasapi/gstwasapisrc.c @@ -53,6 +53,7 @@ static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src", GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS)); #define DEFAULT_ROLE GST_WASAPI_DEVICE_ROLE_CONSOLE +#define DEFAULT_LOOPBACK FALSE #define DEFAULT_EXCLUSIVE FALSE #define DEFAULT_LOW_LATENCY FALSE #define DEFAULT_AUDIOCLIENT3 FALSE @@ -62,6 +63,7 @@ enum PROP_0, PROP_ROLE, PROP_DEVICE, + PROP_LOOPBACK, PROP_EXCLUSIVE, PROP_LOW_LATENCY, PROP_AUDIOCLIENT3 @@ -119,6 +121,12 @@ gst_wasapi_src_class_init (GstWasapiSrcClass * klass) NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (gobject_class, + PROP_LOOPBACK, + g_param_spec_boolean ("loopback", "Loopback recording", + "Open the sink device for loopback recording", + DEFAULT_LOOPBACK, 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", @@ -170,6 +178,7 @@ gst_wasapi_src_init (GstWasapiSrc * self) self->role = DEFAULT_ROLE; self->sharemode = AUDCLNT_SHAREMODE_SHARED; + self->loopback = DEFAULT_LOOPBACK; self->low_latency = DEFAULT_LOW_LATENCY; self->try_audioclient3 = DEFAULT_AUDIOCLIENT3; self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL); @@ -239,6 +248,9 @@ gst_wasapi_src_set_property (GObject * object, guint prop_id, device ? g_utf8_to_utf16 (device, -1, NULL, NULL, NULL) : NULL; break; } + case PROP_LOOPBACK: + self->loopback = g_value_get_boolean (value); + break; case PROP_EXCLUSIVE: self->sharemode = g_value_get_boolean (value) ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED; @@ -269,6 +281,9 @@ 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_LOOPBACK: + g_value_set_boolean (value, self->loopback); + break; case PROP_EXCLUSIVE: g_value_set_boolean (value, self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE); @@ -369,8 +384,9 @@ gst_wasapi_src_open (GstAudioSrc * asrc) * even if the old device was unplugged. We need to handle this somehow. * 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, &device, &client)) { + if (!gst_wasapi_util_get_device_client (GST_ELEMENT (self), + self->loopback ? eRender : eCapture, self->role, self->device_strid, + &device, &client)) { if (!self->device_strid) GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL), ("Failed to get default device")); @@ -419,12 +435,12 @@ gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec) if (gst_wasapi_src_can_audioclient3 (self)) { if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec, (IAudioClient3 *) self->client, self->mix_format, self->low_latency, - &devicep_frames)) + self->loopback, &devicep_frames)) goto beach; } else { if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec, self->client, self->mix_format, self->sharemode, self->low_latency, - &devicep_frames)) + self->loopback, &devicep_frames)) goto beach; } diff --git a/sys/wasapi/gstwasapisrc.h b/sys/wasapi/gstwasapisrc.h index 21e4647..24ed655 100644 --- a/sys/wasapi/gstwasapisrc.h +++ b/sys/wasapi/gstwasapisrc.h @@ -62,6 +62,7 @@ struct _GstWasapiSrc /* properties */ gint role; gint sharemode; + gboolean loopback; gboolean low_latency; gboolean try_audioclient3; wchar_t *device_strid; diff --git a/sys/wasapi/gstwasapiutil.c b/sys/wasapi/gstwasapiutil.c index 5563ee7..af3a864 100644 --- a/sys/wasapi/gstwasapiutil.c +++ b/sys/wasapi/gstwasapiutil.c @@ -543,7 +543,7 @@ out: gboolean gst_wasapi_util_get_device_client (GstElement * self, - gboolean capture, gint role, const wchar_t * device_strid, + gint data_flow, gint role, const wchar_t * device_strid, IMMDevice ** ret_device, IAudioClient ** ret_client) { gboolean res = FALSE; @@ -556,8 +556,8 @@ gst_wasapi_util_get_device_client (GstElement * self, goto beach; if (!device_strid) { - hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator, - capture ? eCapture : eRender, role, &device); + hr = IMMDeviceEnumerator_GetDefaultAudioEndpoint (enumerator, data_flow, + role, &device); HR_FAILED_GOTO (hr, IMMDeviceEnumerator::GetDefaultAudioEndpoint, beach); } else { hr = IMMDeviceEnumerator_GetDevice (enumerator, device_strid, &device); @@ -840,11 +840,11 @@ gboolean gst_wasapi_util_initialize_audioclient (GstElement * self, GstAudioRingBufferSpec * spec, IAudioClient * client, WAVEFORMATEX * format, guint sharemode, gboolean low_latency, - guint * ret_devicep_frames) + gboolean loopback, guint * ret_devicep_frames) { REFERENCE_TIME default_period, min_period; REFERENCE_TIME device_period, device_buffer_duration; - guint rate; + guint rate, stream_flags; HRESULT hr; hr = IAudioClient_GetDevicePeriod (client, &default_period, &min_period); @@ -874,8 +874,12 @@ gst_wasapi_util_initialize_audioclient (GstElement * self, if (sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE) CoInitialize (NULL); - hr = IAudioClient_Initialize (client, sharemode, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_buffer_duration, + stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (loopback) + stream_flags |= AUDCLNT_STREAMFLAGS_LOOPBACK; + + hr = IAudioClient_Initialize (client, sharemode, stream_flags, + device_buffer_duration, /* This must always be 0 in shared mode */ sharemode == AUDCLNT_SHAREMODE_SHARED ? 0 : device_period, format, NULL); @@ -895,9 +899,8 @@ gst_wasapi_util_initialize_audioclient (GstElement * self, GST_WARNING_OBJECT (self, "trying to re-initialize with period %i " "(%i frames, %i rate)", (int) device_period, n_frames, rate); - hr = IAudioClient_Initialize (client, sharemode, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, device_period, - device_period, format, NULL); + hr = IAudioClient_Initialize (client, sharemode, stream_flags, + device_period, device_period, format, NULL); } HR_FAILED_RET (hr, IAudioClient::Initialize, FALSE); @@ -909,9 +912,11 @@ gst_wasapi_util_initialize_audioclient (GstElement * self, gboolean gst_wasapi_util_initialize_audioclient3 (GstElement * self, GstAudioRingBufferSpec * spec, IAudioClient3 * client, - WAVEFORMATEX * format, gboolean low_latency, guint * ret_devicep_frames) + WAVEFORMATEX * format, gboolean low_latency, gboolean loopback, + guint * ret_devicep_frames) { HRESULT hr; + gint stream_flags; guint rate, devicep_frames; guint defaultp_frames, fundp_frames, minp_frames, maxp_frames; WAVEFORMATEX *tmpf; @@ -937,8 +942,12 @@ gst_wasapi_util_initialize_audioclient3 (GstElement * self, devicep_frames = tmp * fundp_frames; } - hr = IAudioClient3_InitializeSharedAudioStream (client, - AUDCLNT_STREAMFLAGS_EVENTCALLBACK, devicep_frames, format, NULL); + stream_flags = AUDCLNT_STREAMFLAGS_EVENTCALLBACK; + if (loopback) + stream_flags |= AUDCLNT_STREAMFLAGS_LOOPBACK; + + hr = IAudioClient3_InitializeSharedAudioStream (client, stream_flags, + devicep_frames, format, NULL); HR_FAILED_RET (hr, IAudioClient3::InitializeSharedAudioStream, FALSE); hr = IAudioClient3_GetCurrentSharedModeEnginePeriod (client, &tmpf, diff --git a/sys/wasapi/gstwasapiutil.h b/sys/wasapi/gstwasapiutil.h index 784ef58..a0e329d 100644 --- a/sys/wasapi/gstwasapiutil.h +++ b/sys/wasapi/gstwasapiutil.h @@ -79,7 +79,7 @@ gboolean gst_wasapi_util_get_devices (GstElement * element, gboolean active, GList ** devices); gboolean gst_wasapi_util_get_device_client (GstElement * element, - gboolean capture, gint role, const wchar_t * device_strid, + gint data_flow, gint role, const wchar_t * device_strid, IMMDevice ** ret_device, IAudioClient ** ret_client); gboolean gst_wasapi_util_get_device_format (GstElement * element, @@ -107,11 +107,12 @@ void gst_wasapi_util_get_best_buffer_sizes (GstAudioRingBufferSpec * spec, gboolean gst_wasapi_util_initialize_audioclient (GstElement * element, GstAudioRingBufferSpec * spec, IAudioClient * client, WAVEFORMATEX * format, guint sharemode, gboolean low_latency, - guint * ret_devicep_frames); + gboolean loopback, guint * ret_devicep_frames); gboolean gst_wasapi_util_initialize_audioclient3 (GstElement * element, GstAudioRingBufferSpec * spec, IAudioClient3 * client, - WAVEFORMATEX * format, gboolean low_latency, guint * ret_devicep_frames); + WAVEFORMATEX * format, gboolean low_latency, gboolean loopback, + guint * ret_devicep_frames); HANDLE gst_wasapi_util_set_thread_characteristics (void); -- 2.7.4