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