2 * Copyright (C) 2012 Olivier Crete <olivier.crete@collabora.com>
4 * gstv4l2devicemonitor.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 "gstv4l2devicemonitor.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 (GstV4l2DeviceMonitor, gst_v4l2_device_monitor,
46 GST_TYPE_DEVICE_MONITOR);
48 static void gst_v4l2_device_monitor_finalize (GObject * object);
49 static GList *gst_v4l2_device_monitor_probe (GstDeviceMonitor * monitor);
52 static gboolean gst_v4l2_device_monitor_start (GstDeviceMonitor * monitor);
53 static void gst_v4l2_device_monitor_stop (GstDeviceMonitor * monitor);
58 gst_v4l2_device_monitor_class_init (GstV4l2DeviceMonitorClass * klass)
60 GstDeviceMonitorClass *dm_class = GST_DEVICE_MONITOR_CLASS (klass);
61 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
63 dm_class->probe = gst_v4l2_device_monitor_probe;
66 dm_class->start = gst_v4l2_device_monitor_start;
67 dm_class->stop = gst_v4l2_device_monitor_stop;
70 gobject_class->finalize = gst_v4l2_device_monitor_finalize;
72 gst_device_monitor_class_set_static_metadata (dm_class,
73 "Video (video4linux2) Device Monitor", "Source/Sink/Video",
74 "List and monitor video4linux2 source and sink devices",
75 "Olivier Crete <olivier.crete@collabora.com>");
79 gst_v4l2_device_monitor_init (GstV4l2DeviceMonitor * monitor)
82 g_cond_init (&monitor->started_cond);
87 gst_v4l2_device_monitor_finalize (GObject * object)
90 GstV4l2DeviceMonitor *monitor = GST_V4L2_DEVICE_MONITOR (object);
92 g_cond_clear (&monitor->started_cond);
95 G_OBJECT_CLASS (gst_v4l2_device_monitor_parent_class)->finalize (object);
98 static GstV4l2Device *
99 gst_v4l2_device_monitor_probe_device (GstV4l2DeviceMonitor * monitor,
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 *) monitor,
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 monitor 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_monitor_probe (GstDeviceMonitor * monitor)
165 GstV4l2DeviceMonitor *self = GST_V4L2_DEVICE_MONITOR (monitor);
167 GList *devices = NULL;
169 it = gst_v4l2_iterator_new ();
171 while (gst_v4l2_iterator_next (it)) {
172 GstV4l2Device *device;
174 device = gst_v4l2_device_monitor_probe_device (self, it->device_path, NULL);
177 gst_object_ref_sink (device);
178 devices = g_list_prepend (devices, device);
182 gst_v4l2_iterator_free (it);
190 gst_v4l2_device_monitor_device_from_udev (GstV4l2DeviceMonitor * monitor,
191 GUdevDevice * udev_device)
193 GstV4l2Device *gstdev;
194 const gchar *device_path = g_udev_device_get_device_file (udev_device);
195 const gchar *device_name;
197 device_name = g_udev_device_get_property (udev_device, "ID_V4L_PRODUCT");
199 device_name = g_udev_device_get_property (udev_device, "ID_MODEL_ENC");
201 device_name = g_udev_device_get_property (udev_device, "ID_MODEL");
203 gstdev = gst_v4l2_device_monitor_probe_device (monitor, device_path,
207 gstdev->syspath = g_strdup (g_udev_device_get_sysfs_path (udev_device));
209 return GST_DEVICE (gstdev);
213 uevent_cb (GUdevClient * client, const gchar * action, GUdevDevice * device,
214 GstV4l2DeviceMonitor * self)
216 GstDeviceMonitor *monitor = GST_DEVICE_MONITOR (self);
218 /* Not V4L2, ignoring */
219 if (g_udev_device_get_property_as_int (device, "ID_V4L_VERSION") != 2)
222 if (!strcmp (action, "add")) {
223 GstDevice *gstdev = NULL;
225 gstdev = gst_v4l2_device_monitor_device_from_udev (self, device);
228 gst_device_monitor_device_add (monitor, gstdev);
229 } else if (!strcmp (action, "remove")) {
230 GstV4l2Device *gstdev = NULL;
233 GST_OBJECT_LOCK (self);
234 for (item = monitor->devices; item; item = item->next) {
237 if (!strcmp (gstdev->syspath, g_udev_device_get_sysfs_path (device))) {
238 gst_object_ref (gstdev);
244 GST_OBJECT_UNLOCK (monitor);
247 gst_device_monitor_device_remove (monitor, GST_DEVICE (gstdev));
248 g_object_unref (gstdev);
251 GST_WARNING ("Unhandled action %s", action);
256 monitor_thread (gpointer data)
258 GstV4l2DeviceMonitor *monitor = data;
259 GMainContext *context = NULL;
260 GMainLoop *loop = NULL;
263 static const gchar *subsystems[] = { "video4linux", NULL };
265 GST_OBJECT_LOCK (monitor);
266 if (monitor->context)
267 context = g_main_context_ref (monitor->context);
269 loop = g_main_loop_ref (monitor->loop);
271 if (context == NULL || loop == NULL) {
272 monitor->started = TRUE;
273 g_cond_broadcast (&monitor->started_cond);
274 GST_OBJECT_UNLOCK (monitor);
277 GST_OBJECT_UNLOCK (monitor);
279 g_main_context_push_thread_default (context);
281 client = g_udev_client_new (subsystems);
283 g_signal_connect (client, "uevent", G_CALLBACK (uevent_cb), monitor);
285 devices = g_udev_client_query_by_subsystem (client, "video4linux");
288 GUdevDevice *udev_device = devices->data;
291 devices = g_list_remove (devices, udev_device);
293 if (g_udev_device_get_property_as_int (udev_device, "ID_V4L_VERSION") == 2) {
294 gstdev = gst_v4l2_device_monitor_device_from_udev (monitor, udev_device);
296 gst_device_monitor_device_add (GST_DEVICE_MONITOR (monitor), gstdev);
299 g_object_unref (udev_device);
302 GST_OBJECT_LOCK (monitor);
303 monitor->started = TRUE;
304 g_cond_broadcast (&monitor->started_cond);
305 GST_OBJECT_UNLOCK (monitor);
307 g_main_loop_run (loop);
308 g_main_loop_unref (loop);
310 g_object_unref (client);
311 g_main_context_unref (context);
313 gst_object_unref (monitor);
319 gst_v4l2_device_monitor_start (GstDeviceMonitor * monitor)
321 GstV4l2DeviceMonitor *self = GST_V4L2_DEVICE_MONITOR (monitor);
323 GST_OBJECT_LOCK (self);
324 g_assert (self->context == NULL);
326 self->context = g_main_context_new ();
327 self->loop = g_main_loop_new (self->context, FALSE);
329 self->thread = g_thread_new ("v4l2-device-monitor", monitor_thread,
330 g_object_ref (self));
332 while (self->started == FALSE)
333 g_cond_wait (&self->started_cond, GST_OBJECT_GET_LOCK (self));
335 GST_OBJECT_UNLOCK (self);
341 gst_v4l2_device_monitor_stop (GstDeviceMonitor * monitor)
343 GstV4l2DeviceMonitor *self = GST_V4L2_DEVICE_MONITOR (monitor);
344 GMainContext *context;
346 GSource *idle_stop_source;
348 GST_OBJECT_LOCK (self);
349 context = self->context;
351 self->context = NULL;
353 GST_OBJECT_UNLOCK (self);
355 if (!context || !loop)
358 idle_stop_source = g_idle_source_new ();
359 g_source_set_callback (idle_stop_source, (GSourceFunc) g_main_loop_quit, loop,
360 (GDestroyNotify) g_main_loop_unref);
361 g_source_attach (idle_stop_source, context);
362 g_source_unref (idle_stop_source);
363 g_main_context_unref (context);
365 g_thread_join (self->thread);
366 g_thread_unref (self->thread);
368 self->started = FALSE;
375 PROP_DEVICE_PATH = 1,
378 G_DEFINE_TYPE (GstV4l2Device, gst_v4l2_device, GST_TYPE_DEVICE);
380 static void gst_v4l2_device_get_property (GObject * object, guint prop_id,
381 GValue * value, GParamSpec * pspec);
382 static void gst_v4l2_device_set_property (GObject * object, guint prop_id,
383 const GValue * value, GParamSpec * pspec);
384 static void gst_v4l2_device_finalize (GObject * object);
385 static GstElement *gst_v4l2_device_create_element (GstDevice * device,
389 gst_v4l2_device_class_init (GstV4l2DeviceClass * klass)
391 GstDeviceClass *dev_class = GST_DEVICE_CLASS (klass);
392 GObjectClass *object_class = G_OBJECT_CLASS (klass);
394 dev_class->create_element = gst_v4l2_device_create_element;
396 object_class->get_property = gst_v4l2_device_get_property;
397 object_class->set_property = gst_v4l2_device_set_property;
398 object_class->finalize = gst_v4l2_device_finalize;
400 g_object_class_install_property (object_class, PROP_DEVICE_PATH,
401 g_param_spec_string ("device-path", "Device Path",
402 "The Path of the device node", "",
403 G_PARAM_STATIC_STRINGS | G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
407 gst_v4l2_device_init (GstV4l2Device * device)
412 gst_v4l2_device_finalize (GObject * object)
414 GstV4l2Device *device = GST_V4L2_DEVICE (object);
416 g_free (device->device_path);
417 g_free (device->syspath);
419 G_OBJECT_CLASS (gst_v4l2_device_parent_class)->finalize (object);
423 gst_v4l2_device_create_element (GstDevice * device, const gchar * name)
425 GstV4l2Device *v4l2_dev = GST_V4L2_DEVICE (device);
428 elem = gst_element_factory_make (v4l2_dev->element, name);
429 g_object_set (elem, "device", v4l2_dev->device_path, NULL);
434 static GstV4l2Device *
435 gst_v4l2_device_new (const gchar * device_path, const gchar * device_name,
436 GstCaps * caps, GstV4l2DeviceType type)
438 GstV4l2Device *gstdev;
439 const gchar *element;
442 g_return_val_if_fail (device_path, NULL);
443 g_return_val_if_fail (device_name, NULL);
444 g_return_val_if_fail (caps, NULL);
447 case GST_V4L2_DEVICE_TYPE_SOURCE:
449 klass = "Video/Source";
451 case GST_V4L2_DEVICE_TYPE_SINK:
452 element = "v4l2sink";
453 klass = "Video/Sink";
456 g_assert_not_reached ();
460 gstdev = g_object_new (GST_TYPE_V4L2_DEVICE, "device-path", device_path,
461 "display-name", device_name, "caps", caps, "device-class", klass, NULL);
463 gstdev->element = element;
471 gst_v4l2_device_get_property (GObject * object, guint prop_id,
472 GValue * value, GParamSpec * pspec)
474 GstV4l2Device *device;
476 device = GST_V4L2_DEVICE_CAST (object);
479 case PROP_DEVICE_PATH:
480 g_value_set_string (value, device->device_path);
483 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
490 gst_v4l2_device_set_property (GObject * object, guint prop_id,
491 const GValue * value, GParamSpec * pspec)
493 GstV4l2Device *device;
495 device = GST_V4L2_DEVICE_CAST (object);
498 case PROP_DEVICE_PATH:
499 device->device_path = g_value_dup_string (value);
502 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);