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>
27
#include <dbus/dbus-glib-lowlevel.h>
31
#define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
32
#include <gudev/gudev.h>
36
#include <sys/ioctl.h>
37
#if USE_SYS_VIDEOIO_H > 0
38
#include <sys/types.h>
39
#include <sys/videoio.h>
41
#include <sys/types.h>
42
#include <sys/videodev2.h>
43
#endif /* USE_SYS_VIDEOIO_H */
46
#include "cheese-camera-device-monitor.h"
47
#include "cheese-marshal.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
* #CheeseCameraDeviceMonitor 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
* CheeseCameraDeviceMonitor::added or
61
* CheeseCameraDeviceMonitor::removed signal when an event happens.
64
G_DEFINE_TYPE (CheeseCameraDeviceMonitor, cheese_camera_device_monitor, G_TYPE_OBJECT)
66
#define CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
67
CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \
68
CheeseCameraDeviceMonitorPrivate))
70
#define CHEESE_CAMERA_DEVICE_MONITOR_ERROR cheese_camera_device_monitor_error_quark ()
72
GST_DEBUG_CATEGORY (cheese_device_monitor_cat);
73
#define GST_CAT_DEFAULT cheese_device_monitor_cat
75
enum CheeseCameraDeviceMonitorError
77
CHEESE_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN,
78
CHEESE_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND
87
#endif /* HAVE_UDEV */
88
} CheeseCameraDeviceMonitorPrivate;
97
static guint monitor_signals[LAST_SIGNAL];
100
cheese_camera_device_monitor_error_quark (void)
102
return g_quark_from_static_string ("cheese-camera-error-quark");
107
cheese_camera_device_monitor_added (CheeseCameraDeviceMonitor *monitor,
108
GUdevDevice *udevice)
110
const char *device_file;
111
const char *product_name;
117
gint v4l_version = 0;
119
const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH");
121
GST_INFO ("Checking udev device '%s'", devpath);
123
bus = g_udev_device_get_property (udevice, "ID_BUS");
124
if (g_strcmp0 (bus, "usb") == 0)
126
vendor = g_udev_device_get_property (udevice, "ID_VENDOR_ID");
128
vendor_id = g_ascii_strtoll (vendor, NULL, 16);
129
product = g_udev_device_get_property (udevice, "ID_MODEL_ID");
131
product_id = g_ascii_strtoll (vendor, NULL, 16);
132
if (vendor_id == 0 || product_id == 0)
134
GST_WARNING ("Error getting vendor and product id");
138
GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id);
143
GST_INFO ("Not an usb device, skipping vendor and model id retrieval");
146
device_file = g_udev_device_get_device_file (udevice);
147
if (device_file == NULL)
149
GST_WARNING ("Error getting V4L device");
153
/* vbi devices support capture capability too, but cannot be used,
154
* so detect them by device name */
155
if (strstr (device_file, "vbi"))
157
GST_INFO ("Skipping vbi device: %s", device_file);
161
v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION");
162
if (v4l_version == 2 || v4l_version == 1)
166
caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
167
if (caps == NULL || strstr (caps, ":capture:") == NULL)
169
GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)"
170
"Removing it from device list.", device_file);
173
product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT");
175
else if (v4l_version == 0)
177
GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file);
182
g_assert_not_reached ();
185
g_signal_emit (monitor, monitor_signals[ADDED], 0,
193
cheese_camera_device_monitor_removed (CheeseCameraDeviceMonitor *monitor,
194
GUdevDevice *udevice)
196
g_signal_emit (monitor, monitor_signals[REMOVED], 0,
197
g_udev_device_get_property (udevice, "DEVPATH"));
201
cheese_camera_device_monitor_uevent_cb (GUdevClient *client,
203
GUdevDevice *udevice,
204
CheeseCameraDeviceMonitor *monitor)
206
if (g_str_equal (action, "remove"))
207
cheese_camera_device_monitor_removed (monitor, udevice);
208
else if (g_str_equal (action, "add"))
209
cheese_camera_device_monitor_added (monitor, udevice);
213
* cheese_camera_device_monitor_coldplug:
214
* @monitor: a #CheeseCameraDeviceMonitor object.
216
* Will actively look for plugged in cameras and emit
217
* ::added for those new cameras.
218
* This is only required when your program starts, so as to connect
219
* to those signals before they are emitted.
222
cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
224
CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
228
if (priv->client == NULL)
231
GST_INFO ("Probing devices with udev...");
233
devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
235
/* Initialize camera structures */
236
for (l = devices; l != NULL; l = l->next)
238
cheese_camera_device_monitor_added (monitor, l->data);
239
g_object_unref (l->data);
242
g_list_free (devices);
244
if (i == 0) GST_WARNING ("No device found");
247
#else /* HAVE_UDEV */
249
cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
252
CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
253
struct v4l2_capability v2cap;
254
struct video_capability v1cap;
257
if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0)
259
g_warning ("Failed to open %s: %s", device_path, strerror (errno));
262
ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
265
ok = ioctl (fd, VIDIOCGCAP, &v1cap);
268
g_warning ("Error while probing v4l capabilities for %s: %s",
269
device_path, strerror (errno));
273
g_print ("Detected v4l device: %s\n", v1cap.name);
274
g_print ("Device type: %d\n", v1cap.type);
275
gstreamer_src = "v4lsrc";
276
product_name = v1cap.name;
280
guint cap = v2cap.capabilities;
281
g_print ("Detected v4l2 device: %s\n", v2cap.card);
282
g_print ("Driver: %s, version: %d\n", v2cap.driver, v2cap.version);
284
/* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */
285
g_print ("Capabilities: 0x%08X\n", v2cap.capabilities);
286
if (!(cap & V4L2_CAP_VIDEO_CAPTURE))
288
g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
289
"Removing it from device list.\n", device_path);
293
gstreamer_src = "v4l2src";
294
product_name = (char *) v2cap.card;
300
g_print ("Probing devices with udev...\n");
302
if (priv->client == NULL)
305
devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
307
/* Initialize camera structures */
308
for (l = devices; l != NULL; l = l->next)
310
cheese_camera_device_monitor_added (monitor, l->data);
311
g_object_unref (l->data);
313
g_list_free (devices);
317
#endif /* HAVE_UDEV */
320
cheese_camera_device_monitor_finalize (GObject *object)
323
CheeseCameraDeviceMonitor *monitor;
325
monitor = CHEESE_CAMERA_DEVICE_MONITOR (object);
326
CheeseCameraDeviceMonitorPrivate *priv = CHEESE_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 (cheese_camera_device_monitor_parent_class)->finalize (object);
338
cheese_camera_device_monitor_class_init (CheeseCameraDeviceMonitorClass *klass)
340
GObjectClass *object_class = G_OBJECT_CLASS (klass);
342
if (cheese_device_monitor_cat == NULL)
343
GST_DEBUG_CATEGORY_INIT (cheese_device_monitor_cat,
344
"cheese-device-monitor",
345
0, "Cheese Camera Device Monitor");
347
object_class->finalize = cheese_camera_device_monitor_finalize;
350
* CheeseCameraDeviceMonitor::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 #cheese_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 (CheeseCameraDeviceMonitorClass, added),
364
_cheese_marshal_VOID__STRING_STRING_STRING_INT,
365
G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
368
* CheeseCameraDeviceMonitor::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 (CheeseCameraDeviceMonitorClass, removed),
379
g_cclosure_marshal_VOID__STRING,
380
G_TYPE_NONE, 1, G_TYPE_STRING);
382
g_type_class_add_private (klass, sizeof (CheeseCameraDeviceMonitorPrivate));
386
cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor *monitor)
389
CheeseCameraDeviceMonitorPrivate *priv = CHEESE_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 (cheese_camera_device_monitor_uevent_cb), monitor);
395
#endif /* HAVE_UDEV */
399
* cheese_camera_device_monitor_new:
401
* Returns a new #CheeseCameraDeviceMonitor object.
403
* Return value: a new #CheeseCameraDeviceMonitor object.
405
CheeseCameraDeviceMonitor *
406
cheese_camera_device_monitor_new (void)
408
return g_object_new (CHEESE_TYPE_CAMERA_DEVICE_MONITOR, NULL);
412
* vim: sw=2 ts=8 cindent noai bs=2