wasapisrc: Implement loopback recording
authorNirbheek Chauhan <nirbheek@centricular.com>
Tue, 3 Apr 2018 19:37:14 +0000 (01:07 +0530)
committerNirbheek Chauhan <nirbheek@centricular.com>
Tue, 3 Apr 2018 19:42:23 +0000 (01:12 +0530)
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
sys/wasapi/gstwasapisrc.c
sys/wasapi/gstwasapisrc.h
sys/wasapi/gstwasapiutil.c
sys/wasapi/gstwasapiutil.h

index c0f775a..1b86f23 100644 (file)
@@ -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;
   }
 
index 7060e78..b17485b 100644 (file)
@@ -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;
   }
 
index 21e4647..24ed655 100644 (file)
@@ -62,6 +62,7 @@ struct _GstWasapiSrc
   /* properties */
   gint role;
   gint sharemode;
+  gboolean loopback;
   gboolean low_latency;
   gboolean try_audioclient3;
   wchar_t *device_strid;
index 5563ee7..af3a864 100644 (file)
@@ -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,
index 784ef58..a0e329d 100644 (file)
@@ -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);