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