2 * Copyright © 2007,2008 Jaap Haitsma <jaap@haitsma.org>
3 * Copyright © 2007-2009 daniel g. siegel <dgsiegel@gnome.org>
4 * Copyright © 2008 Ryan Zeigler <zeiglerr@gmail.com>
5 * Copyright © 2010 Filippo Argiolas <filippo.argiolas@gmail.com>
7 * Licensed under the GNU General Public License Version 2
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 #include <cheese-config.h>
26 #include <glib-object.h>
30 #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
31 #include <gudev/gudev.h>
35 #include <sys/ioctl.h>
36 #if USE_SYS_VIDEOIO_H > 0
37 #include <sys/types.h>
38 #include <sys/videoio.h>
40 #include <sys/types.h>
41 #include <sys/videodev2.h>
42 #endif /* USE_SYS_VIDEOIO_H */
45 #include "cheese-camera-device-monitor.h"
46 #include "cheese-marshal.h"
49 * SECTION:cheese-camera-device-monitor
50 * @short_description: Simple object to enumerate v4l devices
51 * @include: cheese/cheese-camera-device-monitor.h
53 * #CheeseCameraDeviceMonitor provides a basic interface for
54 * video4linux device enumeration and hotplugging.
56 * It uses either GUdev or some platform specific code to list video
57 * devices. It is also capable (right now in linux only, with the
58 * udev backend) to monitor device plugging and emit a
59 * CheeseCameraDeviceMonitor::added or
60 * CheeseCameraDeviceMonitor::removed signal when an event happens.
63 G_DEFINE_TYPE (CheeseCameraDeviceMonitor
, cheese_camera_device_monitor
, G_TYPE_OBJECT
)
65 #define CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
66 CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \
67 CheeseCameraDeviceMonitorPrivate))
69 #define CHEESE_CAMERA_DEVICE_MONITOR_ERROR cheese_camera_device_monitor_error_quark ()
71 GST_DEBUG_CATEGORY (cheese_device_monitor_cat
);
72 #define GST_CAT_DEFAULT cheese_device_monitor_cat
74 enum CheeseCameraDeviceMonitorError
76 CHEESE_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN
,
77 CHEESE_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND
86 #endif /* HAVE_UDEV */
87 } CheeseCameraDeviceMonitorPrivate
;
96 static guint monitor_signals
[LAST_SIGNAL
];
99 cheese_camera_device_monitor_error_quark (void)
101 return g_quark_from_static_string ("cheese-camera-error-quark");
106 cheese_camera_device_monitor_added (CheeseCameraDeviceMonitor
*monitor
,
107 GUdevDevice
*udevice
)
109 const char *device_file
;
110 const char *product_name
;
116 gint v4l_version
= 0;
118 const gchar
*devpath
= g_udev_device_get_property (udevice
, "DEVPATH");
120 GST_INFO ("Checking udev device '%s'", devpath
);
122 bus
= g_udev_device_get_property (udevice
, "ID_BUS");
123 if (g_strcmp0 (bus
, "usb") == 0)
125 vendor
= g_udev_device_get_property (udevice
, "ID_VENDOR_ID");
127 vendor_id
= g_ascii_strtoll (vendor
, NULL
, 16);
128 product
= g_udev_device_get_property (udevice
, "ID_MODEL_ID");
130 product_id
= g_ascii_strtoll (product
, NULL
, 16);
131 if (vendor_id
== 0 || product_id
== 0)
133 GST_WARNING ("Error getting vendor and product id");
137 GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id
, product_id
);
142 GST_INFO ("Not an usb device, skipping vendor and model id retrieval");
145 device_file
= g_udev_device_get_device_file (udevice
);
146 if (device_file
== NULL
)
148 GST_WARNING ("Error getting V4L device");
152 /* vbi devices support capture capability too, but cannot be used,
153 * so detect them by device name */
154 if (strstr (device_file
, "vbi"))
156 GST_INFO ("Skipping vbi device: %s", device_file
);
160 v4l_version
= g_udev_device_get_property_as_int (udevice
, "ID_V4L_VERSION");
161 if (v4l_version
== 2 || v4l_version
== 1)
165 caps
= g_udev_device_get_property (udevice
, "ID_V4L_CAPABILITIES");
166 if (caps
== NULL
|| strstr (caps
, ":capture:") == NULL
)
168 GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)"
169 "Removing it from device list.", device_file
);
172 product_name
= g_udev_device_get_property (udevice
, "ID_V4L_PRODUCT");
174 else if (v4l_version
== 0)
176 GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file
);
181 g_assert_not_reached ();
184 g_signal_emit (monitor
, monitor_signals
[ADDED
], 0,
192 cheese_camera_device_monitor_removed (CheeseCameraDeviceMonitor
*monitor
,
193 GUdevDevice
*udevice
)
195 g_signal_emit (monitor
, monitor_signals
[REMOVED
], 0,
196 g_udev_device_get_property (udevice
, "DEVPATH"));
200 cheese_camera_device_monitor_uevent_cb (GUdevClient
*client
,
202 GUdevDevice
*udevice
,
203 CheeseCameraDeviceMonitor
*monitor
)
205 if (g_str_equal (action
, "remove"))
206 cheese_camera_device_monitor_removed (monitor
, udevice
);
207 else if (g_str_equal (action
, "add"))
208 cheese_camera_device_monitor_added (monitor
, udevice
);
212 * cheese_camera_device_monitor_coldplug:
213 * @monitor: a #CheeseCameraDeviceMonitor object.
215 * Will actively look for plugged in cameras and emit
216 * ::added for those new cameras.
217 * This is only required when your program starts, so as to connect
218 * to those signals before they are emitted.
221 cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor
*monitor
)
223 CheeseCameraDeviceMonitorPrivate
*priv
= CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor
);
227 if (priv
->client
== NULL
)
230 GST_INFO ("Probing devices with udev...");
232 devices
= g_udev_client_query_by_subsystem (priv
->client
, "video4linux");
234 /* Initialize camera structures */
235 for (l
= devices
; l
!= NULL
; l
= l
->next
)
237 cheese_camera_device_monitor_added (monitor
, l
->data
);
238 g_object_unref (l
->data
);
241 g_list_free (devices
);
243 if (i
== 0) GST_WARNING ("No device found");
246 #else /* HAVE_UDEV */
248 cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor
*monitor
)
251 CheeseCameraDeviceMonitorPrivate
*priv
= CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor
);
252 struct v4l2_capability v2cap
;
253 struct video_capability v1cap
;
256 if ((fd
= open (device_path
, O_RDONLY
| O_NONBLOCK
)) < 0)
258 g_warning ("Failed to open %s: %s", device_path
, strerror (errno
));
261 ok
= ioctl (fd
, VIDIOC_QUERYCAP
, &v2cap
);
264 ok
= ioctl (fd
, VIDIOCGCAP
, &v1cap
);
267 g_warning ("Error while probing v4l capabilities for %s: %s",
268 device_path
, strerror (errno
));
272 g_print ("Detected v4l device: %s\n", v1cap
.name
);
273 g_print ("Device type: %d\n", v1cap
.type
);
274 gstreamer_src
= "v4lsrc";
275 product_name
= v1cap
.name
;
279 guint cap
= v2cap
.capabilities
;
280 g_print ("Detected v4l2 device: %s\n", v2cap
.card
);
281 g_print ("Driver: %s, version: %d\n", v2cap
.driver
, v2cap
.version
);
283 /* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */
284 g_print ("Capabilities: 0x%08X\n", v2cap
.capabilities
);
285 if (!(cap
& V4L2_CAP_VIDEO_CAPTURE
))
287 g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
288 "Removing it from device list.\n", device_path
);
292 gstreamer_src
= "v4l2src";
293 product_name
= (char *) v2cap
.card
;
299 g_print ("Probing devices with udev...\n");
301 if (priv
->client
== NULL
)
304 devices
= g_udev_client_query_by_subsystem (priv
->client
, "video4linux");
306 /* Initialize camera structures */
307 for (l
= devices
; l
!= NULL
; l
= l
->next
)
309 cheese_camera_device_monitor_added (monitor
, l
->data
);
310 g_object_unref (l
->data
);
312 g_list_free (devices
);
316 #endif /* HAVE_UDEV */
319 cheese_camera_device_monitor_finalize (GObject
*object
)
322 CheeseCameraDeviceMonitor
*monitor
;
324 monitor
= CHEESE_CAMERA_DEVICE_MONITOR (object
);
325 CheeseCameraDeviceMonitorPrivate
*priv
= CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor
);
327 if (priv
->client
!= NULL
)
329 g_object_unref (priv
->client
);
332 #endif /* HAVE_UDEV */
333 G_OBJECT_CLASS (cheese_camera_device_monitor_parent_class
)->finalize (object
);
337 cheese_camera_device_monitor_class_init (CheeseCameraDeviceMonitorClass
*klass
)
339 GObjectClass
*object_class
= G_OBJECT_CLASS (klass
);
341 if (cheese_device_monitor_cat
== NULL
)
342 GST_DEBUG_CATEGORY_INIT (cheese_device_monitor_cat
,
343 "cheese-device-monitor",
344 0, "Cheese Camera Device Monitor");
346 object_class
->finalize
= cheese_camera_device_monitor_finalize
;
349 * CheeseCameraDeviceMonitor::added:
350 * @device: A private object representing the newly added camera.
351 * @id: Device unique identifier.
352 * @device: Device file name (e.g. /dev/video2).
353 * @product_name: Device product name (human readable, intended to be displayed in a UI).
354 * @api_version: Supported video4linux API: 1 for v4l, 2 for v4l2.
356 * The ::added signal is emitted when a camera is added, or on start-up
357 * after #cheese_camera_device_monitor_colplug is called.
359 monitor_signals
[ADDED
] = g_signal_new ("added", G_OBJECT_CLASS_TYPE (klass
),
360 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
361 G_STRUCT_OFFSET (CheeseCameraDeviceMonitorClass
, added
),
363 _cheese_marshal_VOID__STRING_STRING_STRING_INT
,
364 G_TYPE_NONE
, 4, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_INT
);
367 * CheeseCameraDeviceMonitor::removed:
368 * @device: A private object representing the newly added camera
369 * @id: Device unique identifier.
371 * The ::removed signal is emitted when a camera is un-plugged, or
372 * disabled on the system.
374 monitor_signals
[REMOVED
] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (klass
),
375 G_SIGNAL_RUN_LAST
| G_SIGNAL_ACTION
,
376 G_STRUCT_OFFSET (CheeseCameraDeviceMonitorClass
, removed
),
378 g_cclosure_marshal_VOID__STRING
,
379 G_TYPE_NONE
, 1, G_TYPE_STRING
);
381 g_type_class_add_private (klass
, sizeof (CheeseCameraDeviceMonitorPrivate
));
385 cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor
*monitor
)
388 CheeseCameraDeviceMonitorPrivate
*priv
= CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor
);
389 const gchar
*const subsystems
[] = {"video4linux", NULL
};
391 priv
->client
= g_udev_client_new (subsystems
);
392 g_signal_connect (G_OBJECT (priv
->client
), "uevent",
393 G_CALLBACK (cheese_camera_device_monitor_uevent_cb
), monitor
);
394 #endif /* HAVE_UDEV */
398 * cheese_camera_device_monitor_new:
400 * Returns a new #CheeseCameraDeviceMonitor object.
402 * Return value: a new #CheeseCameraDeviceMonitor object.
404 CheeseCameraDeviceMonitor
*
405 cheese_camera_device_monitor_new (void)
407 return g_object_new (CHEESE_TYPE_CAMERA_DEVICE_MONITOR
, NULL
);
411 * vim: sw=2 ts=8 cindent noai bs=2