Don't assume a connected TpAccount always have a TpConnection
[empathy-mirror.git] / libempathy / cheese-camera-device-monitor.c
blob85c6a0aa49b7c15cfb5ea2850e7f9e03d669344c
1 /*
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/>.
22 #ifdef HAVE_CONFIG_H
23 #include <config.h>
24 #endif
26 #include <glib-object.h>
27 #include <string.h>
29 #ifdef HAVE_UDEV
30 #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
31 #include <gudev/gudev.h>
32 #else
33 #include <fcntl.h>
34 #include <unistd.h>
35 #include <sys/ioctl.h>
36 #if USE_SYS_VIDEOIO_H > 0
37 #include <sys/types.h>
38 #include <sys/videoio.h>
39 #elif defined (__sun)
40 #include <sys/types.h>
41 #include <sys/videodev2.h>
42 #endif /* USE_SYS_VIDEOIO_H */
43 #endif
45 #include "cheese-camera-device-monitor.h"
46 #include "empathy-marshal.h"
48 /**
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
80 typedef struct
82 #ifdef HAVE_UDEV
83 GUdevClient *client;
84 #else
85 guint filler;
86 #endif /* HAVE_UDEV */
87 } CheeseCameraDeviceMonitorPrivate;
89 enum
91 ADDED,
92 REMOVED,
93 LAST_SIGNAL
96 static guint monitor_signals[LAST_SIGNAL];
98 #if 0
99 GQuark
100 cheese_camera_device_monitor_error_quark (void)
102 return g_quark_from_static_string ("cheese-camera-error-quark");
104 #endif
106 #ifdef HAVE_UDEV
107 static void
108 cheese_camera_device_monitor_added (CheeseCameraDeviceMonitor *monitor,
109 GUdevDevice *udevice)
111 const char *device_file;
112 const char *product_name;
113 const char *vendor;
114 const char *product;
115 const char *bus;
116 gint vendor_id = 0;
117 gint product_id = 0;
118 gint v4l_version = 0;
120 const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH");
122 GST_INFO ("Checking udev device '%s'", devpath);
124 bus = g_udev_device_get_property (udevice, "ID_BUS");
125 if (g_strcmp0 (bus, "usb") == 0)
127 vendor = g_udev_device_get_property (udevice, "ID_VENDOR_ID");
128 if (vendor != NULL)
129 vendor_id = g_ascii_strtoll (vendor, NULL, 16);
130 product = g_udev_device_get_property (udevice, "ID_MODEL_ID");
131 if (product != NULL)
132 product_id = g_ascii_strtoll (product, NULL, 16);
133 if (vendor_id == 0 || product_id == 0)
135 GST_WARNING ("Error getting vendor and product id");
137 else
139 GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id);
142 else
144 GST_INFO ("Not an usb device, skipping vendor and model id retrieval");
147 device_file = g_udev_device_get_device_file (udevice);
148 if (device_file == NULL)
150 GST_WARNING ("Error getting V4L device");
151 return;
154 /* vbi devices support capture capability too, but cannot be used,
155 * so detect them by device name */
156 if (strstr (device_file, "vbi"))
158 GST_INFO ("Skipping vbi device: %s", device_file);
159 return;
162 v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION");
163 if (v4l_version == 2 || v4l_version == 1)
165 const char *caps;
167 caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
168 if (caps == NULL || strstr (caps, ":capture:") == NULL)
170 GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)"
171 "Removing it from device list.", device_file);
172 return;
174 product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT");
176 else if (v4l_version == 0)
178 GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file);
179 return;
181 else
183 g_assert_not_reached ();
186 g_signal_emit (monitor, monitor_signals[ADDED], 0,
187 devpath,
188 device_file,
189 product_name,
190 v4l_version);
193 static void
194 cheese_camera_device_monitor_removed (CheeseCameraDeviceMonitor *monitor,
195 GUdevDevice *udevice)
197 g_signal_emit (monitor, monitor_signals[REMOVED], 0,
198 g_udev_device_get_property (udevice, "DEVPATH"));
201 static void
202 cheese_camera_device_monitor_uevent_cb (GUdevClient *client,
203 const gchar *action,
204 GUdevDevice *udevice,
205 CheeseCameraDeviceMonitor *monitor)
207 if (g_str_equal (action, "remove"))
208 cheese_camera_device_monitor_removed (monitor, udevice);
209 else if (g_str_equal (action, "add"))
210 cheese_camera_device_monitor_added (monitor, udevice);
214 * cheese_camera_device_monitor_coldplug:
215 * @monitor: a #CheeseCameraDeviceMonitor object.
217 * Will actively look for plugged in cameras and emit
218 * ::added for those new cameras.
219 * This is only required when your program starts, so as to connect
220 * to those signals before they are emitted.
222 void
223 cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
225 CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
226 GList *devices, *l;
227 gint i = 0;
229 if (priv->client == NULL)
230 return;
232 GST_INFO ("Probing devices with udev...");
234 devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
236 /* Initialize camera structures */
237 for (l = devices; l != NULL; l = l->next)
239 cheese_camera_device_monitor_added (monitor, l->data);
240 g_object_unref (l->data);
241 i++;
243 g_list_free (devices);
245 if (i == 0) GST_WARNING ("No device found");
248 #else /* HAVE_UDEV */
249 void
250 cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
252 #if 0
253 CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
254 struct v4l2_capability v2cap;
255 struct video_capability v1cap;
256 int fd, ok;
258 if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0)
260 g_warning ("Failed to open %s: %s", device_path, strerror (errno));
261 return;
263 ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
264 if (ok < 0)
266 ok = ioctl (fd, VIDIOCGCAP, &v1cap);
267 if (ok < 0)
269 g_warning ("Error while probing v4l capabilities for %s: %s",
270 device_path, strerror (errno));
271 close (fd);
272 return;
274 g_print ("Detected v4l device: %s\n", v1cap.name);
275 g_print ("Device type: %d\n", v1cap.type);
276 gstreamer_src = "v4lsrc";
277 product_name = v1cap.name;
279 else
281 guint cap = v2cap.capabilities;
282 g_print ("Detected v4l2 device: %s\n", v2cap.card);
283 g_print ("Driver: %s, version: %d\n", v2cap.driver, v2cap.version);
285 /* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */
286 g_print ("Capabilities: 0x%08X\n", v2cap.capabilities);
287 if (!(cap & V4L2_CAP_VIDEO_CAPTURE))
289 g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
290 "Removing it from device list.\n", device_path);
291 close (fd);
292 return;
294 gstreamer_src = "v4l2src";
295 product_name = (char *) v2cap.card;
297 close (fd);
299 GList *devices, *l;
301 g_print ("Probing devices with udev...\n");
303 if (priv->client == NULL)
304 return;
306 devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
308 /* Initialize camera structures */
309 for (l = devices; l != NULL; l = l->next)
311 cheese_camera_device_monitor_added (monitor, l->data);
312 g_object_unref (l->data);
314 g_list_free (devices);
315 #endif
318 #endif /* HAVE_UDEV */
320 static void
321 cheese_camera_device_monitor_finalize (GObject *object)
323 #ifdef HAVE_UDEV
324 CheeseCameraDeviceMonitor *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);
330 priv->client = NULL;
332 #endif /* HAVE_UDEV */
333 G_OBJECT_CLASS (cheese_camera_device_monitor_parent_class)->finalize (object);
336 static void
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),
362 NULL, NULL,
363 _empathy_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),
377 NULL, NULL,
378 g_cclosure_marshal_VOID__STRING,
379 G_TYPE_NONE, 1, G_TYPE_STRING);
381 g_type_class_add_private (klass, sizeof (CheeseCameraDeviceMonitorPrivate));
384 static void
385 cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor *monitor)
387 #ifdef HAVE_UDEV
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