1 /* This file is a copy of cheese-camera-device-monitor.c from Empathy. We
2 * just renamespaced it to avoid conflicts when linking on libcheese. */
4 * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org>
5 * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org>
6 * Copyright © 2008 Ryan Zeigler <zeiglerr@gmail.com>
7 * Copyright © 2010 Filippo Argiolas <filippo.argiolas@gmail.com>
9 * Licensed under the GNU General Public License Version 2
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
28 #include <glib-object.h>
32 #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
33 #include <gudev/gudev.h>
37 #include <sys/ioctl.h>
38 #if USE_SYS_VIDEOIO_H > 0
39 #include <sys/types.h>
40 #include <sys/videoio.h>
42 #include <sys/types.h>
43 #include <sys/videodev2.h>
44 #endif /* USE_SYS_VIDEOIO_H */
47 #include "cheese-camera-device-monitor.h"
50 * SECTION:cheese-camera-device-monitor
51 * @short_description: Simple object to enumerate v4l devices
52 * @include: cheese/cheese-camera-device-monitor.h
54 * #EmpathyCameraDeviceMonitor provides a basic interface for
55 * video4linux device enumeration and hotplugging.
57 * It uses either GUdev or some platform specific code to list video
58 * devices. It is also capable (right now in linux only, with the
59 * udev backend) to monitor device plugging and emit a
60 * EmpathyCameraDeviceMonitor::added or
61 * EmpathyCameraDeviceMonitor::removed signal when an event happens.
64 G_DEFINE_TYPE (EmpathyCameraDeviceMonitor, empathy_camera_device_monitor, G_TYPE_OBJECT)
66 #define EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
67 EMPATHY_TYPE_CAMERA_DEVICE_MONITOR, \
68 EmpathyCameraDeviceMonitorPrivate))
70 #define EMPATHY_CAMERA_DEVICE_MONITOR_ERROR empathy_camera_device_monitor_error_quark ()
72 GST_DEBUG_CATEGORY (empathy_device_monitor_cat);
73 #define GST_CAT_DEFAULT empathy_device_monitor_cat
75 enum EmpathyCameraDeviceMonitorError
77 EMPATHY_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN,
78 EMPATHY_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND
87 #endif /* HAVE_UDEV */
88 } EmpathyCameraDeviceMonitorPrivate;
97 static guint monitor_signals[LAST_SIGNAL];
101 empathy_camera_device_monitor_error_quark (void)
103 return g_quark_from_static_string ("empathy-camera-error-quark");
109 empathy_camera_device_monitor_added (EmpathyCameraDeviceMonitor *monitor,
110 GUdevDevice *udevice)
112 const char *device_file;
113 const char *product_name;
119 gint v4l_version = 0;
121 const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH");
123 GST_INFO ("Checking udev device '%s'", devpath);
125 bus = g_udev_device_get_property (udevice, "ID_BUS");
126 if (g_strcmp0 (bus, "usb") == 0)
128 vendor = g_udev_device_get_property (udevice, "ID_VENDOR_ID");
130 vendor_id = g_ascii_strtoll (vendor, NULL, 16);
131 product = g_udev_device_get_property (udevice, "ID_MODEL_ID");
133 product_id = g_ascii_strtoll (product, NULL, 16);
134 if (vendor_id == 0 || product_id == 0)
136 GST_WARNING ("Error getting vendor and product id");
140 GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id);
145 GST_INFO ("Not an usb device, skipping vendor and model id retrieval");
148 device_file = g_udev_device_get_device_file (udevice);
149 if (device_file == NULL)
151 GST_WARNING ("Error getting V4L device");
155 /* vbi devices support capture capability too, but cannot be used,
156 * so detect them by device name */
157 if (strstr (device_file, "vbi"))
159 GST_INFO ("Skipping vbi device: %s", device_file);
163 v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION");
164 if (v4l_version == 2 || v4l_version == 1)
168 caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
169 if (caps == NULL || strstr (caps, ":capture:") == NULL)
171 GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)"
172 "Removing it from device list.", device_file);
175 product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT");
177 else if (v4l_version == 0)
179 GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file);
184 g_assert_not_reached ();
187 g_signal_emit (monitor, monitor_signals[ADDED], 0,
195 empathy_camera_device_monitor_removed (EmpathyCameraDeviceMonitor *monitor,
196 GUdevDevice *udevice)
198 g_signal_emit (monitor, monitor_signals[REMOVED], 0,
199 g_udev_device_get_property (udevice, "DEVPATH"));
203 empathy_camera_device_monitor_uevent_cb (GUdevClient *client,
205 GUdevDevice *udevice,
206 EmpathyCameraDeviceMonitor *monitor)
208 if (g_str_equal (action, "remove"))
209 empathy_camera_device_monitor_removed (monitor, udevice);
210 else if (g_str_equal (action, "add"))
211 empathy_camera_device_monitor_added (monitor, udevice);
215 * empathy_camera_device_monitor_coldplug:
216 * @monitor: a #EmpathyCameraDeviceMonitor object.
218 * Will actively look for plugged in cameras and emit
219 * ::added for those new cameras.
220 * This is only required when your program starts, so as to connect
221 * to those signals before they are emitted.
224 empathy_camera_device_monitor_coldplug (EmpathyCameraDeviceMonitor *monitor)
226 EmpathyCameraDeviceMonitorPrivate *priv = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
230 if (priv->client == NULL)
233 GST_INFO ("Probing devices with udev...");
235 devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
237 /* Initialize camera structures */
238 for (l = devices; l != NULL; l = l->next)
240 empathy_camera_device_monitor_added (monitor, l->data);
241 g_object_unref (l->data);
244 g_list_free (devices);
246 if (i == 0) GST_WARNING ("No device found");
249 #else /* HAVE_UDEV */
251 empathy_camera_device_monitor_coldplug (EmpathyCameraDeviceMonitor *monitor)
254 EmpathyCameraDeviceMonitorPrivate *priv = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
255 struct v4l2_capability v2cap;
256 struct video_capability v1cap;
259 if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0)
261 g_warning ("Failed to open %s: %s", device_path, strerror (errno));
264 ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
267 ok = ioctl (fd, VIDIOCGCAP, &v1cap);
270 g_warning ("Error while probing v4l capabilities for %s: %s",
271 device_path, strerror (errno));
275 g_print ("Detected v4l device: %s\n", v1cap.name);
276 g_print ("Device type: %d\n", v1cap.type);
277 gstreamer_src = "v4lsrc";
278 product_name = v1cap.name;
282 guint cap = v2cap.capabilities;
283 g_print ("Detected v4l2 device: %s\n", v2cap.card);
284 g_print ("Driver: %s, version: %d\n", v2cap.driver, v2cap.version);
286 /* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */
287 g_print ("Capabilities: 0x%08X\n", v2cap.capabilities);
288 if (!(cap & V4L2_CAP_VIDEO_CAPTURE))
290 g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
291 "Removing it from device list.\n", device_path);
295 gstreamer_src = "v4l2src";
296 product_name = (char *) v2cap.card;
302 g_print ("Probing devices with udev...\n");
304 if (priv->client == NULL)
307 devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
309 /* Initialize camera structures */
310 for (l = devices; l != NULL; l = l->next)
312 empathy_camera_device_monitor_added (monitor, l->data);
313 g_object_unref (l->data);
315 g_list_free (devices);
319 #endif /* HAVE_UDEV */
322 empathy_camera_device_monitor_finalize (GObject *object)
325 EmpathyCameraDeviceMonitor *monitor = EMPATHY_CAMERA_DEVICE_MONITOR (object);
326 EmpathyCameraDeviceMonitorPrivate *priv = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
328 if (priv->client != NULL)
330 g_object_unref (priv->client);
333 #endif /* HAVE_UDEV */
334 G_OBJECT_CLASS (empathy_camera_device_monitor_parent_class)->finalize (object);
338 empathy_camera_device_monitor_class_init (EmpathyCameraDeviceMonitorClass *klass)
340 GObjectClass *object_class = G_OBJECT_CLASS (klass);
342 if (empathy_device_monitor_cat == NULL)
343 GST_DEBUG_CATEGORY_INIT (empathy_device_monitor_cat,
344 "empathy-device-monitor",
345 0, "Empathy Camera Device Monitor");
347 object_class->finalize = empathy_camera_device_monitor_finalize;
350 * EmpathyCameraDeviceMonitor::added:
351 * @device: A private object representing the newly added camera.
352 * @id: Device unique identifier.
353 * @device: Device file name (e.g. /dev/video2).
354 * @product_name: Device product name (human readable, intended to be displayed in a UI).
355 * @api_version: Supported video4linux API: 1 for v4l, 2 for v4l2.
357 * The ::added signal is emitted when a camera is added, or on start-up
358 * after #empathy_camera_device_monitor_colplug is called.
360 monitor_signals[ADDED] = g_signal_new ("added", G_OBJECT_CLASS_TYPE (klass),
361 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
362 G_STRUCT_OFFSET (EmpathyCameraDeviceMonitorClass, added),
364 g_cclosure_marshal_generic,
365 G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
368 * EmpathyCameraDeviceMonitor::removed:
369 * @device: A private object representing the newly added camera
370 * @id: Device unique identifier.
372 * The ::removed signal is emitted when a camera is un-plugged, or
373 * disabled on the system.
375 monitor_signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (klass),
376 G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
377 G_STRUCT_OFFSET (EmpathyCameraDeviceMonitorClass, removed),
379 g_cclosure_marshal_generic,
380 G_TYPE_NONE, 1, G_TYPE_STRING);
382 g_type_class_add_private (klass, sizeof (EmpathyCameraDeviceMonitorPrivate));
386 empathy_camera_device_monitor_init (EmpathyCameraDeviceMonitor *monitor)
389 EmpathyCameraDeviceMonitorPrivate *priv = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
390 const gchar *const subsystems[] = {"video4linux", NULL};
392 priv->client = g_udev_client_new (subsystems);
393 g_signal_connect (G_OBJECT (priv->client), "uevent",
394 G_CALLBACK (empathy_camera_device_monitor_uevent_cb), monitor);
395 #endif /* HAVE_UDEV */
399 * empathy_camera_device_monitor_new:
401 * Returns a new #EmpathyCameraDeviceMonitor object.
403 * Return value: a new #EmpathyCameraDeviceMonitor object.
405 EmpathyCameraDeviceMonitor *
406 empathy_camera_device_monitor_new (void)
408 return g_object_new (EMPATHY_TYPE_CAMERA_DEVICE_MONITOR, NULL);
412 * vim: sw=2 ts=8 cindent noai bs=2