• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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