devicemonitor: Stop using g_clear_pointer()
[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_simple_empty ("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_fast (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 == FALSE);
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 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 i;
347
348   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
349
350   GST_OBJECT_LOCK (monitor);
351
352   if (monitor->priv->filters->len == 0) {
353     GST_OBJECT_UNLOCK (monitor);
354     GST_WARNING_OBJECT (monitor, "No filters have been set, will expose all "
355         "devices found");
356     gst_device_monitor_add_filter (monitor, NULL, NULL);
357     GST_OBJECT_LOCK (monitor);
358   }
359
360   if (monitor->priv->providers->len == 0) {
361     GST_OBJECT_UNLOCK (monitor);
362     GST_WARNING_OBJECT (monitor, "No providers match the current filters");
363     return FALSE;
364   }
365
366   gst_bus_set_flushing (monitor->priv->bus, FALSE);
367
368   for (i = 0; i < monitor->priv->providers->len; i++) {
369     GstDeviceProvider *provider =
370         g_ptr_array_index (monitor->priv->providers, i);
371
372     if (gst_device_provider_can_monitor (provider)) {
373       if (!gst_device_provider_start (provider)) {
374         gst_bus_set_flushing (monitor->priv->bus, TRUE);
375
376         for (; i != 0; i--)
377           gst_device_provider_stop (g_ptr_array_index (monitor->priv->providers,
378                   i - 1));
379
380         GST_OBJECT_UNLOCK (monitor);
381         return FALSE;
382       }
383     }
384   }
385
386   monitor->priv->started = TRUE;
387   GST_OBJECT_UNLOCK (monitor);
388
389   return TRUE;
390 }
391
392 /**
393  * gst_device_monitor_stop:
394  * @monitor: A #GstDeviceProvider
395  *
396  * Stops monitoring the devices.
397  *
398  * Since: 1.4
399  */
400 void
401 gst_device_monitor_stop (GstDeviceMonitor * monitor)
402 {
403   guint i;
404
405   g_return_if_fail (GST_IS_DEVICE_MONITOR (monitor));
406
407   gst_bus_set_flushing (monitor->priv->bus, TRUE);
408
409   GST_OBJECT_LOCK (monitor);
410   for (i = 0; i < monitor->priv->providers->len; i++) {
411     GstDeviceProvider *provider =
412         g_ptr_array_index (monitor->priv->providers, i);
413
414     if (gst_device_provider_can_monitor (provider))
415       gst_device_provider_stop (provider);
416   }
417   monitor->priv->started = FALSE;
418   GST_OBJECT_UNLOCK (monitor);
419
420 }
421
422 /**
423  * gst_device_monitor_add_filter:
424  * @monitor: a device monitor
425  * @classes: device classes to use as filter or %NULL for any class
426  * @caps: (allow-none): the #GstCaps to filter or %NULL for ANY
427  *
428  * Adds a filter for which #GstDevice will be monitored, any device that matches
429  * all classes and the #GstCaps will be returned.
430  *
431  * Filters must be added before the #GstDeviceMonitor is started.
432  *
433  * Returns: The id of the new filter or %0 if no provider matched the filter's
434  *  classes.
435  *
436  * Since: 1.4
437  */
438 guint
439 gst_device_monitor_add_filter (GstDeviceMonitor * monitor,
440     const gchar * classes, GstCaps * caps)
441 {
442   GList *factories = NULL;
443   struct DeviceFilter *filter;
444   guint id = 0;
445   gboolean matched = FALSE;
446
447   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), 0);
448   g_return_val_if_fail (!monitor->priv->started, 0);
449
450   GST_OBJECT_LOCK (monitor);
451
452   filter = g_slice_new0 (struct DeviceFilter);
453   filter->id = monitor->priv->last_id++;
454   if (caps)
455     filter->caps = gst_caps_ref (caps);
456   else
457     filter->caps = gst_caps_new_any ();
458   if (classes)
459     filter->classesv = g_strsplit (classes, "/", 0);
460
461   factories = gst_device_provider_factory_list_get_device_providers (1);
462
463   while (factories) {
464     GstDeviceProviderFactory *factory = factories->data;
465
466
467     if (gst_device_provider_factory_has_classesv (factory, filter->classesv)) {
468       GstDeviceProvider *provider;
469
470       provider = gst_device_provider_factory_get (factory);
471
472       if (provider) {
473         guint i;
474
475         for (i = 0; i < monitor->priv->providers->len; i++) {
476           if (g_ptr_array_index (monitor->priv->providers, i) == provider) {
477             gst_object_unref (provider);
478             provider = NULL;
479             matched = TRUE;
480             break;
481           }
482         }
483       }
484
485       if (provider) {
486         GstBus *bus = gst_device_provider_get_bus (provider);
487
488         matched = TRUE;
489         gst_bus_enable_sync_message_emission (bus);
490         g_signal_connect (bus, "sync-message",
491             G_CALLBACK (bus_sync_message), monitor);
492         gst_object_unref (bus);
493         g_ptr_array_add (monitor->priv->providers, provider);
494         monitor->priv->cookie++;
495       }
496     }
497
498     factories = g_list_remove (factories, factory);
499     gst_object_unref (factory);
500   }
501
502   /* Ensure there is no leak here */
503   g_assert (factories == NULL);
504
505   if (matched) {
506     id = filter->id;
507     g_ptr_array_add (monitor->priv->filters, filter);
508   } else {
509     device_filter_free (filter);
510   }
511
512   GST_OBJECT_UNLOCK (monitor);
513
514   return id;
515 }
516
517 /**
518  * gst_device_monitor_remove_filter:
519  * @monitor: a device monitor
520  * @filter_id: the id of the filter
521  *
522  * Removes a filter from the #GstDeviceMonitor using the id that was returned
523  * by gst_device_monitor_add_filter().
524  *
525  * Returns: %TRUE of the filter id was valid, %FALSE otherwise
526  *
527  * Since: 1.4
528  */
529 gboolean
530 gst_device_monitor_remove_filter (GstDeviceMonitor * monitor, guint filter_id)
531 {
532   guint i, j;
533   gboolean removed = FALSE;
534
535   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), FALSE);
536   g_return_val_if_fail (!monitor->priv->started, FALSE);
537   g_return_val_if_fail (filter_id > 0, FALSE);
538
539   GST_OBJECT_LOCK (monitor);
540   for (i = 0; i < monitor->priv->filters->len; i++) {
541     struct DeviceFilter *filter = g_ptr_array_index (monitor->priv->filters, i);
542
543     if (filter->id == filter_id) {
544       g_ptr_array_remove_index (monitor->priv->filters, i);
545       removed = TRUE;
546       break;
547     }
548   }
549
550   if (removed) {
551     for (i = 0; i < monitor->priv->providers->len; i++) {
552       GstDeviceProvider *provider =
553           g_ptr_array_index (monitor->priv->providers, i);
554       GstDeviceProviderFactory *factory =
555           gst_device_provider_get_factory (provider);
556       gboolean valid = FALSE;
557
558       for (j = 0; j < monitor->priv->filters->len; j++) {
559         struct DeviceFilter *filter =
560             g_ptr_array_index (monitor->priv->filters, j);
561
562         if (gst_device_provider_factory_has_classesv (factory,
563                 filter->classesv)) {
564           valid = TRUE;
565           break;
566         }
567       }
568
569       if (!valid) {
570         monitor->priv->cookie++;
571         gst_device_monitor_remove (monitor, i);
572         i--;
573       }
574     }
575   }
576
577   GST_OBJECT_UNLOCK (monitor);
578
579   return removed;
580 }
581
582
583
584 /**
585  * gst_device_monitor_new:
586  *
587  * Create a new #GstDeviceMonitor
588  *
589  * Returns: (transfer full): a new device monitor.
590  *
591  * Since: 1.4
592  */
593 GstDeviceMonitor *
594 gst_device_monitor_new (void)
595 {
596   return g_object_new (GST_TYPE_DEVICE_MONITOR, NULL);
597 }
598
599 /**
600  * gst_device_monitor_get_bus:
601  * @monitor: a #GstDeviceProvider
602  *
603  * Gets the #GstBus of this #GstDeviceMonitor
604  *
605  * Returns: (transfer full): a #GstBus
606  *
607  * Since: 1.4
608  */
609 GstBus *
610 gst_device_monitor_get_bus (GstDeviceMonitor * monitor)
611 {
612   g_return_val_if_fail (GST_IS_DEVICE_MONITOR (monitor), NULL);
613
614   return gst_object_ref (monitor->priv->bus);
615 }