2 * Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
4 * gstv4l2deviceprovider.c: V4l2 device probing and monitoring
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.
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.
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.
26 #include "gstv4l2deviceprovider.h"
33 #include "gstv4l2object.h"
34 #include "v4l2_calls.h"
35 #include "v4l2-utils.h"
38 #include <gudev/gudev.h>
41 static GstV4l2Device *gst_v4l2_device_new (const gchar * device_path,
42 const gchar * device_name, GstCaps * caps, GstV4l2DeviceType type);
45 G_DEFINE_TYPE (GstV4l2DeviceProvider, gst_v4l2_device_provider,
46 GST_TYPE_DEVICE_PROVIDER);
48 static void gst_v4l2_device_provider_finalize (GObject * object);
49 static GList *gst_v4l2_device_provider_probe (GstDeviceProvider * provider);
52 static gboolean gst_v4l2_device_provider_start (GstDeviceProvider * provider);
53 static void gst_v4l2_device_provider_stop (GstDeviceProvider * provider);
58 gst_v4l2_device_provider_class_init (GstV4l2DeviceProviderClass * klass)
60 GstDeviceProviderClass *dm_class = GST_DEVICE_PROVIDER_CLASS (klass);
61 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
63 dm_class->probe = gst_v4l2_device_provider_probe;
66 dm_class->start = gst_v4l2_device_provider_start;
67 dm_class->stop = gst_v4l2_device_provider_stop;
70 gobject_class->finalize = gst_v4l2_device_provider_finalize;
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>");
79 gst_v4l2_device_provider_init (GstV4l2DeviceProvider * provider)
82 g_cond_init (&provider->started_cond);
87 gst_v4l2_device_provider_finalize (GObject * object)
90 GstV4l2DeviceProvider *provider = GST_V4L2_DEVICE_PROVIDER (object);
92 g_cond_clear (&provider->started_cond);
95 G_OBJECT_CLASS (gst_v4l2_device_provider_parent_class)->finalize (object);
98 static GstV4l2Device *
99 gst_v4l2_device_provider_probe_device (GstV4l2DeviceProvider * provider,
100 const gchar * device_path, const gchar * device_name)
102 GstV4l2Object *v4l2obj;
104 GstV4l2Device *device = NULL;
106 GstV4l2DeviceType type = GST_V4L2_DEVICE_TYPE_INVALID;
108 if (stat (device_path, &st) == -1)
111 if (!S_ISCHR (st.st_mode))
114 v4l2obj = gst_v4l2_object_new ((GstElement *) provider,
115 V4L2_BUF_TYPE_VIDEO_CAPTURE, device_path, NULL, NULL, NULL);
117 if (!gst_v4l2_open (v4l2obj))
121 if (v4l2obj->vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
122 type = GST_V4L2_DEVICE_TYPE_SOURCE;
124 if (v4l2obj->vcap.capabilities & V4L2_CAP_VIDEO_OUTPUT) {
125 /* Morph it in case our initial guess was wrong */
126 v4l2obj->type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
128 if (type == GST_V4L2_DEVICE_TYPE_INVALID)
129 type = GST_V4L2_DEVICE_TYPE_SINK;
131 /* We ignore M2M devices that are both capture and output for now
132 * The provider is not for them
137 caps = gst_v4l2_object_get_caps (v4l2obj, NULL);
141 if (gst_caps_is_empty (caps)) {
142 gst_caps_unref (caps);
146 device = gst_v4l2_device_new (device_path,
147 device_name ? device_name : (gchar *) v4l2obj->vcap.card, caps, type);
148 gst_caps_unref (caps);
152 gst_v4l2_close (v4l2obj);
156 gst_v4l2_object_destroy (v4l2obj);
163 gst_v4l2_device_provider_probe (GstDeviceProvider * provider)
165 GstV4l2DeviceProvider *self = GST_V4L2_DEVICE_PROVIDER (provider);
167 GList *devices = NULL;
169 it = gst_v4l2_iterator_new ();
171 while (gst_v4l2_iterator_next (it)) {
172 GstV4l2Device *device;
175 gst_v4l2_device_provider_probe_device (self, it->device_path, NULL);
178 gst_object_ref_sink (device);
179 devices = g_list_prepend (devices, device);
183 gst_v4l2_iterator_free (it);
191 gst_v4l2_device_provider_device_from_udev (GstV4l2DeviceProvider * provider,
192 GUdevDevice * udev_device)
194 GstV4l2Device *gstdev;
195 const gchar *device_path = g_udev_device_get_device_file (udev_device);
196 const gchar *device_name;
198 device_name = g_udev_device_get_property (udev_device, "ID_V4L_PRODUCT");
200 device_name = g_udev_device_get_property (udev_device, "ID_MODEL_ENC");
202 device_name = g_udev_device_get_property (udev_device, "ID_MODEL");
204 gstdev = gst_v4l2_device_provider_probe_device (provider, device_path,
208 gstdev->syspath = g_strdup (g_udev_device_get_sysfs_path (udev_device));
210 return GST_DEVICE (gstdev);
214 uevent_cb (GUdevClient * client, const gchar * action, GUdevDevice * device,
215 GstV4l2DeviceProvider * self)
217 GstDeviceProvider *provider = GST_DEVICE_PROVIDER (self);
219 /* Not V4L2, ignoring */
220 if (g_udev_device_get_property_as_int (device, "ID_V4L_VERSION") != 2)
223 if (!strcmp (action, "add")) {
224 GstDevice *gstdev = NULL;
226 gstdev = gst_v4l2_device_provider_device_from_udev (self, device);
229 gst_device_provider_device_add (provider, gstdev);
230 } else if (!strcmp (action, "remove")) {
231 GstV4l2Device *gstdev = NULL;
234 GST_OBJECT_LOCK (self);
235 for (item = provider->devices; item; item = item->next) {
238 if (!strcmp (gstdev->syspath, g_udev_device_get_sysfs_path (device))) {
239 gst_object_ref (gstdev);
245 GST_OBJECT_UNLOCK (provider);
248 gst_device_provider_device_remove (provider, GST_DEVICE (gstdev));
249 g_object_unref (gstdev);
252 GST_WARNING ("Unhandled action %s", action);
257 provider_thread (gpointer data)
259 GstV4l2DeviceProvider *provider = data;
260 GMainContext *context = NULL;
261 GMainLoop *loop = NULL;
264 static const gchar *subsystems[] = { "video4linux", NULL };
266 GST_OBJECT_LOCK (provider);
267 if (provider->context)
268 context = g_main_context_ref (provider->context);
270 loop = g_main_loop_ref (provider->loop);
272 if (context == NULL || loop == NULL) {
273 provider->started = TRUE;
274 g_cond_broadcast (&provider->started_cond);
275 GST_OBJECT_UNLOCK (provider);
278 GST_OBJECT_UNLOCK (provider);
280 g_main_context_push_thread_default (context);
282 client = g_udev_client_new (subsystems);
284 g_signal_connect (client, "uevent", G_CALLBACK (uevent_cb), provider);
286 devices = g_udev_client_query_by_subsystem (client, "video4linux");
289 GUdevDevice *udev_device = devices->data;
292 devices = g_list_remove (devices, udev_device);
294 if (g_udev_device_get_property_as_int (udev_device, "ID_V4L_VERSION") == 2) {
296 gst_v4l2_device_provider_device_from_udev (provider, udev_device);
298 gst_device_provider_device_add (GST_DEVICE_PROVIDER (provider), gstdev);
301 g_object_unref (udev_device);
304 GST_OBJECT_LOCK (provider);
305 provider->started = TRUE;
306 g_cond_broadcast (&provider->started_cond);
307 GST_OBJECT_UNLOCK (provider);
309 g_main_loop_run (loop);
310 g_main_loop_unref (loop);
312 g_object_unref (client);
313 g_main_context_unref (context);
315 gst_object_unref (provider);
321 gst_v4l2_device_provider_start (GstDeviceProvider * provider)
323 GstV4l2DeviceProvider *self = GST_V4L2_DEVICE_PROVIDER (provider);
325 GST_OBJECT_LOCK (self);
326 g_assert (self->context == NULL);
328 self->context = g_main_context_new ();
329 self->loop = g_main_loop_new (self->context, FALSE);
331 self->thread = g_thread_new ("v4l2-device-provider", provider_thread,
332 g_object_ref (self));
334 while (self->started == FALSE)
335 g_cond_wait (&self->started_cond, GST_OBJECT_GET_LOCK (self));
337 GST_OBJECT_UNLOCK (self);
343 gst_v4l2_device_provider_stop (GstDeviceProvider * provider)
345 GstV4l2DeviceProvider *self = GST_V4L2_DEVICE_PROVIDER (provider);
346 GMainContext *context;
348 GSource *idle_stop_source;
350 GST_OBJECT_LOCK (self);
351 context = self->context;
353 self->context = NULL;
355 GST_OBJECT_UNLOCK (self);
357 if (!context || !loop)
360 idle_stop_source = g_idle_source_new ();
361 g_source_set_callback (idle_stop_source, (GSourceFunc) g_main_loop_quit, loop,
362 (GDestroyNotify) g_main_loop_unref);
363 g_source_attach (idle_stop_source, context);
364 g_source_unref (idle_stop_source);
365 g_main_context_unref (context);
367 g_thread_join (self->thread);
368 g_thread_unref (self->thread);
370 self->started = FALSE;
377 PROP_DEVICE_PATH = 1,
380 G_DEFINE_TYPE (GstV4l2Device, gst_v4l2_device, GST_TYPE_DEVICE);
382 static void gst_v4l2_device_get_property (GObject * object, guint prop_id,
383 GValue * value, GParamSpec * pspec);
384 static void gst_v4l2_device_set_property (GObject * object, guint prop_id,
385 const GValue * value, GParamSpec * pspec);
386 static void gst_v4l2_device_finalize (GObject * object);
387 static GstElement *gst_v4l2_device_create_element (GstDevice * device,
391 gst_v4l2_device_class_init (GstV4l2DeviceClass * klass)
393 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
394 GObjectClass *object_class = G_OBJECT_CLASS (klass);
396 dev_class->create_element = gst_v4l2_device_create_element;
398 object_class->get_property = gst_v4l2_device_get_property;
399 object_class->set_property = gst_v4l2_device_set_property;
400 object_class->finalize = gst_v4l2_device_finalize;
402 g_object_class_install_property (object_class, PROP_DEVICE_PATH,
403 g_param_spec_string ("device-path", "Device Path",
404 "The Path of the device node", "",
405 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
409 gst_v4l2_device_init (GstV4l2Device * device)
414 gst_v4l2_device_finalize (GObject * object)
416 GstV4l2Device *device = GST_V4L2_DEVICE (object);
418 g_free (device->device_path);
419 g_free (device->syspath);
421 G_OBJECT_CLASS (gst_v4l2_device_parent_class)->finalize (object);
425 gst_v4l2_device_create_element (GstDevice * device, const gchar * name)
427 GstV4l2Device *v4l2_dev = GST_V4L2_DEVICE (device);
430 elem = gst_element_factory_make (v4l2_dev->element, name);
431 g_object_set (elem, "device", v4l2_dev->device_path, NULL);
436 static GstV4l2Device *
437 gst_v4l2_device_new (const gchar * device_path, const gchar * device_name,
438 GstCaps * caps, GstV4l2DeviceType type)
440 GstV4l2Device *gstdev;
441 const gchar *element;
444 g_return_val_if_fail (device_path, NULL);
445 g_return_val_if_fail (device_name, NULL);
446 g_return_val_if_fail (caps, NULL);
449 case GST_V4L2_DEVICE_TYPE_SOURCE:
451 klass = "Video/Source";
453 case GST_V4L2_DEVICE_TYPE_SINK:
454 element = "v4l2sink";
455 klass = "Video/Sink";
458 g_assert_not_reached ();
462 gstdev = g_object_new (GST_TYPE_V4L2_DEVICE, "device-path", device_path,
463 "display-name", device_name, "caps", caps, "device-class", klass, NULL);
465 gstdev->element = element;
473 gst_v4l2_device_get_property (GObject * object, guint prop_id,
474 GValue * value, GParamSpec * pspec)
476 GstV4l2Device *device;
478 device = GST_V4L2_DEVICE_CAST (object);
481 case PROP_DEVICE_PATH:
482 g_value_set_string (value, device->device_path);
485 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
492 gst_v4l2_device_set_property (GObject * object, guint prop_id,
493 const GValue * value, GParamSpec * pspec)
495 GstV4l2Device *device;
497 device = GST_V4L2_DEVICE_CAST (object);
500 case PROP_DEVICE_PATH:
501 device->device_path = g_value_dup_string (value);
504 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);