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 return gst_pulse_device_new (info->index, info->description,
211 caps, info->name, GST_PULSE_DEVICE_TYPE_SOURCE, props,
212 !g_strcmp0 (info->name, self->default_source_name));
213 }
214
215 static GstDevice *
new_sink(GstPulseDeviceProvider * self,const pa_sink_info * info)216 new_sink (GstPulseDeviceProvider * self, const pa_sink_info * info)
217 {
218 GstCaps *caps;
219 GstStructure *props;
220 guint i;
221
222 caps = gst_caps_new_empty ();
223
224 for (i = 0; i < info->n_formats; i++)
225 gst_caps_append (caps, gst_pulse_format_info_to_caps (info->formats[i]));
226
227 props = gst_pulse_make_structure (info->proplist);
228
229 return gst_pulse_device_new (info->index, info->description,
230 caps, info->name, GST_PULSE_DEVICE_TYPE_SINK, props,
231 !g_strcmp0 (info->name, self->default_sink_name));
232 }
233
234 static void
get_source_info_cb(pa_context * context,const pa_source_info * info,int eol,void * userdata)235 get_source_info_cb (pa_context * context,
236 const pa_source_info * info, int eol, void *userdata)
237 {
238 GstPulseDeviceProvider *self = userdata;
239 GstDevice *dev;
240
241 if (eol) {
242 pa_threaded_mainloop_signal (self->mainloop, 0);
243 return;
244 }
245
246 dev = new_source (self, info);
247
248 if (dev)
249 gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), dev);
250 }
251
252 static void
get_server_info_cb(pa_context * context,const pa_server_info * info,void * userdata)253 get_server_info_cb (pa_context * context, const pa_server_info * info,
254 void *userdata)
255 {
256 GList *tmp, *devices = NULL;
257 GstPulseDeviceProvider *self = userdata;
258
259 GST_OBJECT_LOCK (self);
260 g_free (self->default_sink_name);
261 g_free (self->default_source_name);
262 self->default_sink_name = g_strdup (info->default_sink_name);
263 self->default_source_name = g_strdup (info->default_source_name);
264 GST_DEBUG_OBJECT (self, "Default sink name: %s", self->default_sink_name);
265
266 for (tmp = GST_DEVICE_PROVIDER (self)->devices; tmp; tmp = tmp->next)
267 devices = g_list_prepend (devices, gst_object_ref (tmp->data));
268 GST_OBJECT_UNLOCK (self);
269
270 for (tmp = devices; tmp; tmp = tmp->next) {
271 GstPulseDevice *dev = tmp->data;
272 GstStructure *props = gst_device_get_properties (GST_DEVICE (dev));
273 gboolean was_default = FALSE, is_default = FALSE;
274
275 g_assert (props);
276 gst_structure_get_boolean (props, "is-default", &was_default);
277 switch (dev->type) {
278 case GST_PULSE_DEVICE_TYPE_SINK:
279 is_default = !g_strcmp0 (dev->internal_name, self->default_sink_name);
280 break;
281 case GST_PULSE_DEVICE_TYPE_SOURCE:
282 is_default = !g_strcmp0 (dev->internal_name, self->default_source_name);
283 break;
284 }
285
286 if (was_default != is_default) {
287 GstDevice *updated_device;
288 gchar *name = gst_device_get_display_name (GST_DEVICE (dev));
289
290 gst_structure_set (props, "is-default", G_TYPE_BOOLEAN, is_default, NULL);
291 updated_device = gst_pulse_device_new (dev->device_index,
292 name, gst_device_get_caps (GST_DEVICE (dev)), dev->internal_name,
293 dev->type, props, is_default);
294
295 gst_device_provider_device_changed (GST_DEVICE_PROVIDER (self),
296 updated_device, GST_DEVICE (dev));
297
298 g_free (name);
299 } else {
300 gst_structure_free (props);
301 }
302 }
303 g_list_free_full (devices, gst_object_unref);
304
305 pa_threaded_mainloop_signal (self->mainloop, 0);
306 }
307
308 static void
get_sink_info_cb(pa_context * context,const pa_sink_info * info,int eol,void * userdata)309 get_sink_info_cb (pa_context * context,
310 const pa_sink_info * info, int eol, void *userdata)
311 {
312 GstPulseDeviceProvider *self = userdata;
313 GstDevice *dev;
314
315 if (eol) {
316 pa_threaded_mainloop_signal (self->mainloop, 0);
317 return;
318 }
319
320 dev = new_sink (self, info);
321
322 if (dev)
323 gst_device_provider_device_add (GST_DEVICE_PROVIDER (self), dev);
324 }
325
326 static void
context_subscribe_cb(pa_context * context,pa_subscription_event_type_t type,uint32_t idx,void * userdata)327 context_subscribe_cb (pa_context * context, pa_subscription_event_type_t type,
328 uint32_t idx, void *userdata)
329 {
330 GstPulseDeviceProvider *self = userdata;
331 GstDeviceProvider *provider = userdata;
332 pa_subscription_event_type_t facility =
333 type & PA_SUBSCRIPTION_EVENT_FACILITY_MASK;
334 pa_subscription_event_type_t event_type =
335 type & PA_SUBSCRIPTION_EVENT_TYPE_MASK;
336
337 if (facility == PA_SUBSCRIPTION_EVENT_SERVER ||
338 facility != PA_SUBSCRIPTION_EVENT_CHANGE) {
339 pa_context_get_server_info (self->context, get_server_info_cb, self);
340
341 return;
342 }
343
344 if (facility != PA_SUBSCRIPTION_EVENT_SOURCE &&
345 facility != PA_SUBSCRIPTION_EVENT_SINK)
346 return;
347
348 if (event_type == PA_SUBSCRIPTION_EVENT_NEW) {
349 /* Microphone in the source output has changed */
350
351 if (facility == PA_SUBSCRIPTION_EVENT_SOURCE)
352 pa_context_get_source_info_by_index (context, idx, get_source_info_cb,
353 self);
354 else if (facility == PA_SUBSCRIPTION_EVENT_SINK)
355 pa_context_get_sink_info_by_index (context, idx, get_sink_info_cb, self);
356 } else if (event_type == PA_SUBSCRIPTION_EVENT_REMOVE) {
357 GstPulseDevice *dev = NULL;
358 GList *item;
359
360 GST_OBJECT_LOCK (self);
361 for (item = provider->devices; item; item = item->next) {
362 dev = item->data;
363
364 if (((facility == PA_SUBSCRIPTION_EVENT_SOURCE &&
365 dev->type == GST_PULSE_DEVICE_TYPE_SOURCE) ||
366 (facility == PA_SUBSCRIPTION_EVENT_SINK &&
367 dev->type == GST_PULSE_DEVICE_TYPE_SINK)) &&
368 dev->device_index == idx) {
369 gst_object_ref (dev);
370 break;
371 }
372 dev = NULL;
373 }
374 GST_OBJECT_UNLOCK (self);
375
376 if (dev) {
377 gst_device_provider_device_remove (GST_DEVICE_PROVIDER (self),
378 GST_DEVICE (dev));
379 gst_object_unref (dev);
380 }
381 }
382 }
383
384 static void
get_source_info_list_cb(pa_context * context,const pa_source_info * info,int eol,void * userdata)385 get_source_info_list_cb (pa_context * context, const pa_source_info * info,
386 int eol, void *userdata)
387 {
388 ListDevicesData *data = userdata;
389
390 if (eol)
391 return;
392
393 data->devices =
394 g_list_prepend (data->devices,
395 gst_object_ref_sink (new_source (data->self, info)));
396 }
397
398 static void
get_sink_info_list_cb(pa_context * context,const pa_sink_info * info,int eol,void * userdata)399 get_sink_info_list_cb (pa_context * context, const pa_sink_info * info,
400 int eol, void *userdata)
401 {
402 ListDevicesData *data = userdata;
403
404 if (eol)
405 return;
406
407 data->devices =
408 g_list_prepend (data->devices, gst_object_ref_sink (new_sink (data->self,
409 info)));
410 }
411
412 static GList *
gst_pulse_device_provider_probe(GstDeviceProvider * provider)413 gst_pulse_device_provider_probe (GstDeviceProvider * provider)
414 {
415 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (provider);
416 pa_mainloop *m = NULL;
417 pa_context *c = NULL;
418 pa_operation *o;
419 ListDevicesData data = { NULL, self };
420
421 if (!(m = pa_mainloop_new ()))
422 return NULL;
423
424 if (!(c = pa_context_new (pa_mainloop_get_api (m), self->client_name))) {
425 GST_ERROR_OBJECT (self, "Failed to create context");
426 goto failed;
427 }
428
429 if (pa_context_connect (c, self->server, 0, NULL) < 0) {
430 GST_ERROR_OBJECT (self, "Failed to connect: %s",
431 pa_strerror (pa_context_errno (self->context)));
432 goto failed;
433 }
434
435 for (;;) {
436 pa_context_state_t state;
437
438 state = pa_context_get_state (c);
439
440 if (!PA_CONTEXT_IS_GOOD (state)) {
441 GST_ELEMENT_ERROR (self, RESOURCE, FAILED, ("Failed to connect: %s",
442 pa_strerror (pa_context_errno (c))), (NULL));
443 goto failed;
444 }
445
446 if (state == PA_CONTEXT_READY)
447 break;
448
449 /* Wait until the context is ready */
450 if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
451 goto failed;
452
453 }
454 GST_DEBUG_OBJECT (self, "connected");
455
456 o = pa_context_get_sink_info_list (c, get_sink_info_list_cb, &data);
457 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
458 pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
459 if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
460 break;
461 }
462 pa_operation_unref (o);
463
464 o = pa_context_get_source_info_list (c, get_source_info_list_cb, &data);
465 while (pa_operation_get_state (o) == PA_OPERATION_RUNNING &&
466 pa_operation_get_state (o) == PA_OPERATION_RUNNING) {
467 if (pa_mainloop_iterate (m, TRUE, NULL) < 0)
468 break;
469 }
470 pa_operation_unref (o);
471
472 pa_context_disconnect (c);
473 pa_mainloop_free (m);
474
475 return data.devices;
476
477 failed:
478
479 return NULL;
480 }
481
482 static gboolean
run_pulse_operation(GstPulseDeviceProvider * self,pa_operation * operation)483 run_pulse_operation (GstPulseDeviceProvider * self, pa_operation * operation)
484 {
485 if (!operation)
486 return FALSE;
487
488 while (pa_operation_get_state (operation) == PA_OPERATION_RUNNING) {
489 if (!PA_CONTEXT_IS_GOOD (pa_context_get_state ((self->context)))) {
490 pa_operation_cancel (operation);
491 pa_operation_unref (operation);
492 return FALSE;
493 }
494
495 pa_threaded_mainloop_wait (self->mainloop);
496 }
497
498 pa_operation_unref (operation);
499
500 return TRUE;
501 }
502
503 static gboolean
gst_pulse_device_provider_start(GstDeviceProvider * provider)504 gst_pulse_device_provider_start (GstDeviceProvider * provider)
505 {
506 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (provider);
507
508 if (!(self->mainloop = pa_threaded_mainloop_new ())) {
509 GST_ERROR_OBJECT (self, "Could not create pulseaudio mainloop");
510 goto mainloop_failed;
511 }
512 if (pa_threaded_mainloop_start (self->mainloop) < 0) {
513 GST_ERROR_OBJECT (self, "Could not start pulseaudio mainloop");
514 pa_threaded_mainloop_free (self->mainloop);
515 self->mainloop = NULL;
516 goto mainloop_failed;
517 }
518
519 pa_threaded_mainloop_lock (self->mainloop);
520
521 if (!(self->context =
522 pa_context_new (pa_threaded_mainloop_get_api (self->mainloop),
523 self->client_name))) {
524 GST_ERROR_OBJECT (self, "Failed to create context");
525 goto unlock_and_fail;
526 }
527
528 pa_context_set_state_callback (self->context, context_state_cb, self);
529 pa_context_set_subscribe_callback (self->context, context_subscribe_cb, self);
530
531
532 GST_DEBUG_OBJECT (self, "connect to server %s", GST_STR_NULL (self->server));
533
534 if (pa_context_connect (self->context, self->server, 0, NULL) < 0) {
535 GST_ERROR_OBJECT (self, "Failed to connect: %s",
536 pa_strerror (pa_context_errno (self->context)));
537 goto unlock_and_fail;
538 }
539
540 for (;;) {
541 pa_context_state_t state;
542
543 state = pa_context_get_state (self->context);
544
545 if (!PA_CONTEXT_IS_GOOD (state)) {
546 GST_ERROR_OBJECT (self, "Failed to connect: %s",
547 pa_strerror (pa_context_errno (self->context)));
548 goto unlock_and_fail;
549 }
550
551 if (state == PA_CONTEXT_READY)
552 break;
553
554 /* Wait until the context is ready */
555 pa_threaded_mainloop_wait (self->mainloop);
556 }
557 GST_DEBUG_OBJECT (self, "connected");
558
559 pa_context_subscribe (self->context,
560 PA_SUBSCRIPTION_MASK_SOURCE | PA_SUBSCRIPTION_MASK_SINK |
561 PA_SUBSCRIPTION_EVENT_SERVER | PA_SUBSCRIPTION_EVENT_CHANGE, NULL, NULL);
562
563 if (!run_pulse_operation (self, pa_context_get_server_info (self->context,
564 get_server_info_cb, self)))
565 goto unlock_and_fail;
566
567 if (!run_pulse_operation (self,
568 pa_context_get_source_info_list (self->context, get_source_info_cb,
569 self)))
570 goto unlock_and_fail;
571
572 if (!run_pulse_operation (self, pa_context_get_sink_info_list (self->context,
573 get_sink_info_cb, self)))
574 goto unlock_and_fail;
575
576 pa_threaded_mainloop_unlock (self->mainloop);
577
578 return TRUE;
579
580 unlock_and_fail:
581 pa_threaded_mainloop_unlock (self->mainloop);
582 gst_pulse_device_provider_stop (provider);
583 return FALSE;
584
585 mainloop_failed:
586 return FALSE;
587 }
588
589 static void
gst_pulse_device_provider_stop(GstDeviceProvider * provider)590 gst_pulse_device_provider_stop (GstDeviceProvider * provider)
591 {
592 GstPulseDeviceProvider *self = GST_PULSE_DEVICE_PROVIDER (provider);
593
594 pa_threaded_mainloop_stop (self->mainloop);
595
596 if (self->context) {
597 pa_context_disconnect (self->context);
598
599 /* Make sure we don't get any further callbacks */
600 pa_context_set_state_callback (self->context, NULL, NULL);
601 pa_context_set_subscribe_callback (self->context, NULL, NULL);
602
603 pa_context_unref (self->context);
604 self->context = NULL;
605 }
606
607 pa_threaded_mainloop_free (self->mainloop);
608 self->mainloop = NULL;
609 }
610
611 enum
612 {
613 PROP_INTERNAL_NAME = 1,
614 };
615
616 G_DEFINE_TYPE (GstPulseDevice, gst_pulse_device, GST_TYPE_DEVICE);
617
618 static void gst_pulse_device_get_property (GObject * object, guint prop_id,
619 GValue * value, GParamSpec * pspec);
620 static void gst_pulse_device_set_property (GObject * object, guint prop_id,
621 const GValue * value, GParamSpec * pspec);
622 static void gst_pulse_device_finalize (GObject * object);
623 static GstElement *gst_pulse_device_create_element (GstDevice * device,
624 const gchar * name);
625 static gboolean gst_pulse_device_reconfigure_element (GstDevice * device,
626 GstElement * element);
627
628 static void
gst_pulse_device_class_init(GstPulseDeviceClass * klass)629 gst_pulse_device_class_init (GstPulseDeviceClass * klass)
630 {
631 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
632 GObjectClass *object_class = G_OBJECT_CLASS (klass);
633
634 dev_class->create_element = gst_pulse_device_create_element;
635 dev_class->reconfigure_element = gst_pulse_device_reconfigure_element;
636
637 object_class->get_property = gst_pulse_device_get_property;
638 object_class->set_property = gst_pulse_device_set_property;
639 object_class->finalize = gst_pulse_device_finalize;
640
641 g_object_class_install_property (object_class, PROP_INTERNAL_NAME,
642 g_param_spec_string ("internal-name", "Internal PulseAudio device name",
643 "The internal name of the PulseAudio device", "",
644 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
645 }
646
647 static void
gst_pulse_device_init(GstPulseDevice * device)648 gst_pulse_device_init (GstPulseDevice * device)
649 {
650 }
651
652 static void
gst_pulse_device_finalize(GObject * object)653 gst_pulse_device_finalize (GObject * object)
654 {
655 GstPulseDevice *device = GST_PULSE_DEVICE (object);
656
657 g_free (device->internal_name);
658
659 G_OBJECT_CLASS (gst_pulse_device_parent_class)->finalize (object);
660 }
661
662 static GstElement *
gst_pulse_device_create_element(GstDevice * device,const gchar * name)663 gst_pulse_device_create_element (GstDevice * device, const gchar * name)
664 {
665 GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
666 GstElement *elem;
667
668 elem = gst_element_factory_make (pulse_dev->element, name);
669 g_object_set (elem, "device", pulse_dev->internal_name, NULL);
670
671 return elem;
672 }
673
674 static gboolean
gst_pulse_device_reconfigure_element(GstDevice * device,GstElement * element)675 gst_pulse_device_reconfigure_element (GstDevice * device, GstElement * element)
676 {
677 GstPulseDevice *pulse_dev = GST_PULSE_DEVICE (device);
678
679 if (!strcmp (pulse_dev->element, "pulsesrc")) {
680 if (!GST_IS_PULSESRC (element))
681 return FALSE;
682 } else if (!strcmp (pulse_dev->element, "pulsesink")) {
683 if (!GST_IS_PULSESINK (element))
684 return FALSE;
685 } else {
686 g_assert_not_reached ();
687 }
688
689 g_object_set (element, "device", pulse_dev->internal_name, NULL);
690
691 return TRUE;
692 }
693
694 /* Takes ownership of @caps and @props */
695 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)696 gst_pulse_device_new (guint device_index, const gchar * device_name,
697 GstCaps * caps, const gchar * internal_name, GstPulseDeviceType type,
698 GstStructure * props, gboolean is_default)
699 {
700 GstPulseDevice *gstdev;
701 const gchar *element = NULL;
702 const gchar *klass = NULL;
703
704 g_return_val_if_fail (device_name, NULL);
705 g_return_val_if_fail (internal_name, NULL);
706 g_return_val_if_fail (caps, NULL);
707
708
709 switch (type) {
710 case GST_PULSE_DEVICE_TYPE_SOURCE:
711 element = "pulsesrc";
712 klass = "Audio/Source";
713 break;
714 case GST_PULSE_DEVICE_TYPE_SINK:
715 element = "pulsesink";
716 klass = "Audio/Sink";
717 break;
718 default:
719 g_assert_not_reached ();
720 break;
721 }
722
723 gst_structure_set (props, "is-default", G_TYPE_BOOLEAN, is_default, NULL);
724 gstdev = g_object_new (GST_TYPE_PULSE_DEVICE,
725 "display-name", device_name, "caps", caps, "device-class", klass,
726 "internal-name", internal_name, "properties", props, NULL);
727
728 gstdev->type = type;
729 gstdev->device_index = device_index;
730 gstdev->element = element;
731 gstdev->is_default = is_default;
732
733 gst_structure_free (props);
734 gst_caps_unref (caps);
735
736 return GST_DEVICE (gstdev);
737 }
738
739
740 static void
gst_pulse_device_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)741 gst_pulse_device_get_property (GObject * object, guint prop_id,
742 GValue * value, GParamSpec * pspec)
743 {
744 GstPulseDevice *device;
745
746 device = GST_PULSE_DEVICE_CAST (object);
747
748 switch (prop_id) {
749 case PROP_INTERNAL_NAME:
750 g_value_set_string (value, device->internal_name);
751 break;
752 default:
753 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
754 break;
755 }
756 }
757
758
759 static void
gst_pulse_device_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)760 gst_pulse_device_set_property (GObject * object, guint prop_id,
761 const GValue * value, GParamSpec * pspec)
762 {
763 GstPulseDevice *device;
764
765 device = GST_PULSE_DEVICE_CAST (object);
766
767 switch (prop_id) {
768 case PROP_INTERNAL_NAME:
769 device->internal_name = g_value_dup_string (value);
770 break;
771 default:
772 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
773 break;
774 }
775 }
776