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>
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.
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.
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.
24 * SECTION:element-wasapi2src
27 * Provides audio capture from the Windows Audio Session API available with
30 * ## Example pipelines
32 * gst-launch-1.0 -v wasapi2src ! fakesrc
33 * ]| Capture from the default audio device and render to fakesrc.
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.
44 #include "gstwasapi2src.h"
45 #include "gstwasapi2util.h"
46 #include "gstwasapi2client.h"
48 GST_DEBUG_CATEGORY_STATIC (gst_wasapi2_src_debug);
49 #define GST_CAT_DEFAULT gst_wasapi2_src_debug
51 static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
54 GST_STATIC_CAPS (GST_WASAPI2_STATIC_CAPS));
56 #define DEFAULT_LOW_LATENCY FALSE
57 #define DEFAULT_MUTE FALSE
58 #define DEFAULT_VOLUME 1.0
60 #define GST_WASAPI2_SRC_LOCK(s) g_mutex_lock(&(s)->lock)
61 #define GST_WASAPI2_SRC_UNLOCK(s) g_mutex_unlock(&(s)->lock)
77 GstWasapi2Client *client;
88 gboolean mute_changed;
89 gboolean volume_changed;
91 /* to protect audioclient from set/get property */
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);
102 static GstCaps *gst_wasapi2_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter);
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);
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);
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));
124 gst_wasapi2_src_class_init (GstWasapi2SrcClass * klass)
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);
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;
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));
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));
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));
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));
160 * GstWasapi2Src:dispatcher:
162 * ICoreDispatcher COM object used for activating device from UI thread.
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));
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>");
184 basesrc_class->get_caps = GST_DEBUG_FUNCPTR (gst_wasapi2_src_get_caps);
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);
194 GST_DEBUG_CATEGORY_INIT (gst_wasapi2_src_debug, "wasapi2src",
195 0, "Windows audio session API source");
199 gst_wasapi2_src_init (GstWasapi2Src * self)
201 self->mute = DEFAULT_MUTE;
202 self->volume = DEFAULT_VOLUME;
203 self->low_latency = DEFAULT_LOW_LATENCY;
205 g_mutex_init (&self->lock);
209 gst_wasapi2_src_dispose (GObject * object)
211 GstWasapi2Src *self = GST_WASAPI2_SRC (object);
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);
218 G_OBJECT_CLASS (parent_class)->dispose (object);
222 gst_wasapi2_src_finalize (GObject * object)
224 GstWasapi2Src *self = GST_WASAPI2_SRC (object);
226 g_free (self->device_id);
227 g_mutex_clear (&self->lock);
229 G_OBJECT_CLASS (parent_class)->finalize (object);
233 gst_wasapi2_src_set_property (GObject * object, guint prop_id,
234 const GValue * value, GParamSpec * pspec)
236 GstWasapi2Src *self = GST_WASAPI2_SRC (object);
240 g_free (self->device_id);
241 self->device_id = g_value_dup_string (value);
243 case PROP_LOW_LATENCY:
244 self->low_latency = g_value_get_boolean (value);
247 gst_wasapi2_src_set_mute (self, g_value_get_boolean (value));
250 gst_wasapi2_src_set_volume (self, g_value_get_double (value));
252 case PROP_DISPATCHER:
253 self->dispatcher = g_value_get_pointer (value);
256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
262 gst_wasapi2_src_get_property (GObject * object, guint prop_id,
263 GValue * value, GParamSpec * pspec)
265 GstWasapi2Src *self = GST_WASAPI2_SRC (object);
269 g_value_set_string (value, self->device_id);
271 case PROP_LOW_LATENCY:
272 g_value_set_boolean (value, self->low_latency);
275 g_value_set_boolean (value, gst_wasapi2_src_get_mute (self));
278 g_value_set_double (value, gst_wasapi2_src_get_volume (self));
281 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
287 gst_wasapi2_src_get_caps (GstBaseSrc * bsrc, GstCaps * filter)
289 GstWasapi2Src *self = GST_WASAPI2_SRC (bsrc);
290 GstCaps *caps = NULL;
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"));
300 caps = gst_wasapi2_client_get_caps (self->client);
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);
307 if (!caps && self->cached_caps)
308 caps = gst_caps_ref (self->cached_caps);
311 caps = gst_pad_get_pad_template_caps (bsrc->srcpad);
315 gst_caps_intersect_full (filter, caps, GST_CAPS_INTERSECT_FIRST);
316 gst_caps_unref (caps);
320 GST_DEBUG_OBJECT (self, "returning caps %" GST_PTR_FORMAT, caps);
326 gst_wasapi2_src_open_unlocked (GstAudioSrc * asrc)
328 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
330 gst_clear_object (&self->client);
333 gst_wasapi2_client_new (GST_WASAPI2_CLIENT_DEVICE_CLASS_CAPTURE,
334 self->low_latency, -1, self->device_id, self->dispatcher);
343 gst_wasapi2_src_open (GstAudioSrc * asrc)
345 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
348 GST_DEBUG_OBJECT (self, "Opening device");
350 GST_WASAPI2_SRC_LOCK (self);
351 ret = gst_wasapi2_src_open_unlocked (asrc);
352 GST_WASAPI2_SRC_UNLOCK (self);
355 GST_ELEMENT_ERROR (self, RESOURCE, OPEN_READ, (NULL),
356 ("Failed to open device"));
364 gst_wasapi2_src_close (GstAudioSrc * asrc)
366 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
368 GST_WASAPI2_SRC_LOCK (self);
370 gst_clear_object (&self->client);
371 gst_clear_caps (&self->cached_caps);
372 self->started = FALSE;
374 GST_WASAPI2_SRC_UNLOCK (self);
380 gst_wasapi2_src_prepare (GstAudioSrc * asrc, GstAudioRingBufferSpec * spec)
382 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
383 GstAudioBaseSrc *bsrc = GST_AUDIO_BASE_SRC (asrc);
384 gboolean ret = FALSE;
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");
393 if (!gst_wasapi2_client_ensure_activation (self->client)) {
394 GST_ERROR_OBJECT (self, "Couldn't activate audio device");
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");
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;
411 if (self->volume_changed) {
412 gst_wasapi2_client_set_volume (self->client, self->volume);
413 self->volume_changed = FALSE;
416 /* Will start IAudioClient on the first read request */
417 self->started = FALSE;
421 GST_WASAPI2_SRC_UNLOCK (self);
427 gst_wasapi2_src_unprepare (GstAudioSrc * asrc)
429 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
431 self->started = FALSE;
433 /* Will reopen device later prepare() */
434 GST_WASAPI2_SRC_LOCK (self);
436 gst_wasapi2_client_stop (self->client);
437 gst_clear_object (&self->client);
439 GST_WASAPI2_SRC_UNLOCK (self);
445 gst_wasapi2_src_read (GstAudioSrc * asrc, gpointer data, guint length,
446 GstClockTime * timestamp)
448 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
453 GST_ERROR_OBJECT (self, "No audio client was configured");
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");
464 self->started = TRUE;
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");
477 gst_wasapi2_src_delay (GstAudioSrc * asrc)
479 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
486 hr = gst_wasapi2_client_delay (self->client, &delay);
487 if (!gst_wasapi2_result (hr)) {
488 GST_WARNING_OBJECT (self, "Failed to get delay");
496 gst_wasapi2_src_reset (GstAudioSrc * asrc)
498 GstWasapi2Src *self = GST_WASAPI2_SRC (asrc);
500 GST_DEBUG_OBJECT (self, "reset called");
502 self->started = FALSE;
507 gst_wasapi2_client_stop (self->client);
511 gst_wasapi2_src_set_mute (GstWasapi2Src * self, gboolean mute)
513 GST_WASAPI2_SRC_LOCK (self);
516 self->mute_changed = TRUE;
519 HRESULT hr = gst_wasapi2_client_set_mute (self->client, mute);
521 GST_INFO_OBJECT (self, "Couldn't set mute");
523 self->mute_changed = FALSE;
526 GST_DEBUG_OBJECT (self, "audio client is not configured yet");
529 GST_WASAPI2_SRC_UNLOCK (self);
533 gst_wasapi2_src_get_mute (GstWasapi2Src * self)
537 GST_WASAPI2_SRC_LOCK (self);
542 HRESULT hr = gst_wasapi2_client_get_mute (self->client, &mute);
544 GST_INFO_OBJECT (self, "Couldn't get mute state");
549 GST_DEBUG_OBJECT (self, "audio client is not configured yet");
552 GST_WASAPI2_SRC_UNLOCK (self);
558 gst_wasapi2_src_set_volume (GstWasapi2Src * self, gdouble volume)
560 GST_WASAPI2_SRC_LOCK (self);
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;
570 gst_wasapi2_client_set_volume (self->client, (gfloat) self->volume);
572 GST_INFO_OBJECT (self, "Couldn't set volume");
574 self->volume_changed = FALSE;
577 GST_DEBUG_OBJECT (self, "audio client is not configured yet");
580 GST_WASAPI2_SRC_UNLOCK (self);
584 gst_wasapi2_src_get_volume (GstWasapi2Src * self)
588 GST_WASAPI2_SRC_LOCK (self);
590 volume = (gfloat) self->volume;
593 HRESULT hr = gst_wasapi2_client_get_volume (self->client, &volume);
595 GST_INFO_OBJECT (self, "Couldn't get volume");
597 self->volume = volume;
600 GST_DEBUG_OBJECT (self, "audio client is not configured yet");
603 GST_WASAPI2_SRC_UNLOCK (self);
605 volume = MAX (0.0, volume);
606 volume = MIN (1.0, volume);