]> git.0d.be Git - empathy.git/blob - tp-account-widgets/cheese-camera-device-monitor.c
tp-account-widgets: use TpAW's debugging functions instead of Empathy's
[empathy.git] / tp-account-widgets / cheese-camera-device-monitor.c
1 /* This file is a copy of cheese-camera-device-monitor.c from Tpaw. We
2  * just renamespaced it to avoid conflicts when linking on libcheese. */
3 /*
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>
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library 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 GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
22  */
23 #ifdef HAVE_CONFIG_H
24   #include "config.h"
25 #endif
26
27 #include <string.h>
28
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
44
45 #include "cheese-camera-device-monitor.h"
46
47 /**
48  * SECTION:cheese-camera-device-monitor
49  * @short_description: Simple object to enumerate v4l devices
50  * @include: cheese/cheese-camera-device-monitor.h
51  *
52  * #TpawCameraDeviceMonitor provides a basic interface for
53  * video4linux device enumeration and hotplugging.
54  *
55  * It uses either GUdev or some platform specific code to list video
56  * devices.  It is also capable (right now in linux only, with the
57  * udev backend) to monitor device plugging and emit a
58  * TpawCameraDeviceMonitor::added or
59  * TpawCameraDeviceMonitor::removed signal when an event happens.
60  */
61
62 G_DEFINE_TYPE (TpawCameraDeviceMonitor, tpaw_camera_device_monitor, G_TYPE_OBJECT)
63
64 #define TPAW_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),                               \
65                                                                                   TPAW_TYPE_CAMERA_DEVICE_MONITOR, \
66                                                                                   TpawCameraDeviceMonitorPrivate))
67
68 #define TPAW_CAMERA_DEVICE_MONITOR_ERROR tpaw_camera_device_monitor_error_quark ()
69
70 #define DEBUG_FLAG TPAW_DEBUG_OTHER
71 #include "tpaw-debug.h"
72
73 enum TpawCameraDeviceMonitorError
74 {
75   TPAW_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN,
76   TPAW_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND
77 };
78
79 typedef struct
80 {
81 #ifdef HAVE_UDEV
82   GUdevClient *client;
83 #else
84   guint filler;
85 #endif /* HAVE_UDEV */
86 } TpawCameraDeviceMonitorPrivate;
87
88 enum
89 {
90   ADDED,
91   REMOVED,
92   LAST_SIGNAL
93 };
94
95 static guint monitor_signals[LAST_SIGNAL];
96
97 #if 0
98 GQuark
99 tpaw_camera_device_monitor_error_quark (void)
100 {
101   return g_quark_from_static_string ("tpaw-camera-error-quark");
102 }
103 #endif
104
105 #ifdef HAVE_UDEV
106 static void
107 tpaw_camera_device_monitor_added (TpawCameraDeviceMonitor *monitor,
108                                     GUdevDevice               *udevice)
109 {
110   const char *device_file;
111   const char *product_name;
112   const char *vendor;
113   const char *product;
114   const char *bus;
115   gint        vendor_id   = 0;
116   gint        product_id  = 0;
117   gint        v4l_version = 0;
118
119   const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH");
120
121   DEBUG ("Checking udev device '%s'", devpath);
122
123   bus = g_udev_device_get_property (udevice, "ID_BUS");
124   if (g_strcmp0 (bus, "usb") == 0)
125   {
126     vendor = g_udev_device_get_property (udevice, "ID_VENDOR_ID");
127     if (vendor != NULL)
128       vendor_id = g_ascii_strtoll (vendor, NULL, 16);
129     product = g_udev_device_get_property (udevice, "ID_MODEL_ID");
130     if (product != NULL)
131       product_id = g_ascii_strtoll (product, NULL, 16);
132     if (vendor_id == 0 || product_id == 0)
133     {
134       DEBUG ("Error getting vendor and product id");
135     }
136     else
137     {
138       DEBUG ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id);
139     }
140   }
141   else
142   {
143     DEBUG ("Not an usb device, skipping vendor and model id retrieval");
144   }
145
146   device_file = g_udev_device_get_device_file (udevice);
147   if (device_file == NULL)
148   {
149     DEBUG ("Error getting V4L device");
150     return;
151   }
152
153   /* vbi devices support capture capability too, but cannot be used,
154    * so detect them by device name */
155   if (strstr (device_file, "vbi"))
156   {
157     DEBUG ("Skipping vbi device: %s", device_file);
158     return;
159   }
160
161   v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION");
162   if (v4l_version == 2 || v4l_version == 1)
163   {
164     const char *caps;
165
166     caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
167     if (caps == NULL || strstr (caps, ":capture:") == NULL)
168     {
169       DEBUG ("Device %s seems to not have the capture capability, (radio tuner?)"
170                    "Removing it from device list.", device_file);
171       return;
172     }
173     product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT");
174   }
175   else if (v4l_version == 0)
176   {
177     DEBUG ("Fix your udev installation to include v4l_id, ignoring %s", device_file);
178     return;
179   }
180   else
181   {
182     g_assert_not_reached ();
183   }
184
185   g_signal_emit (monitor, monitor_signals[ADDED], 0,
186                  devpath,
187                  device_file,
188                  product_name,
189                  v4l_version);
190 }
191
192 static void
193 tpaw_camera_device_monitor_removed (TpawCameraDeviceMonitor *monitor,
194                                       GUdevDevice               *udevice)
195 {
196   g_signal_emit (monitor, monitor_signals[REMOVED], 0,
197                  g_udev_device_get_property (udevice, "DEVPATH"));
198 }
199
200 static void
201 tpaw_camera_device_monitor_uevent_cb (GUdevClient               *client,
202                                         const gchar               *action,
203                                         GUdevDevice               *udevice,
204                                         TpawCameraDeviceMonitor *monitor)
205 {
206   if (g_str_equal (action, "remove"))
207     tpaw_camera_device_monitor_removed (monitor, udevice);
208   else if (g_str_equal (action, "add"))
209     tpaw_camera_device_monitor_added (monitor, udevice);
210 }
211
212 /**
213  * tpaw_camera_device_monitor_coldplug:
214  * @monitor: a #TpawCameraDeviceMonitor object.
215  *
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.
220  */
221 void
222 tpaw_camera_device_monitor_coldplug (TpawCameraDeviceMonitor *monitor)
223 {
224   TpawCameraDeviceMonitorPrivate *priv = TPAW_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
225   GList                            *devices, *l;
226   gint                              i = 0;
227
228   if (priv->client == NULL)
229     return;
230
231   DEBUG ("Probing devices with udev...");
232
233   devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
234
235   /* Initialize camera structures */
236   for (l = devices; l != NULL; l = l->next)
237   {
238     tpaw_camera_device_monitor_added (monitor, l->data);
239     g_object_unref (l->data);
240     i++;
241   }
242   g_list_free (devices);
243
244   if (i == 0) DEBUG ("No device found");
245 }
246
247 #else /* HAVE_UDEV */
248 void
249 tpaw_camera_device_monitor_coldplug (TpawCameraDeviceMonitor *monitor)
250 {
251   #if 0
252   TpawCameraDeviceMonitorPrivate *priv = TPAW_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
253   struct v4l2_capability            v2cap;
254   struct video_capability           v1cap;
255   int                               fd, ok;
256
257   if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0)
258   {
259     g_warning ("Failed to open %s: %s", device_path, strerror (errno));
260     return;
261   }
262   ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
263   if (ok < 0)
264   {
265     ok = ioctl (fd, VIDIOCGCAP, &v1cap);
266     if (ok < 0)
267     {
268       g_warning ("Error while probing v4l capabilities for %s: %s",
269                  device_path, strerror (errno));
270       close (fd);
271       return;
272     }
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;
277   }
278   else
279   {
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);
283
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))
287     {
288       g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
289                "Removing it from device list.\n", device_path);
290       close (fd);
291       return;
292     }
293     gstreamer_src = "v4l2src";
294     product_name  = (char *) v2cap.card;
295   }
296   close (fd);
297
298   GList *devices, *l;
299
300   g_print ("Probing devices with udev...\n");
301
302   if (priv->client == NULL)
303     return;
304
305   devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
306
307   /* Initialize camera structures */
308   for (l = devices; l != NULL; l = l->next)
309   {
310     tpaw_camera_device_monitor_added (monitor, l->data);
311     g_object_unref (l->data);
312   }
313   g_list_free (devices);
314   #endif
315 }
316
317 #endif /* HAVE_UDEV */
318
319 static void
320 tpaw_camera_device_monitor_finalize (GObject *object)
321 {
322 #ifdef HAVE_UDEV
323   TpawCameraDeviceMonitor *monitor = TPAW_CAMERA_DEVICE_MONITOR (object);
324   TpawCameraDeviceMonitorPrivate *priv = TPAW_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
325
326   if (priv->client != NULL)
327   {
328     g_object_unref (priv->client);
329     priv->client = NULL;
330   }
331 #endif /* HAVE_UDEV */
332   G_OBJECT_CLASS (tpaw_camera_device_monitor_parent_class)->finalize (object);
333 }
334
335 static void
336 tpaw_camera_device_monitor_class_init (TpawCameraDeviceMonitorClass *klass)
337 {
338   GObjectClass *object_class = G_OBJECT_CLASS (klass);
339
340   object_class->finalize = tpaw_camera_device_monitor_finalize;
341
342   /**
343    * TpawCameraDeviceMonitor::added:
344    * @device: A private object representing the newly added camera.
345    * @id: Device unique identifier.
346    * @device: Device file name  (e.g. /dev/video2).
347    * @product_name: Device product name (human readable, intended to be displayed in a UI).
348    * @api_version: Supported video4linux API: 1 for v4l, 2 for v4l2.
349    *
350    * The ::added signal is emitted when a camera is added, or on start-up
351    * after #tpaw_camera_device_monitor_colplug is called.
352    **/
353   monitor_signals[ADDED] = g_signal_new ("added", G_OBJECT_CLASS_TYPE (klass),
354                                          G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
355                                          G_STRUCT_OFFSET (TpawCameraDeviceMonitorClass, added),
356                                          NULL, NULL,
357                                          g_cclosure_marshal_generic,
358                                          G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
359
360   /**
361    * TpawCameraDeviceMonitor::removed:
362    * @device: A private object representing the newly added camera
363    * @id: Device unique identifier.
364    *
365    * The ::removed signal is emitted when a camera is un-plugged, or
366    * disabled on the system.
367    **/
368   monitor_signals[REMOVED] = g_signal_new ("removed", G_OBJECT_CLASS_TYPE (klass),
369                                            G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
370                                            G_STRUCT_OFFSET (TpawCameraDeviceMonitorClass, removed),
371                                            NULL, NULL,
372                                            g_cclosure_marshal_generic,
373                                            G_TYPE_NONE, 1, G_TYPE_STRING);
374
375   g_type_class_add_private (klass, sizeof (TpawCameraDeviceMonitorPrivate));
376 }
377
378 static void
379 tpaw_camera_device_monitor_init (TpawCameraDeviceMonitor *monitor)
380 {
381 #ifdef HAVE_UDEV
382   TpawCameraDeviceMonitorPrivate *priv         = TPAW_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
383   const gchar *const                subsystems[] = {"video4linux", NULL};
384
385   priv->client = g_udev_client_new (subsystems);
386   g_signal_connect (G_OBJECT (priv->client), "uevent",
387                     G_CALLBACK (tpaw_camera_device_monitor_uevent_cb), monitor);
388 #endif /* HAVE_UDEV */
389 }
390
391 /**
392  * tpaw_camera_device_monitor_new:
393  *
394  * Returns a new #TpawCameraDeviceMonitor object.
395  *
396  * Return value: a new #TpawCameraDeviceMonitor object.
397  **/
398 TpawCameraDeviceMonitor *
399 tpaw_camera_device_monitor_new (void)
400 {
401   return g_object_new (TPAW_TYPE_CAMERA_DEVICE_MONITOR, NULL);
402 }
403
404 /*
405  * vim: sw=2 ts=8 cindent noai bs=2
406  */