1 /* GStreamer
2 * Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
3 *
4 * pulsedeviceprovider.c: pulseaudio device probing and monitoring
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., 59 Temple Place - Suite 330,
19 * Boston, MA 02111-1307, USA.
20 */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "pulsedeviceprovider.h"
27
28 #include <string.h>
29
30 #include <gst/gst.h>
31
32 #include "pulsesrc.h"
33 #include "pulsesink.h"
34 #include "pulseutil.h"
35
36
37 GST_DEBUG_CATEGORY_EXTERN (pulse_debug);
38 #define GST_CAT_DEFAULT pulse_debug
39
40
41 static GstDevice *gst_pulse_device_new (guint id,
42 const gchar * device_name, GstCaps * caps, const gchar * internal_name,
43 GstPulseDeviceType type, GstStructure * properties, gboolean is_default);
44
45 G_DEFINE_TYPE (GstPulseDeviceProvider, gst_pulse_device_provider,
46 GST_TYPE_DEVICE_PROVIDER);
47
48 static void gst_pulse_device_provider_finalize (GObject * object);
49 static void gst_pulse_device_provider_set_property (GObject * object,
50 guint prop_id, const GValue * value, GParamSpec * pspec);
51 static void gst_pulse_device_provider_get_property (GObject * object,
52 guint prop_id, GValue * value, GParamSpec * pspec);
53
54
55 static GList *gst_pulse_device_provider_probe (GstDeviceProvider * provider);
56 static gboolean gst_pulse_device_provider_start (GstDeviceProvider * provider);
57 static void gst_pulse_device_provider_stop (GstDeviceProvider * provider);
58
59 enum
60 {
61 PROP_0,
62 PROP_SERVER,
63 PROP_CLIENT_NAME,
64 PROP_LAST
65 };
66
67
68 typedef struct
69 {
70 GList *devices;
71 GstPulseDeviceProvider *self;
72 } ListDevicesData;
73
74 static void
gst_pulse_device_provider_class_init(GstPulseDeviceProviderClass * klass)75 gst_pulse_device_provider_class_init (GstPulseDeviceProviderClass * klass)
76 {
77 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
78 GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
79 gchar *client_name;
80
81 gobject_class->set_property = gst_pulse_device_provider_set_property;
82 gobject_class->get_property = gst_pulse_device_provider_get_property;
83 gobject_class->finalize = gst_pulse_device_provider_finalize;
84
85 dm_class->probe = gst_pulse_device_provider_probe;
86 dm_class->start = gst_pulse_device_provider_start;
87 dm_class->stop = gst_pulse_device_provider_stop;
88
89 g_object_class_install_property (gobject_class,
90 PROP_SERVER,
91 g_param_spec_string ("server", "Server",
92 "The PulseAudio server to connect to", NULL,
93 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
94
95 client_name = gst_pulse_client_name ();
96 g_object_class_install_property (gobject_class,
97 PROP_CLIENT_NAME,
98 g_param_spec_string ("client-name", "Client Name",
99 "The PulseAudio client_name_to_use", client_name,
100 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS |
101 GST_PARAM_MUTABLE_READY));
102 g_free (client_name);
103
104 gst_device_provider_class_set_static_metadata (dm_class,
105 "PulseAudio Device Provider", "Sink/Source/Audio",
106 "List and provider PulseAudio source and sink devices",
107 "Olivier Crete <olivier.crete@collabora.com>");
108 }
109
110 static void
gst_pulse_device_provider_init(GstPulseDeviceProvider * self)111 gst_pulse_device_provider_init (GstPulseDeviceProvider * self)
112 {
113 self->client_name = gst_pulse_client_name ();
114 }
115
116 static void
gst_pulse_device_provider_finalize(GObject * object)117 gst_pulse_device_provider_finalize (GObject * object)
118 {
119 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (object);
120
121 g_free (self->client_name);
122 g_free (self->server);
123 g_free (self->default_sink_name);
124 g_free (self->default_source_name);
125
126 G_OBJECT_CLASS (gst_pulse_device_provider_parent_class)->finalize (object);
127 }
128
129
130 static void
gst_pulse_device_provider_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)131 gst_pulse_device_provider_set_property (GObject * object,
132 guint prop_id, const GValue * value, GParamSpec * pspec)
133 {
134 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (object);
135
136 switch (prop_id) {
137 case PROP_SERVER:
138 g_free (self->server);
139 self->server = g_value_dup_string (value);
140 break;
141 case PROP_CLIENT_NAME:
142 g_free (self->client_name);
143 if (!g_value_get_string (value)) {
144 GST_WARNING_OBJECT (self,
145 "Empty PulseAudio client name not allowed. "
146 "Resetting to default value");
147 self->client_name = gst_pulse_client_name ();
148 } else
149 self->client_name = g_value_dup_string (value);
150 break;
151 default:
152 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
153 break;
154 }
155 }
156
157 static void
gst_pulse_device_provider_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)158 gst_pulse_device_provider_get_property (GObject * object,
159 guint prop_id, GValue * value, GParamSpec * pspec)
160 {
161 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (object);
162
163 switch (prop_id) {
164 case PROP_SERVER:
165 g_value_set_string (value, self->server);
166 break;
167 case PROP_CLIENT_NAME:
168 g_value_set_string (value, self->client_name);
169 break;
170 default:
171 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
172 break;
173 }
174 }
175
176 static void
context_state_cb(pa_context * c,void * userdata)177 context_state_cb (pa_context * c, void *userdata)
178 {
179 GstPulseDeviceProvider *self = userdata;
180
181 switch (pa_context_get_state (c)) {
182 case PA_CONTEXT_READY:
183 case PA_CONTEXT_TERMINATED:
184 case PA_CONTEXT_FAILED:
185 pa_threaded_mainloop_signal (self->mainloop, 0);
186 break;
187
188 case PA_CONTEXT_UNCONNECTED:
189 case PA_CONTEXT_CONNECTING:
190 case PA_CONTEXT_AUTHORIZING:
191 case PA_CONTEXT_SETTING_NAME:
192 break;
193 }
194 }
195
196 static GstDevice *
new_source(GstPulseDeviceProvider * self,const pa_source_info * info)197 new_source (GstPulseDeviceProvider * self, const pa_source_info * info)
198 {
199 GstCaps *caps;
200 GstStructure *props;
201 guint i;
202
203 caps = gst_caps_new_empty ();
204
205 for (i = 0; i < info->n_formats; i++)
206 gst_caps_append (caps, gst_pulse_format_info_to_caps (info->formats[i]));
207
208 props = gst_pulse_make_structure (info->proplist);
209
210 if (!g_strcmp0 (gst_structure_get_string (props, "device.api"), "alsa"))
211 gst_device_provider_hide_provider (GST_DEVICE_PROVIDER (self),
212 "alsadeviceprovider");
213
214 return gst_pulse_device_new (info->index, info->description,
215 caps, info->name, GST_PULSE_DEVICE_TYPE_SOURCE, props,
216 !g_strcmp0 (info->name, self->default_source_name));
217 }
218
219 static GstDevice *
new_sink(GstPulseDeviceProvider * self,const pa_sink_info * info)220 new_sink (GstPulseDeviceProvider * self, const pa_sink_info * info)
221 {
222 GstCaps *caps;
223 GstStructure *props;
224 guint i;
225
226 caps = gst_caps_new_empty ();
227
228 for (i = 0; i < info->n_formats; i++)
229 gst_caps_append (caps, gst_pulse_format_info_to_caps (info->formats[i]));
230
231 props = gst_pulse_make_structure (info->proplist);
232
233 return gst_pulse_device_new (info->index, info->description,
234 caps, info->name, GST_PULSE_DEVICE_TYPE_SINK, props,
235 !g_strcmp0 (info->name, self->default_sink_name));
236 }
237
238 static void
get_source_info_cb(pa_context * context,const pa_source_info * info,int eol,void * userdata)239 get_source_info_cb (pa_context * context,
240 const pa_source_info * info, int eol, void *userdata)
241 {
242 GstPulseDeviceProvider *self = userdata;
243 GstDevice *dev;
244
245 if (eol) {
246 pa_threaded_mainloop_signal (self->mainloop, 0);
247 return;
248 }
249
250 dev = new_source (self, info);
251
252 if (dev)
253 gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), dev);
254 }
255
256 static void
get_server_info_cb(pa_context * context,const pa_server_info * info,void * userdata)257 get_server_info_cb (pa_context * context, const pa_server_info * info,
258 void *userdata)
259 {
260 GList *tmp, *devices = NULL;
261 GstPulseDeviceProvider *self = userdata;
262
263 GST_OBJECT_LOCK (self);
264 g_free (self->default_sink_name);
265 g_free (self->default_source_name);
266 self->default_sink_name = g_strdup (info->default_sink_name);
267 self->default_source_name = g_strdup (info->default_source_name);
268 GST_DEBUG_OBJECT (self, "Default sink name: %s", self->default_sink_name);
269
270 for (tmp = GST_DEVICE_PROVIDER (self)->devices; tmp; tmp = tmp->next)
271 devices = g_list_prepend (devices, gst_object_ref (tmp->data));
272 GST_OBJECT_UNLOCK (self);
273
274 for (tmp = devices; tmp; tmp = tmp->next) {
275 GstPulseDevice *dev = tmp->data;
276 GstStructure *props = gst_device_get_properties (GST_DEVICE (dev));
277 gboolean was_default = FALSE, is_default = FALSE;
278
279 g_assert (props);
280 gst_structure_get_boolean (props, "is-default", &was_default);
281 switch (dev->type) {
282 case GST_PULSE_DEVICE_TYPE_SINK:
283 is_default = !g_strcmp0 (dev->internal_name, self->default_sink_name);
284 break;
285 case GST_PULSE_DEVICE_TYPE_SOURCE:
286 is_default = !g_strcmp0 (dev->internal_name, self->default_source_name);
287 break;
288 }
289
290 if (was_default != is_default) {
291 GstDevice *updated_device;
292 gchar *name = gst_device_get_display_name (GST_DEVICE (dev));
293
294 gst_structure_set (props, "is-default", G_TYPE_BOOLEAN, is_default, NULL);
295 updated_device = gst_pulse_device_new (dev->device_index,
296 name, gst_device_get_caps (GST_DEVICE (dev)), dev->internal_name,
297 dev->type, props, is_default);
298
299 gst_device_provider_device_changed (GST_DEVICE_PROVIDER (self),
300 updated_device, GST_DEVICE (dev));
301
302 g_free (name);
303 } else {
304 gst_structure_free (props);
305 }
306 }
307 g_list_free_full (devices, gst_object_unref);
308
309 pa_threaded_mainloop_signal (self->mainloop, 0);
310 }
311
312 static void
get_sink_info_cb(pa_context * context,const pa_sink_info * info,int eol,void * userdata)313 get_sink_info_cb (pa_context * context,
314 const pa_sink_info * info, int eol, void *userdata)
315 {
316 GstPulseDeviceProvider *self = userdata;
317 GstDevice *dev;
318
319 if (eol) {
320 pa_threaded_mainloop_signal (self->mainloop, 0);
321 return;
322 }
323
324 dev = new_sink (self, info);
325
326 if (dev)
327 gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), dev);
328 }
329
330 static void
context_subscribe_cb(pa_context * context,pa_subscription_event_type_t type,uint32_t idx,void * userdata)331 context_subscribe_cb (pa_context * context, pa_subscription_event_type_t type,
332 uint32_t idx, void *userdata)
333 {
334 GstPulseDeviceProvider *self = userdata;
335 GstDeviceProvider *provider = userdata;
336 pa_subscription_event_type_t facility =
337 type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
338 pa_subscription_event_type_t event_type =
339 type & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
340
341 if (facility == PA_SUBSCRIPTION_EVENT_SERVER ||
342 facility != PA_SUBSCRIPTION_EVENT_CHANGE) {
343 pa_context_get_server_info (self->context, get_server_info_cb, self);
344 }
345
346 if (facility != PA_SUBSCRIPTION_EVENT_SOURCE &&
347 facility != PA_SUBSCRIPTION_EVENT_SINK)
348 return;
349
350 if (event_type == PA_SUBSCRIPTION_EVENT_NEW) {
351 /* Microphone in the source output has changed */
352
353 if (facility == PA_SUBSCRIPTION_EVENT_SOURCE)
354 pa_context_get_source_info_by_index (context, idx, get_source_info_cb,
355 self);
356 else if (facility == PA_SUBSCRIPTION_EVENT_SINK)
357 pa_context_get_sink_info_by_index (context, idx, get_sink_info_cb, self);
358 } else if (event_type == PA_SUBSCRIPTION_EVENT_REMOVE) {
359 GstPulseDevice *dev = NULL;
360 GList *item;
361
362 GST_OBJECT_LOCK (self);
363 for (item = provider->devices; item; item = item->next) {
364 dev = item->data;
365
366 if (((facility == PA_SUBSCRIPTION_EVENT_SOURCE &&
367 dev->type == GST_PULSE_DEVICE_TYPE_SOURCE) ||
368 (facility == PA_SUBSCRIPTION_EVENT_SINK &&
369 dev->type == GST_PULSE_DEVICE_TYPE_SINK)) &&
370 dev->device_index == idx) {
371 gst_object_ref (dev);
372 break;
373 }
374 dev = NULL;
375 }
376 GST_OBJECT_UNLOCK (self);
377
378 if (dev) {
379 gst_device_provider_device_remove (GST_DEVICE_PROVIDER (self),
380 GST_DEVICE (dev));
381 gst_object_unref (dev);
382 }
383 }
384 }
385
386 static void
get_source_info_list_cb(pa_context * context,const pa_source_info * info,int eol,void * userdata)387 get_source_info_list_cb (pa_context * context, const pa_source_info * info,
388 int eol, void *userdata)
389 {
390 ListDevicesData *data = userdata;
391
392 if (eol)
393 return;
394
395 data->devices =
396 g_list_prepend (data->devices,
397 gst_object_ref_sink (new_source (data->self, info)));
398 }
399
400 static void
get_sink_info_list_cb(pa_context * context,const pa_sink_info * info,int eol,void * userdata)401 get_sink_info_list_cb (pa_context * context, const pa_sink_info * info,
402 int eol, void *userdata)
403 {
404 ListDevicesData *data = userdata;
405
406 if (eol)
407 return;
408
409 data->devices =
410 g_list_prepend (data->devices, gst_object_ref_sink (new_sink (data->self,
411 info)));
412 }
413
414 static GList *
gst_pulse_device_provider_probe(GstDeviceProvider * provider)415 gst_pulse_device_provider_probe (GstDeviceProvider * provider)
416 {
417 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (provider);
418 pa_mainloop *m = NULL;
419 pa_context *c = NULL;
420 pa_operation *o;
421 ListDevicesData data = { NULL, self };
422
423 if (!(m = pa_mainloop_new ()))
424 return NULL;
425
426 if (!(c = pa_context_new (pa_mainloop_get_api (m), self->client_name))) {
427 GST_ERROR_OBJECT (self, "Failed to create context");
428 goto failed;
429 }
430
431 if (pa_context_connect (c, self->server, 0, NULL) < 0) {
432 GST_ERROR_OBJECT (self, "Failed to connect: %s",
433 pa_strerror (pa_context_errno (self->context)));
434 goto failed;
435 }
436
437 for (;;) {
438 pa_context_state_t state;
439
440 state = pa_context_get_state (c);
441
442 if (!PA_CONTEXT_IS_GOOD (state)) {
443 GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Failed to connect: %s",
444 pa_strerror (pa_context_errno (c))), (NULL));
445 goto failed;
446 }
447
448 if (state == PA_CONTEXT_READY)
449 break;
450
451 /* Wait until the context is ready */
452 if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
453 goto failed;
454
455 }
456 GST_DEBUG_OBJECT (self, "connected");
457
458 o = pa_context_get_sink_info_list (c, get_sink_info_list_cb, &data);
459 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
460 pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
461 if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
462 break;
463 }
464 pa_operation_unref (o);
465
466 o = pa_context_get_source_info_list (c, get_source_info_list_cb, &data);
467 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
468 pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
469 if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
470 break;
471 }
472 pa_operation_unref (o);
473
474 pa_context_disconnect (c);
475 pa_mainloop_free (m);
476
477 return data.devices;
478
479 failed:
480
481 return NULL;
482 }
483
484 static gboolean
run_pulse_operation(GstPulseDeviceProvider * self,pa_operation * operation)485 run_pulse_operation (GstPulseDeviceProvider * self, pa_operation * operation)
486 {
487 if (!operation)
488 return FALSE;
489
490 while (pa_operation_get_state (operation) == PA_OPERATION_RUNNING) {
491 if (!PA_CONTEXT_IS_GOOD (pa_context_get_state ((self->context)))) {
492 pa_operation_cancel (operation);
493 pa_operation_unref (operation);
494 return FALSE;
495 }
496
497 pa_threaded_mainloop_wait (self->mainloop);
498 }
499
500 pa_operation_unref (operation);
501
502 return TRUE;
503 }
504
505 static gboolean
gst_pulse_device_provider_start(GstDeviceProvider * provider)506 gst_pulse_device_provider_start (GstDeviceProvider * provider)
507 {
508 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (provider);
509
510 if (!(self->mainloop = pa_threaded_mainloop_new ())) {
511 GST_ERROR_OBJECT (self, "Could not create pulseaudio mainloop");
512 goto mainloop_failed;
513 }
514 if (pa_threaded_mainloop_start (self->mainloop) < 0) {
515 GST_ERROR_OBJECT (self, "Could not start pulseaudio mainloop");
516 pa_threaded_mainloop_free (self->mainloop);
517 self->mainloop = NULL;
518 goto mainloop_failed;
519 }
520
521 pa_threaded_mainloop_lock (self->mainloop);
522
523 if (!(self->context =
524 pa_context_new (pa_threaded_mainloop_get_api (self->mainloop),
525 self->client_name))) {
526 GST_ERROR_OBJECT (self, "Failed to create context");
527 goto unlock_and_fail;
528 }
529
530 pa_context_set_state_callback (self->context, context_state_cb, self);
531 pa_context_set_subscribe_callback (self->context, context_subscribe_cb, self);
532
533
534 GST_DEBUG_OBJECT (self, "connect to server %s", GST_STR_NULL (self->server));
535
536 if (pa_context_connect (self->context, self->server, 0, NULL) < 0) {
537 GST_ERROR_OBJECT (self, "Failed to connect: %s",
538 pa_strerror (pa_context_errno (self->context)));
539 goto unlock_and_fail;
540 }
541
542 for (;;) {
543 pa_context_state_t state;
544
545 state = pa_context_get_state (self->context);
546
547 if (!PA_CONTEXT_IS_GOOD (state)) {
548 GST_ERROR_OBJECT (self, "Failed to connect: %s",
549 pa_strerror (pa_context_errno (self->context)));
550 goto unlock_and_fail;
551 }
552
553 if (state == PA_CONTEXT_READY)
554 break;
555
556 /* Wait until the context is ready */
557 pa_threaded_mainloop_wait (self->mainloop);
558 }
559 GST_DEBUG_OBJECT (self, "connected");
560
561 pa_context_subscribe (self->context,
562 PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SINK |
563 PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, NULL, NULL);
564
565 if (!run_pulse_operation (self, pa_context_get_server_info (self->context,
566 get_server_info_cb, self)))
567 goto unlock_and_fail;
568
569 if (!run_pulse_operation (self,
570 pa_context_get_source_info_list (self->context, get_source_info_cb,
571 self)))
572 goto unlock_and_fail;
573
574 if (!run_pulse_operation (self, pa_context_get_sink_info_list (self->context,
575 get_sink_info_cb, self)))
576 goto unlock_and_fail;
577
578 pa_threaded_mainloop_unlock (self->mainloop);
579
580 return TRUE;
581
582 unlock_and_fail:
583 pa_threaded_mainloop_unlock (self->mainloop);
584 gst_pulse_device_provider_stop (provider);
585 return FALSE;
586
587 mainloop_failed:
588 return FALSE;
589 }
590
591 static void
gst_pulse_device_provider_stop(GstDeviceProvider * provider)592 gst_pulse_device_provider_stop (GstDeviceProvider * provider)
593 {
594 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (provider);
595
596 pa_threaded_mainloop_stop (self->mainloop);
597
598 if (self->context) {
599 pa_context_disconnect (self->context);
600
601 /* Make sure we don't get any further callbacks */
602 pa_context_set_state_callback (self->context, NULL, NULL);
603 pa_context_set_subscribe_callback (self->context, NULL, NULL);
604
605 pa_context_unref (self->context);
606 self->context = NULL;
607 }
608
609 pa_threaded_mainloop_free (self->mainloop);
610 self->mainloop = NULL;
611 }
612
613 enum
614 {
615 PROP_INTERNAL_NAME = 1,
616 };
617
618 G_DEFINE_TYPE (GstPulseDevice, gst_pulse_device, GST_TYPE_DEVICE);
619
620 static void gst_pulse_device_get_property (GObject * object, guint prop_id,
621 GValue * value, GParamSpec * pspec);
622 static void gst_pulse_device_set_property (GObject * object, guint prop_id,
623 const GValue * value, GParamSpec * pspec);
624 static void gst_pulse_device_finalize (GObject * object);
625 static GstElement *gst_pulse_device_create_element (GstDevice * device,
626 const gchar * name);
627 static gboolean gst_pulse_device_reconfigure_element (GstDevice * device,
628 GstElement * element);
629
630 static void
gst_pulse_device_class_init(GstPulseDeviceClass * klass)631 gst_pulse_device_class_init (GstPulseDeviceClass * klass)
632 {
633 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
634 GObjectClass *object_class = G_OBJECT_CLASS (klass);
635
636 dev_class->create_element = gst_pulse_device_create_element;
637 dev_class->reconfigure_element = gst_pulse_device_reconfigure_element;
638
639 object_class->get_property = gst_pulse_device_get_property;
640 object_class->set_property = gst_pulse_device_set_property;
641 object_class->finalize = gst_pulse_device_finalize;
642
643 g_object_class_install_property (object_class, PROP_INTERNAL_NAME,
644 g_param_spec_string ("internal-name", "Internal PulseAudio device name",
645 "The internal name of the PulseAudio device", "",
646 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
647 }
648
649 static void
gst_pulse_device_init(GstPulseDevice * device)650 gst_pulse_device_init (GstPulseDevice * device)
651 {
652 }
653
654 static void
gst_pulse_device_finalize(GObject * object)655 gst_pulse_device_finalize (GObject * object)
656 {
657 GstPulseDevice *device = GST_PULSE_DEVICE (object);
658
659 g_free (device->internal_name);
660
661 G_OBJECT_CLASS (gst_pulse_device_parent_class)->finalize (object);
662 }
663
664 static GstElement *
gst_pulse_device_create_element(GstDevice * device,const gchar * name)665 gst_pulse_device_create_element (GstDevice * device, const gchar * name)
666 {
667 GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
668 GstElement *elem;
669
670 elem = gst_element_factory_make (pulse_dev->element, name);
671 g_object_set (elem, "device", pulse_dev->internal_name, NULL);
672
673 return elem;
674 }
675
676 static gboolean
gst_pulse_device_reconfigure_element(GstDevice * device,GstElement * element)677 gst_pulse_device_reconfigure_element (GstDevice * device, GstElement * element)
678 {
679 GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
680
681 if (!strcmp (pulse_dev->element, "pulsesrc")) {
682 if (!GST_IS_PULSESRC (element))
683 return FALSE;
684 } else if (!strcmp (pulse_dev->element, "pulsesink")) {
685 if (!GST_IS_PULSESINK (element))
686 return FALSE;
687 } else {
688 g_assert_not_reached ();
689 }
690
691 g_object_set (element, "device", pulse_dev->internal_name, NULL);
692
693 return TRUE;
694 }
695
696 /* Takes ownership of @caps and @props */
697 static GstDevice *
gst_pulse_device_new(guint device_index,const gchar * device_name,GstCaps * caps,const gchar * internal_name,GstPulseDeviceType type,GstStructure * props,gboolean is_default)698 gst_pulse_device_new (guint device_index, const gchar * device_name,
699 GstCaps * caps, const gchar * internal_name, GstPulseDeviceType type,
700 GstStructure * props, gboolean is_default)
701 {
702 GstPulseDevice *gstdev;
703 const gchar *element = NULL;
704 const gchar *klass = NULL;
705
706 g_return_val_if_fail (device_name, NULL);
707 g_return_val_if_fail (internal_name, NULL);
708 g_return_val_if_fail (caps, NULL);
709
710
711 switch (type) {
712 case GST_PULSE_DEVICE_TYPE_SOURCE:
713 element = "pulsesrc";
714 klass = "Audio/Source";
715 break;
716 case GST_PULSE_DEVICE_TYPE_SINK:
717 element = "pulsesink";
718 klass = "Audio/Sink";
719 break;
720 default:
721 g_assert_not_reached ();
722 break;
723 }
724
725 gst_structure_set (props, "is-default", G_TYPE_BOOLEAN, is_default, NULL);
726 gstdev = g_object_new (GST_TYPE_PULSE_DEVICE,
727 "display-name", device_name, "caps", caps, "device-class", klass,
728 "internal-name", internal_name, "properties", props, NULL);
729
730 gstdev->type = type;
731 gstdev->device_index = device_index;
732 gstdev->element = element;
733 gstdev->is_default = is_default;
734
735 gst_structure_free (props);
736 gst_caps_unref (caps);
737
738 return GST_DEVICE (gstdev);
739 }
740
741
742 static void
gst_pulse_device_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)743 gst_pulse_device_get_property (GObject * object, guint prop_id,
744 GValue * value, GParamSpec * pspec)
745 {
746 GstPulseDevice *device;
747
748 device = GST_PULSE_DEVICE_CAST (object);
749
750 switch (prop_id) {
751 case PROP_INTERNAL_NAME:
752 g_value_set_string (value, device->internal_name);
753 break;
754 default:
755 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
756 break;
757 }
758 }
759
760
761 static void
gst_pulse_device_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)762 gst_pulse_device_set_property (GObject * object, guint prop_id,
763 const GValue * value, GParamSpec * pspec)
764 {
765 GstPulseDevice *device;
766
767 device = GST_PULSE_DEVICE_CAST (object);
768
769 switch (prop_id) {
770 case PROP_INTERNAL_NAME:
771 device->internal_name = g_value_dup_string (value);
772 break;
773 default:
774 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
775 break;
776 }
777 }
778