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