• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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>
5  *
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.
10  *
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.
15  *
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.
20  */
21 
22 /**
23  * SECTION:element-wasapisrc
24  * @title: wasapisrc
25  *
26  * Provides audio capture from the Windows Audio Session API available with
27  * Vista and newer.
28  *
29  * ## Example pipelines
30  * |[
31  * gst-launch-1.0 -v wasapisrc ! fakesink
32  * ]| Capture from the default audio device and render to fakesink.
33  *
34  * |[
35  * gst-launch-1.0 -v wasapisrc low-latency=true ! fakesink
36  * ]| Capture from the default audio device with the minimum possible latency and render to fakesink.
37  *
38  */
39 #ifdef HAVE_CONFIG_H
40 #  include <config.h>
41 #endif
42 
43 #include "gstwasapisrc.h"
44 
45 #include <avrt.h>
46 
47 GST_DEBUG_CATEGORY_STATIC (gst_wasapi_src_debug);
48 #define GST_CAT_DEFAULT gst_wasapi_src_debug
49 
50 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
51     GST_PAD_SRC,
52     GST_PAD_ALWAYS,
53     GST_STATIC_CAPS (GST_WASAPI_STATIC_CAPS));
54 
55 #define DEFAULT_ROLE          GST_WASAPI_DEVICE_ROLE_CONSOLE
56 #define DEFAULT_LOOPBACK      FALSE
57 #define DEFAULT_EXCLUSIVE     FALSE
58 #define DEFAULT_LOW_LATENCY   FALSE
59 #define DEFAULT_AUDIOCLIENT3  FALSE
60 /* The clock provided by WASAPI is always off and causes buffers to be late
61  * very quickly on the sink. Disable pending further investigation. */
62 #define DEFAULT_PROVIDE_CLOCK FALSE
63 
64 enum
65 {
66   PROP_0,
67   PROP_ROLE,
68   PROP_DEVICE,
69   PROP_LOOPBACK,
70   PROP_EXCLUSIVE,
71   PROP_LOW_LATENCY,
72   PROP_AUDIOCLIENT3
73 };
74 
75 static void gst_wasapi_src_dispose (GObject * object);
76 static void gst_wasapi_src_finalize (GObject * object);
77 static void gst_wasapi_src_set_property (GObject * object, guint prop_id,
78     const GValue * value, GParamSpec * pspec);
79 static void gst_wasapi_src_get_property (GObject * object, guint prop_id,
80     GValue * value, GParamSpec * pspec);
81 
82 static GstCaps *gst_wasapi_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter);
83 
84 static gboolean gst_wasapi_src_open (GstAudioSrc * asrc);
85 static gboolean gst_wasapi_src_close (GstAudioSrc * asrc);
86 static gboolean gst_wasapi_src_prepare (GstAudioSrc * asrc,
87     GstAudioRingBufferSpec * spec);
88 static gboolean gst_wasapi_src_unprepare (GstAudioSrc * asrc);
89 static guint gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data,
90     guint length, GstClockTime * timestamp);
91 static guint gst_wasapi_src_delay (GstAudioSrc * asrc);
92 static void gst_wasapi_src_reset (GstAudioSrc * asrc);
93 
94 #if DEFAULT_PROVIDE_CLOCK
95 static GstClockTime gst_wasapi_src_get_time (GstClock * clock,
96     gpointer user_data);
97 #endif
98 
99 #define gst_wasapi_src_parent_class parent_class
100 G_DEFINE_TYPE (GstWasapiSrc, gst_wasapi_src, GST_TYPE_AUDIO_SRC);
101 
102 static void
gst_wasapi_src_class_init(GstWasapiSrcClass * klass)103 gst_wasapi_src_class_init (GstWasapiSrcClass * klass)
104 {
105   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
106   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
107   GstBaseSrcClass *gstbasesrc_class = GST_BASE_SRC_CLASS (klass);
108   GstAudioSrcClass *gstaudiosrc_class = GST_AUDIO_SRC_CLASS (klass);
109 
110   gobject_class->dispose = gst_wasapi_src_dispose;
111   gobject_class->finalize = gst_wasapi_src_finalize;
112   gobject_class->set_property = gst_wasapi_src_set_property;
113   gobject_class->get_property = gst_wasapi_src_get_property;
114 
115   g_object_class_install_property (gobject_class,
116       PROP_ROLE,
117       g_param_spec_enum ("role", "Role",
118           "Role of the device: communications, multimedia, etc",
119           GST_WASAPI_DEVICE_TYPE_ROLE, DEFAULT_ROLE, G_PARAM_READWRITE |
120           G_PARAM_STATIC_STRINGS | GST_PARAM_MUTABLE_READY));
121 
122   g_object_class_install_property (gobject_class,
123       PROP_DEVICE,
124       g_param_spec_string ("device", "Device",
125           "WASAPI playback device as a GUID string",
126           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
127 
128   g_object_class_install_property (gobject_class,
129       PROP_LOOPBACK,
130       g_param_spec_boolean ("loopback", "Loopback recording",
131           "Open the sink device for loopback recording",
132           DEFAULT_LOOPBACK, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
133 
134   g_object_class_install_property (gobject_class,
135       PROP_EXCLUSIVE,
136       g_param_spec_boolean ("exclusive", "Exclusive mode",
137           "Open the device in exclusive mode",
138           DEFAULT_EXCLUSIVE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
139 
140   g_object_class_install_property (gobject_class,
141       PROP_LOW_LATENCY,
142       g_param_spec_boolean ("low-latency", "Low latency",
143           "Optimize all settings for lowest latency. Always safe to enable.",
144           DEFAULT_LOW_LATENCY, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
145 
146   g_object_class_install_property (gobject_class,
147       PROP_AUDIOCLIENT3,
148       g_param_spec_boolean ("use-audioclient3", "Use the AudioClient3 API",
149           "Whether to use the Windows 10 AudioClient3 API when available",
150           DEFAULT_AUDIOCLIENT3, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
151 
152   gst_element_class_add_static_pad_template (gstelement_class, &src_template);
153   gst_element_class_set_static_metadata (gstelement_class, "WasapiSrc",
154       "Source/Audio/Hardware",
155       "Stream audio from an audio capture device through WASAPI",
156       "Nirbheek Chauhan <nirbheek@centricular.com>, "
157       "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>");
158 
159   gstbasesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_wasapi_src_get_caps);
160 
161   gstaudiosrc_class->open = GST_DEBUG_FUNCPTR (gst_wasapi_src_open);
162   gstaudiosrc_class->close = GST_DEBUG_FUNCPTR (gst_wasapi_src_close);
163   gstaudiosrc_class->read = GST_DEBUG_FUNCPTR (gst_wasapi_src_read);
164   gstaudiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_wasapi_src_prepare);
165   gstaudiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_wasapi_src_unprepare);
166   gstaudiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_wasapi_src_delay);
167   gstaudiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_wasapi_src_reset);
168 
169   GST_DEBUG_CATEGORY_INIT (gst_wasapi_src_debug, "wasapisrc",
170       0, "Windows audio session API source");
171 
172   gst_type_mark_as_plugin_api (GST_WASAPI_DEVICE_TYPE_ROLE, 0);
173 }
174 
175 static void
gst_wasapi_src_init(GstWasapiSrc * self)176 gst_wasapi_src_init (GstWasapiSrc * self)
177 {
178 #if DEFAULT_PROVIDE_CLOCK
179   /* override with a custom clock */
180   if (GST_AUDIO_BASE_SRC (self)->clock)
181     gst_object_unref (GST_AUDIO_BASE_SRC (self)->clock);
182 
183   GST_AUDIO_BASE_SRC (self)->clock = gst_audio_clock_new ("GstWasapiSrcClock",
184       gst_wasapi_src_get_time, gst_object_ref (self),
185       (GDestroyNotify) gst_object_unref);
186 #endif
187 
188   self->role = DEFAULT_ROLE;
189   self->sharemode = AUDCLNT_SHAREMODE_SHARED;
190   self->loopback = DEFAULT_LOOPBACK;
191   self->low_latency = DEFAULT_LOW_LATENCY;
192   self->try_audioclient3 = DEFAULT_AUDIOCLIENT3;
193   self->event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
194   self->cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
195   self->client_needs_restart = FALSE;
196   self->adapter = gst_adapter_new ();
197 
198   /* Extra event handles used for loopback */
199   self->loopback_event_handle = CreateEvent (NULL, FALSE, FALSE, NULL);
200   self->loopback_cancellable = CreateEvent (NULL, TRUE, FALSE, NULL);
201 
202   self->enumerator = gst_mm_device_enumerator_new ();
203 }
204 
205 static void
gst_wasapi_src_dispose(GObject * object)206 gst_wasapi_src_dispose (GObject * object)
207 {
208   GstWasapiSrc *self = GST_WASAPI_SRC (object);
209 
210   if (self->event_handle != NULL) {
211     CloseHandle (self->event_handle);
212     self->event_handle = NULL;
213   }
214 
215   if (self->cancellable != NULL) {
216     CloseHandle (self->cancellable);
217     self->cancellable = NULL;
218   }
219 
220   if (self->client_clock != NULL) {
221     IUnknown_Release (self->client_clock);
222     self->client_clock = NULL;
223   }
224 
225   if (self->client != NULL) {
226     IUnknown_Release (self->client);
227     self->client = NULL;
228   }
229 
230   if (self->capture_client != NULL) {
231     IUnknown_Release (self->capture_client);
232     self->capture_client = NULL;
233   }
234 
235   if (self->loopback_client != NULL) {
236     IUnknown_Release (self->loopback_client);
237     self->loopback_client = NULL;
238   }
239 
240   if (self->loopback_event_handle != NULL) {
241     CloseHandle (self->loopback_event_handle);
242     self->loopback_event_handle = NULL;
243   }
244 
245   if (self->loopback_cancellable != NULL) {
246     CloseHandle (self->loopback_cancellable);
247     self->loopback_cancellable = NULL;
248   }
249 
250   gst_clear_object (&self->enumerator);
251 
252   G_OBJECT_CLASS (parent_class)->dispose (object);
253 }
254 
255 static void
gst_wasapi_src_finalize(GObject * object)256 gst_wasapi_src_finalize (GObject * object)
257 {
258   GstWasapiSrc *self = GST_WASAPI_SRC (object);
259 
260   CoTaskMemFree (self->mix_format);
261   self->mix_format = NULL;
262 
263   g_clear_pointer (&self->cached_caps, gst_caps_unref);
264   g_clear_pointer (&self->positions, g_free);
265   g_clear_pointer (&self->device_strid, g_free);
266 
267   g_object_unref (self->adapter);
268   self->adapter = NULL;
269 
270   G_OBJECT_CLASS (parent_class)->finalize (object);
271 }
272 
273 static void
gst_wasapi_src_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)274 gst_wasapi_src_set_property (GObject * object, guint prop_id,
275     const GValue * value, GParamSpec * pspec)
276 {
277   GstWasapiSrc *self = GST_WASAPI_SRC (object);
278 
279   switch (prop_id) {
280     case PROP_ROLE:
281       self->role = gst_wasapi_device_role_to_erole (g_value_get_enum (value));
282       break;
283     case PROP_DEVICE:
284     {
285       const gchar *device = g_value_get_string (value);
286       g_free (self->device_strid);
287       self->device_strid =
288           device ? g_utf8_to_utf16 (device, -1, NULL, NULL, NULL) : NULL;
289       break;
290     }
291     case PROP_LOOPBACK:
292       self->loopback = g_value_get_boolean (value);
293       break;
294     case PROP_EXCLUSIVE:
295       self->sharemode = g_value_get_boolean (value)
296           ? AUDCLNT_SHAREMODE_EXCLUSIVE : AUDCLNT_SHAREMODE_SHARED;
297       break;
298     case PROP_LOW_LATENCY:
299       self->low_latency = g_value_get_boolean (value);
300       break;
301     case PROP_AUDIOCLIENT3:
302       self->try_audioclient3 = g_value_get_boolean (value);
303       break;
304     default:
305       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
306       break;
307   }
308 }
309 
310 static void
gst_wasapi_src_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)311 gst_wasapi_src_get_property (GObject * object, guint prop_id,
312     GValue * value, GParamSpec * pspec)
313 {
314   GstWasapiSrc *self = GST_WASAPI_SRC (object);
315 
316   switch (prop_id) {
317     case PROP_ROLE:
318       g_value_set_enum (value, gst_wasapi_erole_to_device_role (self->role));
319       break;
320     case PROP_DEVICE:
321       g_value_take_string (value, self->device_strid ?
322           g_utf16_to_utf8 (self->device_strid, -1, NULL, NULL, NULL) : NULL);
323       break;
324     case PROP_LOOPBACK:
325       g_value_set_boolean (value, self->loopback);
326       break;
327     case PROP_EXCLUSIVE:
328       g_value_set_boolean (value,
329           self->sharemode == AUDCLNT_SHAREMODE_EXCLUSIVE);
330       break;
331     case PROP_LOW_LATENCY:
332       g_value_set_boolean (value, self->low_latency);
333       break;
334     case PROP_AUDIOCLIENT3:
335       g_value_set_boolean (value, self->try_audioclient3);
336       break;
337     default:
338       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
339       break;
340   }
341 }
342 
343 static gboolean
gst_wasapi_src_can_audioclient3(GstWasapiSrc * self)344 gst_wasapi_src_can_audioclient3 (GstWasapiSrc * self)
345 {
346   return (self->sharemode == AUDCLNT_SHAREMODE_SHARED &&
347       self->try_audioclient3 && gst_wasapi_util_have_audioclient3 ());
348 }
349 
350 static GstCaps *
gst_wasapi_src_get_caps(GstBaseSrc * bsrc,GstCaps * filter)351 gst_wasapi_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
352 {
353   GstWasapiSrc *self = GST_WASAPI_SRC (bsrc);
354   WAVEFORMATEX *format = NULL;
355   GstCaps *caps = NULL;
356 
357   GST_DEBUG_OBJECT (self, "entering get caps");
358 
359   if (self->cached_caps) {
360     caps = gst_caps_ref (self->cached_caps);
361   } else {
362     GstCaps *template_caps;
363     gboolean ret;
364 
365     template_caps = gst_pad_get_pad_template_caps (bsrc->srcpad);
366 
367     if (!self->client) {
368       caps = template_caps;
369       goto out;
370     }
371 
372     ret = gst_wasapi_util_get_device_format (GST_ELEMENT (self),
373         self->sharemode, self->device, self->client, &format);
374     if (!ret) {
375       GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL),
376           ("failed to detect format"));
377       gst_caps_unref (template_caps);
378       return NULL;
379     }
380 
381     gst_wasapi_util_parse_waveformatex ((WAVEFORMATEXTENSIBLE *) format,
382         template_caps, &caps, &self->positions);
383     if (caps == NULL) {
384       GST_ELEMENT_ERROR (self, STREAM, FORMAT, (NULL), ("unknown format"));
385       gst_caps_unref (template_caps);
386       return NULL;
387     }
388 
389     {
390       gchar *pos_str = gst_audio_channel_positions_to_string (self->positions,
391           format->nChannels);
392       GST_INFO_OBJECT (self, "positions are: %s", pos_str);
393       g_free (pos_str);
394     }
395 
396     self->mix_format = format;
397     gst_caps_replace (&self->cached_caps, caps);
398     gst_caps_unref (template_caps);
399   }
400 
401   if (filter) {
402     GstCaps *filtered =
403         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
404     gst_caps_unref (caps);
405     caps = filtered;
406   }
407 
408 out:
409   GST_DEBUG_OBJECT (self, "returning caps %" GST_PTR_FORMAT, caps);
410   return caps;
411 }
412 
413 static gboolean
gst_wasapi_src_open(GstAudioSrc * asrc)414 gst_wasapi_src_open (GstAudioSrc * asrc)
415 {
416   GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
417   gboolean res = FALSE;
418   IAudioClient *client = NULL;
419   IMMDevice *device = NULL;
420   IMMDevice *loopback_device = NULL;
421 
422   if (self->client)
423     return TRUE;
424 
425   /* FIXME: Switching the default device does not switch the stream to it,
426    * even if the old device was unplugged. We need to handle this somehow.
427    * For example, perhaps we should automatically switch to the new device if
428    * the default device is changed and a device isn't explicitly selected. */
429   if (!gst_wasapi_util_get_device (self->enumerator,
430           self->loopback ? eRender : eCapture, self->role, self->device_strid,
431           &device)
432       || !gst_wasapi_util_get_audio_client (GST_ELEMENT (self),
433           device, &client)) {
434     if (!self->device_strid)
435       GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
436           ("Failed to get default device"));
437     else
438       GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
439           ("Failed to open device %S", self->device_strid));
440     goto beach;
441   }
442 
443   /* An oddness of wasapi loopback feature is that capture client will not
444    * provide any audio data if there is no outputting sound.
445    * To workaround this problem, probably we can add timeout around loop
446    * in this case but it's glitch prone. So, instead of timeout,
447    * we will keep pusing silence data to into wasapi client so that make audio
448    * client report audio data in any case
449    */
450   if (!gst_wasapi_util_get_device (self->enumerator,
451           eRender, self->role, self->device_strid, &loopback_device)
452       || !gst_wasapi_util_get_audio_client (GST_ELEMENT (self),
453           loopback_device, &self->loopback_client)) {
454     if (!self->device_strid)
455       GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
456           ("Failed to get default device for loopback"));
457     else
458       GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
459           ("Failed to open device %S", self->device_strid));
460     goto beach;
461 
462     /* no need to hold this object */
463     IUnknown_Release (loopback_device);
464   }
465 
466   self->client = client;
467   self->device = device;
468   res = TRUE;
469 
470 beach:
471 
472   return res;
473 }
474 
475 static gboolean
gst_wasapi_src_close(GstAudioSrc * asrc)476 gst_wasapi_src_close (GstAudioSrc * asrc)
477 {
478   GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
479 
480   if (self->device != NULL) {
481     IUnknown_Release (self->device);
482     self->device = NULL;
483   }
484 
485   if (self->client != NULL) {
486     IUnknown_Release (self->client);
487     self->client = NULL;
488   }
489 
490   if (self->loopback_client != NULL) {
491     IUnknown_Release (self->loopback_client);
492     self->loopback_client = NULL;
493   }
494 
495   return TRUE;
496 }
497 
498 static gpointer
gst_wasapi_src_loopback_silence_feeding_thread(GstWasapiSrc * self)499 gst_wasapi_src_loopback_silence_feeding_thread (GstWasapiSrc * self)
500 {
501   HRESULT hr;
502   UINT32 buffer_frames;
503   gboolean res G_GNUC_UNUSED = FALSE;
504   BYTE *data;
505   DWORD dwWaitResult;
506   HANDLE event_handle[2];
507   UINT32 padding;
508   UINT32 n_frames;
509 
510   /* NOTE: if this task cause glitch, we need to consider thread priority
511    * adjusing. See gstaudioutilsprivate.c (e.g., AvSetMmThreadCharacteristics)
512    * for this context */
513 
514   GST_INFO_OBJECT (self, "Run loopback silence feeding thread");
515 
516   event_handle[0] = self->loopback_event_handle;
517   event_handle[1] = self->loopback_cancellable;
518 
519   hr = IAudioClient_GetBufferSize (self->loopback_client, &buffer_frames);
520   HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
521 
522   hr = IAudioClient_SetEventHandle (self->loopback_client,
523       self->loopback_event_handle);
524   HR_FAILED_GOTO (hr, IAudioClient::SetEventHandle, beach);
525 
526   /* To avoid start-up glitches, before starting the streaming, we fill the
527    * buffer with silence as recommended by the documentation:
528    * https://msdn.microsoft.com/en-us/library/windows/desktop/dd370879%28v=vs.85%29.aspx */
529   hr = IAudioRenderClient_GetBuffer (self->loopback_render_client,
530       buffer_frames, &data);
531   HR_FAILED_GOTO (hr, IAudioRenderClient::GetBuffer, beach);
532 
533   hr = IAudioRenderClient_ReleaseBuffer (self->loopback_render_client,
534       buffer_frames, AUDCLNT_BUFFERFLAGS_SILENT);
535   HR_FAILED_GOTO (hr, IAudioRenderClient::ReleaseBuffer, beach);
536 
537   hr = IAudioClient_Start (self->loopback_client);
538   HR_FAILED_GOTO (hr, IAudioClock::Start, beach);
539 
540   /* There is an OS bug prior to Windows 10, that is loopback capture client
541    * will not receive event (in case of event-driven mode).
542    * A guide for workaround this case is that signal it whenever render client
543    * writes data.
544    * See https://docs.microsoft.com/en-us/windows/win32/api/audioclient/nf-audioclient-iaudioclient-initialize
545    */
546 
547   /* Signal for read thread to wakeup */
548   SetEvent (self->event_handle);
549 
550   /* Ok, now we are ready for running for feeding silence data */
551   while (1) {
552     dwWaitResult = WaitForMultipleObjects (2, event_handle, FALSE, INFINITE);
553     if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_OBJECT_0 + 1) {
554       GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
555           (guint) dwWaitResult);
556       goto stop;
557     }
558 
559     /* Stopping was requested from unprepare() */
560     if (dwWaitResult == WAIT_OBJECT_0 + 1) {
561       GST_DEBUG_OBJECT (self, "operation was cancelled");
562       goto stop;
563     }
564 
565     hr = IAudioClient_GetCurrentPadding (self->loopback_client, &padding);
566     HR_FAILED_GOTO (hr, IAudioClock::Start, stop);
567 
568     if (buffer_frames < padding) {
569       GST_WARNING_OBJECT (self,
570           "Current padding %d is too large (buffer size %d)",
571           padding, buffer_frames);
572       n_frames = 0;
573     } else {
574       n_frames = buffer_frames - padding;
575     }
576 
577     hr = IAudioRenderClient_GetBuffer (self->loopback_render_client, n_frames,
578         &data);
579     HR_FAILED_GOTO (hr, IAudioRenderClient::GetBuffer, stop);
580 
581     hr = IAudioRenderClient_ReleaseBuffer (self->loopback_render_client,
582         n_frames, AUDCLNT_BUFFERFLAGS_SILENT);
583     HR_FAILED_GOTO (hr, IAudioRenderClient::ReleaseBuffer, stop);
584 
585     /* Signal for read thread to wakeup */
586     SetEvent (self->event_handle);
587   }
588 
589 stop:
590   IAudioClient_Stop (self->loopback_client);
591 
592 beach:
593   GST_INFO_OBJECT (self, "Terminate loopback silence feeding thread");
594 
595   return NULL;
596 }
597 
598 static gboolean
gst_wasapi_src_prepare(GstAudioSrc * asrc,GstAudioRingBufferSpec * spec)599 gst_wasapi_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
600 {
601   GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
602   gboolean res = FALSE;
603   REFERENCE_TIME latency_rt;
604   guint bpf, rate, devicep_frames, buffer_frames;
605   HRESULT hr;
606 
607   if (gst_wasapi_src_can_audioclient3 (self)) {
608     if (!gst_wasapi_util_initialize_audioclient3 (GST_ELEMENT (self), spec,
609             (IAudioClient3 *) self->client, self->mix_format, self->low_latency,
610             self->loopback, &devicep_frames))
611       goto beach;
612   } else {
613     if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
614             self->client, self->mix_format, self->sharemode, self->low_latency,
615             self->loopback, &devicep_frames))
616       goto beach;
617   }
618 
619   bpf = GST_AUDIO_INFO_BPF (&spec->info);
620   rate = GST_AUDIO_INFO_RATE (&spec->info);
621 
622   /* Total size in frames of the allocated buffer that we will read from */
623   hr = IAudioClient_GetBufferSize (self->client, &buffer_frames);
624   HR_FAILED_GOTO (hr, IAudioClient::GetBufferSize, beach);
625 
626   GST_INFO_OBJECT (self, "buffer size is %i frames, device period is %i "
627       "frames, bpf is %i bytes, rate is %i Hz", buffer_frames,
628       devicep_frames, bpf, rate);
629 
630   /* Actual latency-time/buffer-time will be different now */
631   spec->segsize = devicep_frames * bpf;
632 
633   /* We need a minimum of 2 segments to ensure glitch-free playback */
634   spec->segtotal = MAX (buffer_frames * bpf / spec->segsize, 2);
635 
636   GST_INFO_OBJECT (self, "segsize is %i, segtotal is %i", spec->segsize,
637       spec->segtotal);
638 
639   /* Get WASAPI latency for logging */
640   hr = IAudioClient_GetStreamLatency (self->client, &latency_rt);
641   HR_FAILED_GOTO (hr, IAudioClient::GetStreamLatency, beach);
642 
643   GST_INFO_OBJECT (self, "wasapi stream latency: %" G_GINT64_FORMAT " (%"
644       G_GINT64_FORMAT " ms)", latency_rt, latency_rt / 10000);
645 
646   /* Set the event handler which will trigger reads */
647   hr = IAudioClient_SetEventHandle (self->client, self->event_handle);
648   HR_FAILED_GOTO (hr, IAudioClient::SetEventHandle, beach);
649 
650   /* Get the clock and the clock freq */
651   if (!gst_wasapi_util_get_clock (GST_ELEMENT (self), self->client,
652           &self->client_clock))
653     goto beach;
654 
655   hr = IAudioClock_GetFrequency (self->client_clock, &self->client_clock_freq);
656   HR_FAILED_GOTO (hr, IAudioClock::GetFrequency, beach);
657 
658   GST_INFO_OBJECT (self, "wasapi clock freq is %" G_GUINT64_FORMAT,
659       self->client_clock_freq);
660 
661   /* Get capture source client and start it up */
662   if (!gst_wasapi_util_get_capture_client (GST_ELEMENT (self), self->client,
663           &self->capture_client)) {
664     goto beach;
665   }
666 
667   /* In case loopback, spawn another dedicated thread for feeding silence data
668    * into wasapi render client */
669   if (self->loopback) {
670     /* don't need to be audioclient3 or low-latency since we will keep pushing
671      * silence data which is not varying over entire playback */
672     if (!gst_wasapi_util_initialize_audioclient (GST_ELEMENT (self), spec,
673             self->loopback_client, self->mix_format, self->sharemode,
674             FALSE, FALSE, &devicep_frames))
675       goto beach;
676 
677     if (!gst_wasapi_util_get_render_client (GST_ELEMENT (self),
678             self->loopback_client, &self->loopback_render_client)) {
679       goto beach;
680     }
681 
682     self->loopback_thread = g_thread_new ("wasapi-loopback",
683         (GThreadFunc) gst_wasapi_src_loopback_silence_feeding_thread, self);
684   }
685 
686   hr = IAudioClient_Start (self->client);
687   HR_FAILED_GOTO (hr, IAudioClock::Start, beach);
688   self->client_needs_restart = FALSE;
689 
690   gst_audio_ring_buffer_set_channel_positions (GST_AUDIO_BASE_SRC
691       (self)->ringbuffer, self->positions);
692 
693   res = TRUE;
694 
695   /* reset cancellable event handle */
696   ResetEvent (self->cancellable);
697 
698 beach:
699 
700   /* unprepare() is not called if prepare() fails, but we want it to be, so call
701    * it manually when needed */
702   if (!res)
703     gst_wasapi_src_unprepare (asrc);
704 
705   return res;
706 }
707 
708 static gboolean
gst_wasapi_src_unprepare(GstAudioSrc * asrc)709 gst_wasapi_src_unprepare (GstAudioSrc * asrc)
710 {
711   GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
712 
713   if (self->client != NULL) {
714     IAudioClient_Stop (self->client);
715   }
716 
717   if (self->capture_client != NULL) {
718     IUnknown_Release (self->capture_client);
719     self->capture_client = NULL;
720   }
721 
722   if (self->client_clock != NULL) {
723     IUnknown_Release (self->client_clock);
724     self->client_clock = NULL;
725   }
726 
727   if (self->loopback_thread) {
728     GST_DEBUG_OBJECT (self, "loopback task thread is stopping");
729 
730     SetEvent (self->loopback_cancellable);
731 
732     g_thread_join (self->loopback_thread);
733     self->loopback_thread = NULL;
734     ResetEvent (self->loopback_cancellable);
735     GST_DEBUG_OBJECT (self, "loopback task thread has been stopped");
736   }
737 
738   if (self->loopback_render_client != NULL) {
739     IUnknown_Release (self->loopback_render_client);
740     self->loopback_render_client = NULL;
741   }
742 
743   self->client_clock_freq = 0;
744 
745   return TRUE;
746 }
747 
748 static guint
gst_wasapi_src_read(GstAudioSrc * asrc,gpointer data,guint length,GstClockTime * timestamp)749 gst_wasapi_src_read (GstAudioSrc * asrc, gpointer data, guint length,
750     GstClockTime * timestamp)
751 {
752   GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
753   HRESULT hr;
754   gint16 *from = NULL;
755   guint wanted = length;
756   guint bpf;
757   DWORD flags;
758 
759   GST_OBJECT_LOCK (self);
760   if (self->client_needs_restart) {
761     hr = IAudioClient_Start (self->client);
762     HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioClient::Start, self,
763         GST_OBJECT_UNLOCK (self); goto err);
764     self->client_needs_restart = FALSE;
765     ResetEvent (self->cancellable);
766     gst_adapter_clear (self->adapter);
767   }
768 
769   bpf = self->mix_format->nBlockAlign;
770   GST_OBJECT_UNLOCK (self);
771 
772   /* If we've accumulated enough data, return it immediately */
773   if (gst_adapter_available (self->adapter) >= wanted) {
774     memcpy (data, gst_adapter_map (self->adapter, wanted), wanted);
775     gst_adapter_flush (self->adapter, wanted);
776     GST_DEBUG_OBJECT (self, "Adapter has enough data, returning %i", wanted);
777     goto out;
778   }
779 
780   while (wanted > 0) {
781     DWORD dwWaitResult;
782     guint got_frames, avail_frames, n_frames, want_frames, read_len;
783     HANDLE event_handle[2];
784 
785     event_handle[0] = self->event_handle;
786     event_handle[1] = self->cancellable;
787 
788     /* Wait for data to become available */
789     dwWaitResult = WaitForMultipleObjects (2, event_handle, FALSE, INFINITE);
790     if (dwWaitResult != WAIT_OBJECT_0 && dwWaitResult != WAIT_OBJECT_0 + 1) {
791       GST_ERROR_OBJECT (self, "Error waiting for event handle: %x",
792           (guint) dwWaitResult);
793       goto err;
794     }
795 
796     /* ::reset was requested */
797     if (dwWaitResult == WAIT_OBJECT_0 + 1) {
798       GST_DEBUG_OBJECT (self, "operation was cancelled");
799       return -1;
800     }
801 
802     hr = IAudioCaptureClient_GetBuffer (self->capture_client,
803         (BYTE **) & from, &got_frames, &flags, NULL, NULL);
804     if (hr != S_OK) {
805       if (hr == AUDCLNT_S_BUFFER_EMPTY) {
806         gchar *msg = gst_wasapi_util_hresult_to_string (hr);
807         GST_WARNING_OBJECT (self, "IAudioCaptureClient::GetBuffer failed: %s"
808             ", retrying", msg);
809         g_free (msg);
810         length = 0;
811         goto out;
812       }
813       HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioCaptureClient::GetBuffer, self,
814           goto err);
815     }
816 
817     if (G_UNLIKELY (flags != 0)) {
818       /* https://docs.microsoft.com/en-us/windows/win32/api/audioclient/ne-audioclient-_audclnt_bufferflags */
819       if (flags & AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY)
820         GST_DEBUG_OBJECT (self, "WASAPI reported discontinuity (glitch?)");
821       if (flags & AUDCLNT_BUFFERFLAGS_TIMESTAMP_ERROR)
822         GST_DEBUG_OBJECT (self, "WASAPI reported a timestamp error");
823     }
824 
825     /* Copy all the frames we got into the adapter, and then extract at most
826      * @wanted size of frames from it. This helps when ::GetBuffer returns more
827      * data than we can handle right now. */
828     {
829       GstBuffer *tmp = gst_buffer_new_allocate (NULL, got_frames * bpf, NULL);
830       /* If flags has AUDCLNT_BUFFERFLAGS_SILENT, we will ignore the actual
831        * data and write out silence, see:
832        * https://docs.microsoft.com/en-us/windows/win32/api/audioclient/ne-audioclient-_audclnt_bufferflags */
833       if (flags & AUDCLNT_BUFFERFLAGS_SILENT)
834         memset (from, 0, got_frames * bpf);
835       gst_buffer_fill (tmp, 0, from, got_frames * bpf);
836       gst_adapter_push (self->adapter, tmp);
837     }
838 
839     /* Release all captured buffers; we copied them above */
840     hr = IAudioCaptureClient_ReleaseBuffer (self->capture_client, got_frames);
841     from = NULL;
842     HR_FAILED_ELEMENT_ERROR_AND (hr, IAudioCaptureClient::ReleaseBuffer, self,
843         goto err);
844 
845     want_frames = wanted / bpf;
846     avail_frames = gst_adapter_available (self->adapter) / bpf;
847 
848     /* Only copy data that will fit into the allocated buffer of size @length */
849     n_frames = MIN (avail_frames, want_frames);
850     read_len = n_frames * bpf;
851 
852     GST_DEBUG_OBJECT (self, "frames captured: %i (%i bytes), "
853         "can read: %i (%i bytes), will read: %i (%i bytes), "
854         "adapter has: %i (%i bytes)", got_frames, got_frames * bpf, want_frames,
855         wanted, n_frames, read_len, avail_frames, avail_frames * bpf);
856 
857     memcpy (data, gst_adapter_map (self->adapter, read_len), read_len);
858     gst_adapter_flush (self->adapter, read_len);
859     wanted -= read_len;
860   }
861 
862 
863 out:
864   return length;
865 
866 err:
867   length = -1;
868   goto out;
869 }
870 
871 static guint
gst_wasapi_src_delay(GstAudioSrc * asrc)872 gst_wasapi_src_delay (GstAudioSrc * asrc)
873 {
874   GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
875   guint delay = 0;
876   HRESULT hr;
877 
878   hr = IAudioClient_GetCurrentPadding (self->client, &delay);
879   HR_FAILED_RET (hr, IAudioClock::GetCurrentPadding, 0);
880 
881   return delay;
882 }
883 
884 static void
gst_wasapi_src_reset(GstAudioSrc * asrc)885 gst_wasapi_src_reset (GstAudioSrc * asrc)
886 {
887   GstWasapiSrc *self = GST_WASAPI_SRC (asrc);
888   HRESULT hr;
889 
890   if (!self->client)
891     return;
892 
893   SetEvent (self->cancellable);
894 
895   GST_OBJECT_LOCK (self);
896   hr = IAudioClient_Stop (self->client);
897   HR_FAILED_AND (hr, IAudioClock::Stop, goto err);
898 
899   hr = IAudioClient_Reset (self->client);
900   HR_FAILED_AND (hr, IAudioClock::Reset, goto err);
901 
902 err:
903   self->client_needs_restart = TRUE;
904   GST_OBJECT_UNLOCK (self);
905 }
906 
907 #if DEFAULT_PROVIDE_CLOCK
908 static GstClockTime
gst_wasapi_src_get_time(GstClock * clock,gpointer user_data)909 gst_wasapi_src_get_time (GstClock * clock, gpointer user_data)
910 {
911   GstWasapiSrc *self = GST_WASAPI_SRC (user_data);
912   HRESULT hr;
913   guint64 devpos;
914   GstClockTime result;
915 
916   if (G_UNLIKELY (self->client_clock == NULL))
917     return GST_CLOCK_TIME_NONE;
918 
919   hr = IAudioClock_GetPosition (self->client_clock, &devpos, NULL);
920   HR_FAILED_RET (hr, IAudioClock::GetPosition, GST_CLOCK_TIME_NONE);
921 
922   result = gst_util_uint64_scale_int (devpos, GST_SECOND,
923       self->client_clock_freq);
924 
925   /*
926      GST_DEBUG_OBJECT (self, "devpos = %" G_GUINT64_FORMAT
927      " frequency = %" G_GUINT64_FORMAT
928      " result = %" G_GUINT64_FORMAT " ms",
929      devpos, self->client_clock_freq, GST_TIME_AS_MSECONDS (result));
930    */
931 
932   return result;
933 }
934 #endif
935