f139db3fb6f65f7026733e02b6ffbc9e9004dcea
[platform/upstream/gstreamer.git] / sys / wasapi2 / gstwasapi2src.c
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  * Copyright (C) 2020 Seungha Yang <seungha@centricular.com>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
20  * Boston, MA 02110-1301, USA.
21  */
22
23 /**
24  * SECTION:element-wasapi2src
25  * @title: wasapi2src
26  *
27  * Provides audio capture from the Windows Audio Session API available with
28  * Windows 10.
29  *
30  * ## Example pipelines
31  * |[
32  * gst-launch-1.0 -v wasapi2src ! fakesrc
33  * ]| Capture from the default audio device and render to fakesrc.
34  *
35  * |[
36  * gst-launch-1.0 -v wasapi2src low-latency=true ! fakesrc
37  * ]| Capture from the default audio device with the minimum possible latency and render to fakesrc.
38  *
39  */
40 #ifdef HAVE_CONFIG_H
41 #include <config.h>
42 #endif
43
44 #include "gstwasapi2src.h"
45 #include "gstwasapi2util.h"
46 #include "gstwasapi2client.h"
47
48 GST_DEBUG_CATEGORY_STATIC (gst_wasapi2_src_debug);
49 #define GST_CAT_DEFAULT gst_wasapi2_src_debug
50
51 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
52     GST_PAD_SRC,
53     GST_PAD_ALWAYS,
54     GST_STATIC_CAPS (GST_WASAPI2_STATIC_CAPS));
55
56 #define DEFAULT_LOW_LATENCY   FALSE
57 #define DEFAULT_MUTE          FALSE
58 #define DEFAULT_VOLUME        1.0
59
60 #define GST_WASAPI2_SRC_LOCK(s) g_mutex_lock(&(s)->lock)
61 #define GST_WASAPI2_SRC_UNLOCK(s) g_mutex_unlock(&(s)->lock)
62
63 enum
64 {
65   PROP_0,
66   PROP_DEVICE,
67   PROP_LOW_LATENCY,
68   PROP_MUTE,
69   PROP_VOLUME,
70   PROP_DISPATCHER,
71 };
72
73 struct _GstWasapi2Src
74 {
75   GstAudioSrc parent;
76
77   GstWasapi2Client *client;
78   GstCaps *cached_caps;
79   gboolean started;
80
81   /* properties */
82   gchar *device_id;
83   gboolean low_latency;
84   gboolean mute;
85   gdouble volume;
86   gpointer dispatcher;
87
88   gboolean mute_changed;
89   gboolean volume_changed;
90
91   /* to protect audioclient from set/get property */
92   GMutex lock;
93 };
94
95 static void gst_wasapi2_src_dispose (GObject * object);
96 static void gst_wasapi2_src_finalize (GObject * object);
97 static void gst_wasapi2_src_set_property (GObject * object, guint prop_id,
98     const GValue * value, GParamSpec * pspec);
99 static void gst_wasapi2_src_get_property (GObject * object, guint prop_id,
100     GValue * value, GParamSpec * pspec);
101
102 static GstCaps *gst_wasapi2_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter);
103
104 static gboolean gst_wasapi2_src_open (GstAudioSrc * asrc);
105 static gboolean gst_wasapi2_src_close (GstAudioSrc * asrc);
106 static gboolean gst_wasapi2_src_prepare (GstAudioSrc * asrc,
107     GstAudioRingBufferSpec * spec);
108 static gboolean gst_wasapi2_src_unprepare (GstAudioSrc * asrc);
109 static guint gst_wasapi2_src_read (GstAudioSrc * asrc, gpointer data,
110     guint length, GstClockTime * timestamp);
111 static guint gst_wasapi2_src_delay (GstAudioSrc * asrc);
112 static void gst_wasapi2_src_reset (GstAudioSrc * asrc);
113
114 static void gst_wasapi2_src_set_mute (GstWasapi2Src * self, gboolean mute);
115 static gboolean gst_wasapi2_src_get_mute (GstWasapi2Src * self);
116 static void gst_wasapi2_src_set_volume (GstWasapi2Src * self, gdouble volume);
117 static gdouble gst_wasapi2_src_get_volume (GstWasapi2Src * self);
118
119 #define gst_wasapi2_src_parent_class parent_class
120 G_DEFINE_TYPE_WITH_CODE (GstWasapi2Src, gst_wasapi2_src, GST_TYPE_AUDIO_SRC,
121     G_IMPLEMENT_INTERFACE (GST_TYPE_STREAM_VOLUME, NULL));
122
123 static void
124 gst_wasapi2_src_class_init (GstWasapi2SrcClass * klass)
125 {
126   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
127   GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
128   GstBaseSrcClass *basesrc_class = GST_BASE_SRC_CLASS (klass);
129   GstAudioSrcClass *audiosrc_class = GST_AUDIO_SRC_CLASS (klass);
130
131   gobject_class->dispose = gst_wasapi2_src_dispose;
132   gobject_class->finalize = gst_wasapi2_src_finalize;
133   gobject_class->set_property = gst_wasapi2_src_set_property;
134   gobject_class->get_property = gst_wasapi2_src_get_property;
135
136   g_object_class_install_property (gobject_class, PROP_DEVICE,
137       g_param_spec_string ("device", "Device",
138           "WASAPI playback device as a GUID string",
139           NULL, GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
140           G_PARAM_STATIC_STRINGS));
141
142   g_object_class_install_property (gobject_class, PROP_LOW_LATENCY,
143       g_param_spec_boolean ("low-latency", "Low latency",
144           "Optimize all settings for lowest latency. Always safe to enable.",
145           DEFAULT_LOW_LATENCY, GST_PARAM_MUTABLE_READY | G_PARAM_READWRITE |
146           G_PARAM_STATIC_STRINGS));
147
148   g_object_class_install_property (gobject_class, PROP_MUTE,
149       g_param_spec_boolean ("mute", "Mute", "Mute state of this stream",
150           DEFAULT_MUTE, GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE |
151           G_PARAM_STATIC_STRINGS));
152
153   g_object_class_install_property (gobject_class, PROP_VOLUME,
154       g_param_spec_double ("volume", "Volume", "Volume of this stream",
155           0.0, 1.0, DEFAULT_VOLUME,
156           GST_PARAM_MUTABLE_PLAYING | G_PARAM_READWRITE |
157           G_PARAM_STATIC_STRINGS));
158
159   /**
160    * GstWasapi2Src:dispatcher:
161    *
162    * ICoreDispatcher COM object used for activating device from UI thread.
163    *
164    * Since: 1.18
165    */
166   g_object_class_install_property (gobject_class, PROP_DISPATCHER,
167       g_param_spec_pointer ("dispatcher", "Dispatcher",
168           "ICoreDispatcher COM object to use. In order for application to ask "
169           "permission of audio device, device activation should be running "
170           "on UI thread via ICoreDispatcher. This element will increase "
171           "the reference count of given ICoreDispatcher and release it after "
172           "use. Therefore, caller does not need to consider additional "
173           "reference count management",
174           GST_PARAM_MUTABLE_READY | G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
175
176   gst_element_class_add_static_pad_template (element_class, &src_template);
177   gst_element_class_set_static_metadata (element_class, "Wasapi2Src",
178       "Source/Audio/Hardware",
179       "Stream audio from an audio capture device through WASAPI",
180       "Nirbheek Chauhan <nirbheek@centricular.com>, "
181       "Ole André Vadla Ravnås <ole.andre.ravnas@tandberg.com>, "
182       "Seungha Yang <seungha@centricular.com>");
183
184   basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_wasapi2_src_get_caps);
185
186   audiosrc_class->open = GST_DEBUG_FUNCPTR (gst_wasapi2_src_open);
187   audiosrc_class->close = GST_DEBUG_FUNCPTR (gst_wasapi2_src_close);
188   audiosrc_class->read = GST_DEBUG_FUNCPTR (gst_wasapi2_src_read);
189   audiosrc_class->prepare = GST_DEBUG_FUNCPTR (gst_wasapi2_src_prepare);
190   audiosrc_class->unprepare = GST_DEBUG_FUNCPTR (gst_wasapi2_src_unprepare);
191   audiosrc_class->delay = GST_DEBUG_FUNCPTR (gst_wasapi2_src_delay);
192   audiosrc_class->reset = GST_DEBUG_FUNCPTR (gst_wasapi2_src_reset);
193
194   GST_DEBUG_CATEGORY_INIT (gst_wasapi2_src_debug, "wasapi2src",
195       0, "Windows audio session API source");
196 }
197
198 static void
199 gst_wasapi2_src_init (GstWasapi2Src * self)
200 {
201   self->mute = DEFAULT_MUTE;
202   self->volume = DEFAULT_VOLUME;
203   self->low_latency = DEFAULT_LOW_LATENCY;
204
205   g_mutex_init (&self->lock);
206 }
207
208 static void
209 gst_wasapi2_src_dispose (GObject * object)
210 {
211   GstWasapi2Src *self = GST_WASAPI2_SRC (object);
212
213   GST_WASAPI2_SRC_LOCK (self);
214   gst_clear_object (&self->client);
215   gst_clear_caps (&self->cached_caps);
216   GST_WASAPI2_SRC_UNLOCK (self);
217
218   G_OBJECT_CLASS (parent_class)->dispose (object);
219 }
220
221 static void
222 gst_wasapi2_src_finalize (GObject * object)
223 {
224   GstWasapi2Src *self = GST_WASAPI2_SRC (object);
225
226   g_free (self->device_id);
227   g_mutex_clear (&self->lock);
228
229   G_OBJECT_CLASS (parent_class)->finalize (object);
230 }
231
232 static void
233 gst_wasapi2_src_set_property (GObject * object, guint prop_id,
234     const GValue * value, GParamSpec * pspec)
235 {
236   GstWasapi2Src *self = GST_WASAPI2_SRC (object);
237
238   switch (prop_id) {
239     case PROP_DEVICE:
240       g_free (self->device_id);
241       self->device_id = g_value_dup_string (value);
242       break;
243     case PROP_LOW_LATENCY:
244       self->low_latency = g_value_get_boolean (value);
245       break;
246     case PROP_MUTE:
247       gst_wasapi2_src_set_mute (self, g_value_get_boolean (value));
248       break;
249     case PROP_VOLUME:
250       gst_wasapi2_src_set_volume (self, g_value_get_double (value));
251       break;
252     case PROP_DISPATCHER:
253       self->dispatcher = g_value_get_pointer (value);
254       break;
255     default:
256       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257       break;
258   }
259 }
260
261 static void
262 gst_wasapi2_src_get_property (GObject * object, guint prop_id,
263     GValue * value, GParamSpec * pspec)
264 {
265   GstWasapi2Src *self = GST_WASAPI2_SRC (object);
266
267   switch (prop_id) {
268     case PROP_DEVICE:
269       g_value_set_string (value, self->device_id);
270       break;
271     case PROP_LOW_LATENCY:
272       g_value_set_boolean (value, self->low_latency);
273       break;
274     case PROP_MUTE:
275       g_value_set_boolean (value, gst_wasapi2_src_get_mute (self));
276       break;
277     case PROP_VOLUME:
278       g_value_set_double (value, gst_wasapi2_src_get_volume (self));
279       break;
280     default:
281       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
282       break;
283   }
284 }
285
286 static GstCaps *
287 gst_wasapi2_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
288 {
289   GstWasapi2Src *self = GST_WASAPI2_SRC (bsrc);
290   GstCaps *caps = NULL;
291
292   /* In case of UWP, device activation might not be finished yet */
293   if (self->client && !gst_wasapi2_client_ensure_activation (self->client)) {
294     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_WRITE, (NULL),
295         ("Failed to activate device"));
296     return NULL;
297   }
298
299   if (self->client)
300     caps = gst_wasapi2_client_get_caps (self->client);
301
302   /* store one caps here so that we can return device caps even if
303    * audioclient was closed due to unprepare() */
304   if (!self->cached_caps && caps)
305     self->cached_caps = gst_caps_ref (caps);
306
307   if (!caps && self->cached_caps)
308     caps = gst_caps_ref (self->cached_caps);
309
310   if (!caps)
311     caps = gst_pad_get_pad_template_caps (bsrc->srcpad);
312
313   if (filter) {
314     GstCaps *filtered =
315         gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
316     gst_caps_unref (caps);
317     caps = filtered;
318   }
319
320   GST_DEBUG_OBJECT (self, "returning caps %" GST_PTR_FORMAT, caps);
321
322   return caps;
323 }
324
325 static gboolean
326 gst_wasapi2_src_open_unlocked (GstAudioSrc * asrc)
327 {
328   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
329
330   gst_clear_object (&self->client);
331
332   self->client =
333       gst_wasapi2_client_new (GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE,
334       self->low_latency, -1, self->device_id, self->dispatcher);
335
336   if (!self->client)
337     return FALSE;
338
339   return TRUE;
340 }
341
342 static gboolean
343 gst_wasapi2_src_open (GstAudioSrc * asrc)
344 {
345   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
346   gboolean ret;
347
348   GST_DEBUG_OBJECT (self, "Opening device");
349
350   GST_WASAPI2_SRC_LOCK (self);
351   ret = gst_wasapi2_src_open_unlocked (asrc);
352   GST_WASAPI2_SRC_UNLOCK (self);
353
354   if (!ret) {
355     GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
356         ("Failed to open device"));
357     return FALSE;
358   }
359
360   return TRUE;
361 }
362
363 static gboolean
364 gst_wasapi2_src_close (GstAudioSrc * asrc)
365 {
366   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
367
368   GST_WASAPI2_SRC_LOCK (self);
369
370   gst_clear_object (&self->client);
371   gst_clear_caps (&self->cached_caps);
372   self->started = FALSE;
373
374   GST_WASAPI2_SRC_UNLOCK (self);
375
376   return TRUE;
377 }
378
379 static gboolean
380 gst_wasapi2_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
381 {
382   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
383   GstAudioBaseSrc *bsrc = GST_AUDIO_BASE_SRC (asrc);
384   gboolean ret = FALSE;
385   HRESULT hr;
386
387   GST_WASAPI2_SRC_LOCK (self);
388   if (!self->client && !gst_wasapi2_src_open_unlocked (asrc)) {
389     GST_ERROR_OBJECT (self, "No audio client was configured");
390     goto done;
391   }
392
393   if (!gst_wasapi2_client_ensure_activation (self->client)) {
394     GST_ERROR_OBJECT (self, "Couldn't activate audio device");
395     goto done;
396   }
397
398   hr = gst_wasapi2_client_open (self->client, spec, bsrc->ringbuffer);
399   if (!gst_wasapi2_result (hr)) {
400     GST_ERROR_OBJECT (self, "Couldn't open audio client");
401     goto done;
402   }
403
404   /* Set mute and volume here again, maybe when "mute" property was set, audioclient
405    * might not be configured at that moment */
406   if (self->mute_changed) {
407     gst_wasapi2_client_set_mute (self->client, self->mute);
408     self->mute_changed = FALSE;
409   }
410
411   if (self->volume_changed) {
412     gst_wasapi2_client_set_volume (self->client, self->volume);
413     self->volume_changed = FALSE;
414   }
415
416   /* Will start IAudioClient on the first read request */
417   self->started = FALSE;
418   ret = TRUE;
419
420 done:
421   GST_WASAPI2_SRC_UNLOCK (self);
422
423   return ret;
424 }
425
426 static gboolean
427 gst_wasapi2_src_unprepare (GstAudioSrc * asrc)
428 {
429   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
430
431   self->started = FALSE;
432
433   /* Will reopen device later prepare() */
434   GST_WASAPI2_SRC_LOCK (self);
435   if (self->client) {
436     gst_wasapi2_client_stop (self->client);
437     gst_clear_object (&self->client);
438   }
439   GST_WASAPI2_SRC_UNLOCK (self);
440
441   return TRUE;
442 }
443
444 static guint
445 gst_wasapi2_src_read (GstAudioSrc * asrc, gpointer data, guint length,
446     GstClockTime * timestamp)
447 {
448   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
449   HRESULT hr;
450   guint read_len = 0;
451
452   if (!self->client) {
453     GST_ERROR_OBJECT (self, "No audio client was configured");
454     return -1;
455   }
456
457   if (!self->started) {
458     HRESULT hr = gst_wasapi2_client_start (self->client);
459     if (!gst_wasapi2_result (hr)) {
460       GST_ERROR_OBJECT (self, "Failed to re-start client");
461       return -1;
462     }
463
464     self->started = TRUE;
465   }
466
467   hr = gst_wasapi2_client_read (self->client, data, length, &read_len);
468   if (!gst_wasapi2_result (hr)) {
469     GST_WARNING_OBJECT (self, "Failed to read data");
470     return -1;
471   }
472
473   return read_len;
474 }
475
476 static guint
477 gst_wasapi2_src_delay (GstAudioSrc * asrc)
478 {
479   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
480   guint32 delay;
481   HRESULT hr;
482
483   if (!self->client)
484     return 0;
485
486   hr = gst_wasapi2_client_delay (self->client, &delay);
487   if (!gst_wasapi2_result (hr)) {
488     GST_WARNING_OBJECT (self, "Failed to get delay");
489     return 0;
490   }
491
492   return delay;
493 }
494
495 static void
496 gst_wasapi2_src_reset (GstAudioSrc * asrc)
497 {
498   GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
499
500   GST_DEBUG_OBJECT (self, "reset called");
501
502   self->started = FALSE;
503
504   if (!self->client)
505     return;
506
507   gst_wasapi2_client_stop (self->client);
508 }
509
510 static void
511 gst_wasapi2_src_set_mute (GstWasapi2Src * self, gboolean mute)
512 {
513   GST_WASAPI2_SRC_LOCK (self);
514
515   self->mute = mute;
516   self->mute_changed = TRUE;
517
518   if (self->client) {
519     HRESULT hr = gst_wasapi2_client_set_mute (self->client, mute);
520     if (FAILED (hr)) {
521       GST_INFO_OBJECT (self, "Couldn't set mute");
522     } else {
523       self->mute_changed = FALSE;
524     }
525   } else {
526     GST_DEBUG_OBJECT (self, "audio client is not configured yet");
527   }
528
529   GST_WASAPI2_SRC_UNLOCK (self);
530 }
531
532 static gboolean
533 gst_wasapi2_src_get_mute (GstWasapi2Src * self)
534 {
535   gboolean mute;
536
537   GST_WASAPI2_SRC_LOCK (self);
538
539   mute = self->mute;
540
541   if (self->client) {
542     HRESULT hr = gst_wasapi2_client_get_mute (self->client, &mute);
543     if (FAILED (hr)) {
544       GST_INFO_OBJECT (self, "Couldn't get mute state");
545     } else {
546       self->mute = mute;
547     }
548   } else {
549     GST_DEBUG_OBJECT (self, "audio client is not configured yet");
550   }
551
552   GST_WASAPI2_SRC_UNLOCK (self);
553
554   return mute;
555 }
556
557 static void
558 gst_wasapi2_src_set_volume (GstWasapi2Src * self, gdouble volume)
559 {
560   GST_WASAPI2_SRC_LOCK (self);
561
562   self->volume = volume;
563   /* clip volume value */
564   self->volume = MAX (0.0, self->volume);
565   self->volume = MIN (1.0, self->volume);
566   self->volume_changed = TRUE;
567
568   if (self->client) {
569     HRESULT hr =
570         gst_wasapi2_client_set_volume (self->client, (gfloat) self->volume);
571     if (FAILED (hr)) {
572       GST_INFO_OBJECT (self, "Couldn't set volume");
573     } else {
574       self->volume_changed = FALSE;
575     }
576   } else {
577     GST_DEBUG_OBJECT (self, "audio client is not configured yet");
578   }
579
580   GST_WASAPI2_SRC_UNLOCK (self);
581 }
582
583 static gdouble
584 gst_wasapi2_src_get_volume (GstWasapi2Src * self)
585 {
586   gfloat volume;
587
588   GST_WASAPI2_SRC_LOCK (self);
589
590   volume = (gfloat) self->volume;
591
592   if (self->client) {
593     HRESULT hr = gst_wasapi2_client_get_volume (self->client, &volume);
594     if (FAILED (hr)) {
595       GST_INFO_OBJECT (self, "Couldn't get volume");
596     } else {
597       self->volume = volume;
598     }
599   } else {
600     GST_DEBUG_OBJECT (self, "audio client is not configured yet");
601   }
602
603   GST_WASAPI2_SRC_UNLOCK (self);
604
605   volume = MAX (0.0, volume);
606   volume = MIN (1.0, volume);
607
608   return volume;
609 }