Revert "v4l2object: Use mmap64 to match libv4l2 signature"
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / gstv4l2deviceprovider.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 "gstv4l2deviceprovider.h"
27
28 #include <string.h>
29 #include <sys/stat.h>
30
31 #include <gst/gst.h>
32
33 #include "gstv4l2object.h"
34 #include "v4l2-utils.h"
35
36 #ifdef HAVE_GUDEV
37 #include <gudev/gudev.h>
38 #endif
39
40 static GstV4l2Device *gst_v4l2_device_new (const gchar * device_path,
41     const gchar * device_name, GstCaps * caps, GstV4l2DeviceType type,
42     GstStructure * props);
43
44
45 G_DEFINE_TYPE (GstV4l2DeviceProvider, gst_v4l2_device_provider,
46     GST_TYPE_DEVICE_PROVIDER);
47
48 static void gst_v4l2_device_provider_finalize (GObject * object);
49 static GList *gst_v4l2_device_provider_probe (GstDeviceProvider * provider);
50
51 #ifdef HAVE_GUDEV
52 static gboolean gst_v4l2_device_provider_start (GstDeviceProvider * provider);
53 static void gst_v4l2_device_provider_stop (GstDeviceProvider * provider);
54 #endif
55
56
57 static void
58 gst_v4l2_device_provider_class_init (GstV4l2DeviceProviderClass * klass)
59 {
60   GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
61   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
62
63   dm_class->probe = gst_v4l2_device_provider_probe;
64
65 #ifdef HAVE_GUDEV
66   dm_class->start = gst_v4l2_device_provider_start;
67   dm_class->stop = gst_v4l2_device_provider_stop;
68 #endif
69
70   gobject_class->finalize = gst_v4l2_device_provider_finalize;
71
72   gst_device_provider_class_set_static_metadata (dm_class,
73       "Video (video4linux2) Device Provider", "Source/Sink/Video",
74       "List and monitor video4linux2 source and sink devices",
75       "Olivier Crete <olivier.crete@collabora.com>");
76 }
77
78 static void
79 gst_v4l2_device_provider_init (GstV4l2DeviceProvider * provider)
80 {
81 #ifdef HAVE_GUDEV
82   g_cond_init (&provider->started_cond);
83 #endif
84 }
85
86 static void
87 gst_v4l2_device_provider_finalize (GObject * object)
88 {
89 #ifdef HAVE_GUDEV
90   GstV4l2DeviceProvider *provider = GST_V4L2_DEVICE_PROVIDER (object);
91
92   g_cond_clear (&provider->started_cond);
93 #endif
94
95   G_OBJECT_CLASS (gst_v4l2_device_provider_parent_class)->finalize (object);
96 }
97
98 static GstV4l2Device *
99 gst_v4l2_device_provider_probe_device (GstV4l2DeviceProvider * provider,
100     const gchar * device_path, const gchar * device_name, GstStructure * props)
101 {
102   GstV4l2Object *v4l2obj = NULL;
103   GstCaps *caps;
104   GstV4l2Device *device = NULL;
105   struct stat st;
106   GstV4l2DeviceType type = GST_V4L2_DEVICE_TYPE_INVALID;
107
108   g_return_val_if_fail (props != NULL, NULL);
109
110   if (stat (device_path, &st) == -1)
111     goto destroy;
112
113   if (!S_ISCHR (st.st_mode))
114     goto destroy;
115
116   v4l2obj = gst_v4l2_object_new (NULL, GST_OBJECT (provider),
117       V4L2_BUF_TYPE_VIDEO_CAPTURE, device_path, NULL, NULL, NULL);
118
119   if (!gst_v4l2_open (v4l2obj))
120     goto destroy;
121
122   gst_structure_set (props, "device.api", G_TYPE_STRING, "v4l2", NULL);
123   gst_structure_set (props, "device.path", G_TYPE_STRING, device_path, NULL);
124
125   gst_structure_set (props, "v4l2.device.driver", G_TYPE_STRING,
126       v4l2obj->vcap.driver, NULL);
127   gst_structure_set (props, "v4l2.device.card", G_TYPE_STRING,
128       v4l2obj->vcap.card, NULL);
129   gst_structure_set (props, "v4l2.device.bus_info", G_TYPE_STRING,
130       v4l2obj->vcap.bus_info, NULL);
131   gst_structure_set (props, "v4l2.device.version", G_TYPE_UINT,
132       v4l2obj->vcap.version, NULL);
133   gst_structure_set (props, "v4l2.device.capabilities", G_TYPE_UINT,
134       v4l2obj->vcap.capabilities, NULL);
135   gst_structure_set (props, "v4l2.device.device_caps", G_TYPE_UINT,
136       v4l2obj->vcap.device_caps, NULL);
137
138   if (v4l2obj->device_caps &
139       (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_CAPTURE_MPLANE)) {
140     /* We ignore touch sensing devices; those are't really video */
141     if (v4l2obj->device_caps & V4L2_CAP_TOUCH)
142       goto close;
143
144     type = GST_V4L2_DEVICE_TYPE_SOURCE;
145   }
146
147   if (v4l2obj->device_caps &
148       (V4L2_CAP_VIDEO_OUTPUT | V4L2_CAP_VIDEO_OUTPUT_MPLANE)) {
149     /* We ignore M2M devices that are both capture and output for now
150      * The provider is not for them */
151     if (type != GST_V4L2_DEVICE_TYPE_INVALID)
152       goto close;
153
154     type = GST_V4L2_DEVICE_TYPE_SINK;
155
156     /* We have opened as a capture as we didn't know, now that know,
157      * let's fixed it */
158     if (v4l2obj->device_caps & V4L2_CAP_VIDEO_OUTPUT_MPLANE)
159       v4l2obj->type = V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE;
160     else
161       v4l2obj->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
162   }
163
164   if (type == GST_V4L2_DEVICE_TYPE_INVALID)
165     goto close;
166
167   caps = gst_v4l2_object_get_caps (v4l2obj, NULL);
168
169   if (caps == NULL)
170     goto close;
171   if (gst_caps_is_empty (caps)) {
172     gst_caps_unref (caps);
173     goto close;
174   }
175
176   device = gst_v4l2_device_new (device_path,
177       device_name ? device_name : (gchar *) v4l2obj->vcap.card, caps, type,
178       props);
179   gst_caps_unref (caps);
180
181 close:
182
183   gst_v4l2_close (v4l2obj);
184
185 destroy:
186
187   if (v4l2obj)
188     gst_v4l2_object_destroy (v4l2obj);
189
190   if (props)
191     gst_structure_free (props);
192
193   return device;
194 }
195
196
197 static GList *
198 gst_v4l2_device_provider_probe (GstDeviceProvider * provider)
199 {
200   GstV4l2DeviceProvider *self = GST_V4L2_DEVICE_PROVIDER (provider);
201   GstV4l2Iterator *it;
202   GList *devices = NULL;
203
204   it = gst_v4l2_iterator_new ();
205
206   while (gst_v4l2_iterator_next (it)) {
207     GstStructure *props;
208     GstV4l2Device *device;
209
210     props = gst_structure_new ("v4l2-proplist", "device.path", G_TYPE_STRING,
211         it->device_path, "udev-probed", G_TYPE_BOOLEAN, FALSE, NULL);
212     device = gst_v4l2_device_provider_probe_device (self, it->device_path, NULL,
213         props);
214
215     if (device) {
216       gst_object_ref_sink (device);
217       devices = g_list_prepend (devices, device);
218     }
219   }
220
221   gst_v4l2_iterator_free (it);
222
223   return devices;
224 }
225
226 #ifdef HAVE_GUDEV
227
228 static GstDevice *
229 gst_v4l2_device_provider_device_from_udev (GstV4l2DeviceProvider * provider,
230     GUdevDevice * udev_device)
231 {
232   GstV4l2Device *gstdev;
233   const gchar *device_path = g_udev_device_get_device_file (udev_device);
234   const gchar *device_name, *str;
235   GstStructure *props;
236
237   props = gst_structure_new ("v4l2deviceprovider", "udev-probed",
238       G_TYPE_BOOLEAN, TRUE, NULL);
239
240   str = g_udev_device_get_property (udev_device, "ID_PATH");
241   if (!(str && *str)) {
242     str = g_udev_device_get_sysfs_path (udev_device);
243   }
244   if (str && *str)
245     gst_structure_set (props, "device.bus_path", G_TYPE_STRING, str, NULL);
246
247   if ((str = g_udev_device_get_sysfs_path (udev_device)) && *str)
248     gst_structure_set (props, "sysfs.path", G_TYPE_STRING, str, NULL);
249
250   if ((str = g_udev_device_get_property (udev_device, "ID_ID")) && *str)
251     gst_structure_set (props, "udev.id", G_TYPE_STRING, str, NULL);
252
253   if ((str = g_udev_device_get_property (udev_device, "ID_BUS")) && *str)
254     gst_structure_set (props, "device.bus", G_TYPE_STRING, str, NULL);
255
256   if ((str = g_udev_device_get_property (udev_device, "SUBSYSTEM")) && *str)
257     gst_structure_set (props, "device.subsystem", G_TYPE_STRING, str, NULL);
258
259   if ((str = g_udev_device_get_property (udev_device, "ID_VENDOR_ID")) && *str)
260     gst_structure_set (props, "device.vendor.id", G_TYPE_STRING, str, NULL);
261
262   str = g_udev_device_get_property (udev_device, "ID_VENDOR_FROM_DATABASE");
263   if (!(str && *str)) {
264     str = g_udev_device_get_property (udev_device, "ID_VENDOR_ENC");
265     if (!(str && *str)) {
266       str = g_udev_device_get_property (udev_device, "ID_VENDOR");
267     }
268   }
269   if (str && *str)
270     gst_structure_set (props, "device.vendor.name", G_TYPE_STRING, str, NULL);
271
272   if ((str = g_udev_device_get_property (udev_device, "ID_MODEL_ID")) && *str)
273     gst_structure_set (props, "device.product.id", G_TYPE_STRING, str, NULL);
274
275   device_name = g_udev_device_get_property (udev_device, "ID_V4L_PRODUCT");
276   if (!(device_name && *device_name)) {
277     device_name =
278         g_udev_device_get_property (udev_device, "ID_MODEL_FROM_DATABASE");
279     if (!(device_name && *device_name)) {
280       device_name = g_udev_device_get_property (udev_device, "ID_MODEL_ENC");
281       if (!(device_name && *device_name)) {
282         device_name = g_udev_device_get_property (udev_device, "ID_MODEL");
283       }
284     }
285   }
286   if (device_name && *device_name)
287     gst_structure_set (props, "device.product.name", G_TYPE_STRING, device_name,
288         NULL);
289
290   if ((str = g_udev_device_get_property (udev_device, "ID_SERIAL")) && *str)
291     gst_structure_set (props, "device.serial", G_TYPE_STRING, str, NULL);
292
293   if ((str = g_udev_device_get_property (udev_device, "ID_V4L_CAPABILITIES"))
294       && *str)
295     gst_structure_set (props, "device.capabilities", G_TYPE_STRING, str, NULL);
296
297   gstdev = gst_v4l2_device_provider_probe_device (provider, device_path,
298       device_name, props);
299
300   if (gstdev)
301     gstdev->syspath = g_strdup (g_udev_device_get_sysfs_path (udev_device));
302
303   return GST_DEVICE (gstdev);
304 }
305
306 static void
307 uevent_cb (GUdevClient * client, const gchar * action, GUdevDevice * device,
308     GstV4l2DeviceProvider * self)
309 {
310   GstDeviceProvider *provider = GST_DEVICE_PROVIDER (self);
311
312   /* Not V4L2, ignoring */
313   if (g_udev_device_get_property_as_int (device, "ID_V4L_VERSION") != 2)
314     return;
315
316   if (!strcmp (action, "add")) {
317     GstDevice *gstdev = NULL;
318
319     gstdev = gst_v4l2_device_provider_device_from_udev (self, device);
320
321     if (gstdev)
322       gst_device_provider_device_add (provider, gstdev);
323   } else if (!strcmp (action, "remove")) {
324     GstV4l2Device *gstdev = NULL;
325     GList *item;
326
327     GST_OBJECT_LOCK (self);
328     for (item = provider->devices; item; item = item->next) {
329       gstdev = item->data;
330
331       if (!strcmp (gstdev->syspath, g_udev_device_get_sysfs_path (device))) {
332         gst_object_ref (gstdev);
333         break;
334       }
335
336       gstdev = NULL;
337     }
338     GST_OBJECT_UNLOCK (provider);
339
340     if (gstdev) {
341       gst_device_provider_device_remove (provider, GST_DEVICE (gstdev));
342       g_object_unref (gstdev);
343     }
344   } else {
345     GST_WARNING ("Unhandled action %s", action);
346   }
347 }
348
349 static gpointer
350 provider_thread (gpointer data)
351 {
352   GstV4l2DeviceProvider *provider = data;
353   GMainContext *context = NULL;
354   GMainLoop *loop = NULL;
355   GUdevClient *client;
356   GList *devices;
357   static const gchar *subsystems[] = { "video4linux", NULL };
358
359   GST_OBJECT_LOCK (provider);
360   if (provider->context)
361     context = g_main_context_ref (provider->context);
362   if (provider->loop)
363     loop = g_main_loop_ref (provider->loop);
364
365   if (context == NULL || loop == NULL) {
366     provider->started = TRUE;
367     g_cond_broadcast (&provider->started_cond);
368     GST_OBJECT_UNLOCK (provider);
369     return NULL;
370   }
371   GST_OBJECT_UNLOCK (provider);
372
373   g_main_context_push_thread_default (context);
374
375   client = g_udev_client_new (subsystems);
376
377   g_signal_connect (client, "uevent", G_CALLBACK (uevent_cb), provider);
378
379   devices = g_udev_client_query_by_subsystem (client, "video4linux");
380
381   while (devices) {
382     GUdevDevice *udev_device = devices->data;
383     GstDevice *gstdev;
384
385     devices = g_list_remove (devices, udev_device);
386
387     if (g_udev_device_get_property_as_int (udev_device, "ID_V4L_VERSION") == 2) {
388       gstdev =
389           gst_v4l2_device_provider_device_from_udev (provider, udev_device);
390       if (gstdev)
391         gst_device_provider_device_add (GST_DEVICE_PROVIDER (provider), gstdev);
392     }
393
394     g_object_unref (udev_device);
395   }
396
397   GST_OBJECT_LOCK (provider);
398   provider->started = TRUE;
399   g_cond_broadcast (&provider->started_cond);
400   GST_OBJECT_UNLOCK (provider);
401
402   g_main_loop_run (loop);
403   g_main_loop_unref (loop);
404
405   g_object_unref (client);
406   g_main_context_unref (context);
407
408   gst_object_unref (provider);
409
410   return NULL;
411 }
412
413 static gboolean
414 gst_v4l2_device_provider_start (GstDeviceProvider * provider)
415 {
416   GstV4l2DeviceProvider *self = GST_V4L2_DEVICE_PROVIDER (provider);
417
418   GST_OBJECT_LOCK (self);
419   g_assert (self->context == NULL);
420
421   self->context = g_main_context_new ();
422   self->loop = g_main_loop_new (self->context, FALSE);
423
424   self->thread = g_thread_new ("v4l2-device-provider", provider_thread,
425       g_object_ref (self));
426
427   while (self->started == FALSE)
428     g_cond_wait (&self->started_cond, GST_OBJECT_GET_LOCK (self));
429
430   GST_OBJECT_UNLOCK (self);
431
432   return TRUE;
433 }
434
435 static void
436 gst_v4l2_device_provider_stop (GstDeviceProvider * provider)
437 {
438   GstV4l2DeviceProvider *self = GST_V4L2_DEVICE_PROVIDER (provider);
439   GMainContext *context;
440   GMainLoop *loop;
441   GSource *idle_stop_source;
442
443   GST_OBJECT_LOCK (self);
444   context = self->context;
445   loop = self->loop;
446   self->context = NULL;
447   self->loop = NULL;
448   GST_OBJECT_UNLOCK (self);
449
450   if (!context || !loop)
451     return;
452
453   idle_stop_source = g_idle_source_new ();
454   g_source_set_callback (idle_stop_source, (GSourceFunc) g_main_loop_quit, loop,
455       (GDestroyNotify) g_main_loop_unref);
456   g_source_attach (idle_stop_source, context);
457   g_source_unref (idle_stop_source);
458   g_main_context_unref (context);
459
460   g_thread_join (self->thread);
461   self->thread = NULL;
462   self->started = FALSE;
463 }
464
465 #endif
466
467 enum
468 {
469   PROP_DEVICE_PATH = 1,
470 };
471
472 G_DEFINE_TYPE (GstV4l2Device, gst_v4l2_device, GST_TYPE_DEVICE);
473
474 static void gst_v4l2_device_get_property (GObject * object, guint prop_id,
475     GValue * value, GParamSpec * pspec);
476 static void gst_v4l2_device_set_property (GObject * object, guint prop_id,
477     const GValue * value, GParamSpec * pspec);
478 static void gst_v4l2_device_finalize (GObject * object);
479 static GstElement *gst_v4l2_device_create_element (GstDevice * device,
480     const gchar * name);
481
482 static void
483 gst_v4l2_device_class_init (GstV4l2DeviceClass * klass)
484 {
485   GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
486   GObjectClass *object_class = G_OBJECT_CLASS (klass);
487
488   dev_class->create_element = gst_v4l2_device_create_element;
489
490   object_class->get_property = gst_v4l2_device_get_property;
491   object_class->set_property = gst_v4l2_device_set_property;
492   object_class->finalize = gst_v4l2_device_finalize;
493
494   g_object_class_install_property (object_class, PROP_DEVICE_PATH,
495       g_param_spec_string ("device-path", "Device Path",
496           "The Path of the device node", "",
497           G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
498 }
499
500 static void
501 gst_v4l2_device_init (GstV4l2Device * device)
502 {
503 }
504
505 static void
506 gst_v4l2_device_finalize (GObject * object)
507 {
508   GstV4l2Device *device = GST_V4L2_DEVICE (object);
509
510   g_free (device->device_path);
511   g_free (device->syspath);
512
513   G_OBJECT_CLASS (gst_v4l2_device_parent_class)->finalize (object);
514 }
515
516 static GstElement *
517 gst_v4l2_device_create_element (GstDevice * device, const gchar * name)
518 {
519   GstV4l2Device *v4l2_dev = GST_V4L2_DEVICE (device);
520   GstElement *elem;
521
522   elem = gst_element_factory_make (v4l2_dev->element, name);
523   g_object_set (elem, "device", v4l2_dev->device_path, NULL);
524
525   return elem;
526 }
527
528 static GstV4l2Device *
529 gst_v4l2_device_new (const gchar * device_path, const gchar * device_name,
530     GstCaps * caps, GstV4l2DeviceType type, GstStructure * props)
531 {
532   GstV4l2Device *gstdev;
533   const gchar *element = NULL;
534   const gchar *klass = NULL;
535
536   g_return_val_if_fail (device_path, NULL);
537   g_return_val_if_fail (device_name, NULL);
538   g_return_val_if_fail (caps, NULL);
539
540   switch (type) {
541     case GST_V4L2_DEVICE_TYPE_SOURCE:
542       element = "v4l2src";
543       klass = "Video/Source";
544       break;
545     case GST_V4L2_DEVICE_TYPE_SINK:
546       element = "v4l2sink";
547       klass = "Video/Sink";
548       break;
549     default:
550       g_assert_not_reached ();
551       break;
552   }
553
554   gstdev = g_object_new (GST_TYPE_V4L2_DEVICE, "device-path", device_path,
555       "display-name", device_name, "caps", caps, "device-class", klass,
556       "properties", props, NULL);
557
558   gstdev->element = element;
559
560
561   return gstdev;
562 }
563
564
565 static void
566 gst_v4l2_device_get_property (GObject * object, guint prop_id,
567     GValue * value, GParamSpec * pspec)
568 {
569   GstV4l2Device *device;
570
571   device = GST_V4L2_DEVICE_CAST (object);
572
573   switch (prop_id) {
574     case PROP_DEVICE_PATH:
575       g_value_set_string (value, device->device_path);
576       break;
577     default:
578       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
579       break;
580   }
581 }
582
583
584 static void
585 gst_v4l2_device_set_property (GObject * object, guint prop_id,
586     const GValue * value, GParamSpec * pspec)
587 {
588   GstV4l2Device *device;
589
590   device = GST_V4L2_DEVICE_CAST (object);
591
592   switch (prop_id) {
593     case PROP_DEVICE_PATH:
594       device->device_path = g_value_dup_string (value);
595       break;
596     default:
597       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
598       break;
599   }
600 }