devicemonitor: do start and stop outside of the lock
[platform/upstream/gstreamer.git] / gst / gstdevicemonitor.c
1 /* GStreamer
2  * Copyright (C) 2013 Olivier Crete <olivier.crete@collabora.com>
3  *
4  * gstdevicemonitor.c: device monitor
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 /**
23  * SECTION:gstdevicemonitor
24  * @short_description: A device monitor and prober
25  * @see_also: #GstDevice, #GstDeviceProvider
26  *
27  * Applications should create a #GstDeviceMonitor when they want
28  * to probe, list and monitor devices of a specific type. The
29  * #GstDeviceMonitor will create the appropriate
30  * #GstDeviceProvider objects and manage them. It will then post
31  * messages on its #GstBus for devices that have been added and
32  * removed.
33  *
34  * The device monitor will monitor all devices matching the filters that
35  * the application has set.
36  *
37  *
38  * The basic use pattern of a device monitor is as follows:
39  * |[
40  *   static gboolean
41  *   my_bus_func (GstBus * bus, GstMessage * message, gpointer user_data)
42  *   {
43  *      GstDevice *device;
44  *      gchar *name;
45  *
46  *      switch (GST_MESSAGE_TYPE (message)) {
47  *        case GST_MESSAGE_DEVICE_ADDED:
48  *          gst_message_parse_device_added (message, &device);
49  *          name = gst_device_get_display_name (device);
50  *          g_print("Device added: %s\n", name);
51  *          g_free (name);
52  *          break;
53  *        case GST_MESSAGE_DEVICE_REMOVED:
54  *          gst_message_parse_device_removed (message, &device);
55  *          name = gst_device_get_display_name (device);
56  *          g_print("Device removed: %s\n", name);
57  *          g_free (name);
58  *          break;
59  *        default:
60  *          break;
61  *      }
62  *
63  *      return G_SOURCE_CONTINUE;
64  *   }
65  *
66  *   GstDeviceMonitor *
67  *   setup_raw_video_source_device_monitor (void) {
68  *      GstDeviceMonitor *monitor;
69  *      GstBus *bus;
70  *      GstCaps *caps;
71  *
72  *      monitor = gst_device_monitor_new ();
73  *
74  *      bus = gst_device_monitor_get_bus (monitor);
75  *      gst_bus_add_watch (bus, my_bus_func, NULL);
76  *      gst_object_unref (bus);
77  *
78  *      caps = gst_caps_new_empty_simple ("video/x-raw");
79  *      gst_device_monitor_add_filter (monitor, "Video/Source", caps);
80  *      gst_caps_unref (caps);
81  *
82  *      gst_device_monitor_start (monitor);
83  *
84  *      return monitor;
85  *   }
86  * ]|
87  *
88  * Since: 1.4
89  */
90
91
92 #ifdef HAVE_CONFIG_H
93 #include "config.h"
94 #endif
95
96 #include "gst_private.h"
97 #include "gstdevicemonitor.h"
98
99 struct _GstDeviceMonitorPrivate
100 {
101   gboolean started;
102
103   GstBus *bus;
104
105   GPtrArray *providers;
106   guint cookie;
107
108   GPtrArray *filters;
109
110   guint last_id;
111 };
112
113
114 G_DEFINE_TYPE (GstDeviceMonitor, gst_device_monitor, GST_TYPE_OBJECT);
115
116 static void gst_device_monitor_dispose (GObject * object);
117
118 struct DeviceFilter
119 {
120   guint id;
121
122   gchar **classesv;
123   GstCaps *caps;
124 };
125
126 static void
127 device_filter_free (struct DeviceFilter *filter)
128 {
129   g_strfreev (filter->classesv);
130   gst_caps_unref (filter->caps);
131
132   g_slice_free (struct DeviceFilter, filter);
133 }
134
135 static void
136 gst_device_monitor_class_init (GstDeviceMonitorClass * klass)
137 {
138   GObjectClass *object_class = G_OBJECT_CLASS (klass);
139
140   g_type_class_add_private (klass, sizeof (GstDeviceMonitorPrivate));
141
142   object_class->dispose = gst_device_monitor_dispose;
143 }
144
145 static void
146 bus_sync_message (GstBus * bus, GstMessage * message,
147     GstDeviceMonitor * monitor)
148 {
149   GstMessageType type = GST_MESSAGE_TYPE (message);
150
151   if (type == GST_MESSAGE_DEVICE_ADDED || type == GST_MESSAGE_DEVICE_REMOVED) {
152     gboolean matches;
153     GstDevice *device;
154
155     if (type == GST_MESSAGE_DEVICE_ADDED)
156       gst_message_parse_device_added (message, &device);
157     else
158       gst_message_parse_device_removed (message, &device);
159
160     GST_OBJECT_LOCK (monitor);
161     if (monitor->priv->filters->len) {
162       guint i;
163
164       for (i = 0; i < monitor->priv->filters->len; i++) {
165         struct DeviceFilter *filter =
166             g_ptr_array_index (monitor->priv->filters, i);
167         GstCaps *caps;
168
169         caps = gst_device_get_caps (device);
170         matches = gst_caps_can_intersect (filter->caps, caps) &&
171             gst_device_has_classesv (device, filter->classesv);
172         gst_caps_unref (caps);
173         if (matches)
174           break;
175       }
176     } else {
177       matches = TRUE;
178     }
179     GST_OBJECT_UNLOCK (monitor);
180
181     gst_object_unref (device);
182
183     if (matches)
184       gst_bus_post (monitor->priv->bus, gst_message_ref (message));
185   }
186 }
187
188
189 static void
190 gst_device_monitor_init (GstDeviceMonitor * self)
191 {
192   self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
193       GST_TYPE_DEVICE_MONITOR, GstDeviceMonitorPrivate);
194
195   self->priv->bus = gst_bus_new ();
196   gst_bus_set_flushing (self->priv->bus, TRUE);
197
198   self->priv->providers = g_ptr_array_new ();
199   self->priv->filters = g_ptr_array_new_with_free_func (
200       (GDestroyNotify) device_filter_free);
201
202   self->priv->last_id = 1;
203 }
204
205
206 static void
207 gst_device_monitor_remove (GstDeviceMonitor * self, guint i)
208 {
209   GstDeviceProvider *provider = g_ptr_array_index (self->priv->providers, i);
210   GstBus *bus;
211
212   g_ptr_array_remove_index (self->priv->providers, i);
213
214   bus = gst_device_provider_get_bus (provider);
215   g_signal_handlers_disconnect_by_func (bus, bus_sync_message, self);
216   gst_object_unref (bus);
217
218   gst_object_unref (provider);
219 }
220
221 static void
222 gst_device_monitor_dispose (GObject * object)
223 {
224   GstDeviceMonitor *self = GST_DEVICE_MONITOR (object);
225
226   g_return_if_fail (!self->priv->started);
227
228   if (self->priv->providers) {
229     while (self->priv->providers->len)
230       gst_device_monitor_remove (self, self->priv->providers->len - 1);
231     g_ptr_array_unref (self->priv->providers);
232     self->priv->providers = NULL;
233   }
234
235   if (self->priv->filters) {
236     g_ptr_array_unref (self->priv->filters);
237     self->priv->filters = NULL;
238   }
239
240   gst_object_replace ((GstObject **) & self->priv->bus, NULL);
241
242   G_OBJECT_CLASS (gst_device_monitor_parent_class)->dispose (object);
243 }
244
245 /**
246  * gst_device_monitor_get_devices:
247  * @monitor: A #GstDeviceProvider
248  *
249  * Gets a list of devices from all of the relevant monitors. This may actually
250  * probe the hardware if the monitor is not currently started.
251  *
252  * Returns: (transfer full) (element-type GstDevice): a #GList of
253  *   #GstDevice
254  *
255  * Since: 1.4
256  */
257
258 GList *
259 gst_device_monitor_get_devices (GstDeviceMonitor * monitor)
260 {
261   GList *devices = NULL;
262   guint i;
263   guint cookie;
264
265   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
266
267   GST_OBJECT_LOCK (monitor);
268
269   if (monitor->priv->filters->len == 0) {
270     GST_OBJECT_UNLOCK (monitor);
271     GST_WARNING_OBJECT (monitor, "No filters have been set");
272     return FALSE;
273   }
274
275   if (monitor->priv->providers->len == 0) {
276     GST_OBJECT_UNLOCK (monitor);
277     GST_WARNING_OBJECT (monitor, "No providers match the current filters");
278     return FALSE;
279   }
280
281 again:
282
283   g_list_free_full (devices, gst_object_unref);
284   devices = NULL;
285
286   cookie = monitor->priv->cookie;
287
288   for (i = 0; i < monitor->priv->providers->len; i++) {
289     GList *tmpdev;
290     GstDeviceProvider *provider =
291         gst_object_ref (g_ptr_array_index (monitor->priv->providers, i));
292     GList *item;
293
294     GST_OBJECT_UNLOCK (monitor);
295
296     tmpdev = gst_device_provider_get_devices (provider);
297
298     GST_OBJECT_LOCK (monitor);
299
300     for (item = tmpdev; item; item = item->next) {
301       GstDevice *dev = GST_DEVICE (item->data);
302       GstCaps *caps = gst_device_get_caps (dev);
303       guint j;
304
305       for (j = 0; j < monitor->priv->filters->len; j++) {
306         struct DeviceFilter *filter =
307             g_ptr_array_index (monitor->priv->filters, j);
308         if (gst_caps_can_intersect (filter->caps, caps) &&
309             gst_device_has_classesv (dev, filter->classesv)) {
310           devices = g_list_prepend (devices, gst_object_ref (dev));
311           break;
312         }
313       }
314       gst_caps_unref (caps);
315     }
316
317     g_list_free_full (tmpdev, gst_object_unref);
318     gst_object_unref (provider);
319
320
321     if (monitor->priv->cookie != cookie)
322       goto again;
323   }
324
325   GST_OBJECT_UNLOCK (monitor);
326
327   return g_list_reverse (devices);
328 }
329
330 /**
331  * gst_device_monitor_start:
332  * @monitor: A #GstDeviceMonitor
333  *
334  * Starts monitoring the devices, one this has succeeded, the
335  * %GST_MESSAGE_DEVICE_ADDED and %GST_MESSAGE_DEVICE_REMOVED messages
336  * will be emitted on the bus when the list of devices changes.
337  *
338  * Returns: %TRUE if the device monitoring could be started
339  *
340  * Since: 1.4
341  */
342
343 gboolean
344 gst_device_monitor_start (GstDeviceMonitor * monitor)
345 {
346   guint cookie, i;
347   GList *pending = NULL, *started = NULL, *removed = NULL;
348
349   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
350
351   GST_OBJECT_LOCK (monitor);
352
353   if (monitor->priv->filters->len == 0) {
354     GST_OBJECT_UNLOCK (monitor);
355     GST_WARNING_OBJECT (monitor, "No filters have been set, will expose all "
356         "devices found");
357     gst_device_monitor_add_filter (monitor, NULL, NULL);
358     GST_OBJECT_LOCK (monitor);
359   }
360
361   if (monitor->priv->providers->len == 0) {
362     GST_OBJECT_UNLOCK (monitor);
363     GST_WARNING_OBJECT (monitor, "No providers match the current filters");
364     return FALSE;
365   }
366
367   gst_bus_set_flushing (monitor->priv->bus, FALSE);
368
369 again:
370   cookie = monitor->priv->cookie;
371
372   g_list_free_full (pending, gst_object_unref);
373   pending = NULL;
374   removed = started;
375   started = NULL;
376
377   for (i = 0; i < monitor->priv->providers->len; i++) {
378     GstDeviceProvider *provider;
379     GList *find;
380
381     provider = g_ptr_array_index (monitor->priv->providers, i);
382
383     find = g_list_find (removed, provider);
384     if (find) {
385       /* this was already started, move to started list */
386       removed = g_list_remove_link (removed, find);
387       started = g_list_concat (started, find);
388     } else {
389       /* not started, add to pending list */
390       pending = g_list_append (pending, gst_object_ref (provider));
391     }
392   }
393   g_list_free_full (removed, gst_object_unref);
394   removed = NULL;
395
396   while (pending) {
397     GstDeviceProvider *provider = pending->data;
398
399     if (gst_device_provider_can_monitor (provider)) {
400       GST_OBJECT_UNLOCK (monitor);
401
402       if (!gst_device_provider_start (provider))
403         goto start_failed;
404
405       GST_OBJECT_LOCK (monitor);
406     }
407     started = g_list_prepend (started, provider);
408     pending = g_list_delete_link (pending, pending);
409
410     if (monitor->priv->cookie != cookie)
411       goto again;
412   }
413   monitor->priv->started = TRUE;
414   GST_OBJECT_UNLOCK (monitor);
415
416   g_list_free_full (started, gst_object_unref);
417
418   return TRUE;
419
420 start_failed:
421   {
422     GST_OBJECT_LOCK (monitor);
423     gst_bus_set_flushing (monitor->priv->bus, TRUE);
424     GST_OBJECT_UNLOCK (monitor);
425
426     while (started) {
427       GstDeviceProvider *provider = started->data;
428
429       gst_device_provider_stop (provider);
430       gst_object_unref (provider);
431
432       started = g_list_delete_link (started, started);
433     }
434     return FALSE;
435   }
436 }
437
438 /**
439  * gst_device_monitor_stop:
440  * @monitor: A #GstDeviceProvider
441  *
442  * Stops monitoring the devices.
443  *
444  * Since: 1.4
445  */
446 void
447 gst_device_monitor_stop (GstDeviceMonitor * monitor)
448 {
449   guint i;
450   GList *started = NULL;
451
452   g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
453
454   gst_bus_set_flushing (monitor->priv->bus, TRUE);
455
456   GST_OBJECT_LOCK (monitor);
457   for (i = 0; i < monitor->priv->providers->len; i++) {
458     GstDeviceProvider *provider =
459         g_ptr_array_index (monitor->priv->providers, i);
460
461     started = g_list_prepend (started, gst_object_ref (provider));
462   }
463   GST_OBJECT_UNLOCK (monitor);
464
465   while (started) {
466     GstDeviceProvider *provider = started->data;
467
468     if (gst_device_provider_can_monitor (provider))
469       gst_device_provider_stop (provider);
470
471     started = g_list_delete_link (started, started);
472     gst_object_unref (provider);
473   }
474
475   GST_OBJECT_LOCK (monitor);
476   monitor->priv->started = FALSE;
477   GST_OBJECT_UNLOCK (monitor);
478
479 }
480
481 /**
482  * gst_device_monitor_add_filter:
483  * @monitor: a device monitor
484  * @classes: (allow-none): device classes to use as filter or %NULL for any class
485  * @caps: (allow-none): the #GstCaps to filter or %NULL for ANY
486  *
487  * Adds a filter for which #GstDevice will be monitored, any device that matches
488  * all classes and the #GstCaps will be returned.
489  *
490  * Filters must be added before the #GstDeviceMonitor is started.
491  *
492  * Returns: The id of the new filter or 0 if no provider matched the filter's
493  *  classes.
494  *
495  * Since: 1.4
496  */
497 guint
498 gst_device_monitor_add_filter (GstDeviceMonitor * monitor,
499     const gchar * classes, GstCaps * caps)
500 {
501   GList *factories = NULL;
502   struct DeviceFilter *filter;
503   guint id = 0;
504   gboolean matched = FALSE;
505
506   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
507   g_return_val_if_fail (!monitor->priv->started, 0);
508
509   GST_OBJECT_LOCK (monitor);
510
511   filter = g_slice_new0 (struct DeviceFilter);
512   filter->id = monitor->priv->last_id++;
513   if (caps)
514     filter->caps = gst_caps_ref (caps);
515   else
516     filter->caps = gst_caps_new_any ();
517   if (classes)
518     filter->classesv = g_strsplit (classes, "/", 0);
519
520   factories = gst_device_provider_factory_list_get_device_providers (1);
521
522   while (factories) {
523     GstDeviceProviderFactory *factory = factories->data;
524
525     if (gst_device_provider_factory_has_classesv (factory, filter->classesv)) {
526       GstDeviceProvider *provider;
527
528       provider = gst_device_provider_factory_get (factory);
529
530       if (provider) {
531         guint i;
532
533         for (i = 0; i < monitor->priv->providers->len; i++) {
534           if (g_ptr_array_index (monitor->priv->providers, i) == provider) {
535             gst_object_unref (provider);
536             provider = NULL;
537             matched = TRUE;
538             break;
539           }
540         }
541       }
542
543       if (provider) {
544         GstBus *bus = gst_device_provider_get_bus (provider);
545
546         matched = TRUE;
547         gst_bus_enable_sync_message_emission (bus);
548         g_signal_connect (bus, "sync-message",
549             G_CALLBACK (bus_sync_message), monitor);
550         gst_object_unref (bus);
551         g_ptr_array_add (monitor->priv->providers, provider);
552         monitor->priv->cookie++;
553       }
554     }
555
556     factories = g_list_remove (factories, factory);
557     gst_object_unref (factory);
558   }
559
560   /* Ensure there is no leak here */
561   g_assert (factories == NULL);
562
563   if (matched) {
564     id = filter->id;
565     g_ptr_array_add (monitor->priv->filters, filter);
566   } else {
567     device_filter_free (filter);
568   }
569
570   GST_OBJECT_UNLOCK (monitor);
571
572   return id;
573 }
574
575 /**
576  * gst_device_monitor_remove_filter:
577  * @monitor: a device monitor
578  * @filter_id: the id of the filter
579  *
580  * Removes a filter from the #GstDeviceMonitor using the id that was returned
581  * by gst_device_monitor_add_filter().
582  *
583  * Returns: %TRUE of the filter id was valid, %FALSE otherwise
584  *
585  * Since: 1.4
586  */
587 gboolean
588 gst_device_monitor_remove_filter (GstDeviceMonitor * monitor, guint filter_id)
589 {
590   guint i, j;
591   gboolean removed = FALSE;
592
593   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
594   g_return_val_if_fail (!monitor->priv->started, FALSE);
595   g_return_val_if_fail (filter_id > 0, FALSE);
596
597   GST_OBJECT_LOCK (monitor);
598   for (i = 0; i < monitor->priv->filters->len; i++) {
599     struct DeviceFilter *filter = g_ptr_array_index (monitor->priv->filters, i);
600
601     if (filter->id == filter_id) {
602       g_ptr_array_remove_index (monitor->priv->filters, i);
603       removed = TRUE;
604       break;
605     }
606   }
607
608   if (removed) {
609     for (i = 0; i < monitor->priv->providers->len; i++) {
610       GstDeviceProvider *provider =
611           g_ptr_array_index (monitor->priv->providers, i);
612       GstDeviceProviderFactory *factory =
613           gst_device_provider_get_factory (provider);
614       gboolean valid = FALSE;
615
616       for (j = 0; j < monitor->priv->filters->len; j++) {
617         struct DeviceFilter *filter =
618             g_ptr_array_index (monitor->priv->filters, j);
619
620         if (gst_device_provider_factory_has_classesv (factory,
621                 filter->classesv)) {
622           valid = TRUE;
623           break;
624         }
625       }
626
627       if (!valid) {
628         monitor->priv->cookie++;
629         gst_device_monitor_remove (monitor, i);
630         i--;
631       }
632     }
633   }
634
635   GST_OBJECT_UNLOCK (monitor);
636
637   return removed;
638 }
639
640
641
642 /**
643  * gst_device_monitor_new:
644  *
645  * Create a new #GstDeviceMonitor
646  *
647  * Returns: (transfer full): a new device monitor.
648  *
649  * Since: 1.4
650  */
651 GstDeviceMonitor *
652 gst_device_monitor_new (void)
653 {
654   return g_object_new (GST_TYPE_DEVICE_MONITOR, NULL);
655 }
656
657 /**
658  * gst_device_monitor_get_bus:
659  * @monitor: a #GstDeviceProvider
660  *
661  * Gets the #GstBus of this #GstDeviceMonitor
662  *
663  * Returns: (transfer full): a #GstBus
664  *
665  * Since: 1.4
666  */
667 GstBus *
668 gst_device_monitor_get_bus (GstDeviceMonitor * monitor)
669 {
670   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
671
672   return gst_object_ref (monitor->priv->bus);
673 }