]> git.0d.be Git - empathy.git/blob - libempathy/cheese-camera-device-monitor.c
hindi update
[empathy.git] / libempathy / cheese-camera-device-monitor.c
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>
6  *
7  * Licensed under the GNU General Public License Version 2
8  *
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.
13  *
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.
18  *
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/>.
21  */
22 #ifdef HAVE_CONFIG_H
23   #include <config.h>
24 #endif
25
26 #include <glib-object.h>
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 #include "empathy-marshal.h"
47
48 /**
49  * SECTION:cheese-camera-device-monitor
50  * @short_description: Simple object to enumerate v4l devices
51  * @include: cheese/cheese-camera-device-monitor.h
52  *
53  * #CheeseCameraDeviceMonitor provides a basic interface for
54  * video4linux device enumeration and hotplugging.
55  *
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.
61  */
62
63 G_DEFINE_TYPE (CheeseCameraDeviceMonitor, cheese_camera_device_monitor, G_TYPE_OBJECT)
64
65 #define CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),                               \
66                                                                                   CHEESE_TYPE_CAMERA_DEVICE_MONITOR, \
67                                                                                   CheeseCameraDeviceMonitorPrivate))
68
69 #define CHEESE_CAMERA_DEVICE_MONITOR_ERROR cheese_camera_device_monitor_error_quark ()
70
71 GST_DEBUG_CATEGORY (cheese_device_monitor_cat);
72 #define GST_CAT_DEFAULT cheese_device_monitor_cat
73
74 enum CheeseCameraDeviceMonitorError
75 {
76   CHEESE_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN,
77   CHEESE_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND
78 };
79
80 typedef struct
81 {
82 #ifdef HAVE_UDEV
83   GUdevClient *client;
84 #else
85   guint filler;
86 #endif /* HAVE_UDEV */
87 } CheeseCameraDeviceMonitorPrivate;
88
89 enum
90 {
91   ADDED,
92   REMOVED,
93   LAST_SIGNAL
94 };
95
96 static guint monitor_signals[LAST_SIGNAL];
97
98 #if 0
99 GQuark
100 cheese_camera_device_monitor_error_quark (void)
101 {
102   return g_quark_from_static_string ("cheese-camera-error-quark");
103 }
104 #endif
105
106 #ifdef HAVE_UDEV
107 static void
108 cheese_camera_device_monitor_added (CheeseCameraDeviceMonitor *monitor,
109                                     GUdevDevice               *udevice)
110 {
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;
119
120   const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH");
121
122   GST_INFO ("Checking udev device '%s'", devpath);
123
124   bus = g_udev_device_get_property (udevice, "ID_BUS");
125   if (g_strcmp0 (bus, "usb") == 0)
126   {
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)
134     {
135       GST_WARNING ("Error getting vendor and product id");
136     }
137     else
138     {
139       GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id);
140     }
141   }
142   else
143   {
144     GST_INFO ("Not an usb device, skipping vendor and model id retrieval");
145   }
146
147   device_file = g_udev_device_get_device_file (udevice);
148   if (device_file == NULL)
149   {
150     GST_WARNING ("Error getting V4L device");
151     return;
152   }
153
154   /* vbi devices support capture capability too, but cannot be used,
155    * so detect them by device name */
156   if (strstr (device_file, "vbi"))
157   {
158     GST_INFO ("Skipping vbi device: %s", device_file);
159     return;
160   }
161
162   v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION");
163   if (v4l_version == 2 || v4l_version == 1)
164   {
165     const char *caps;
166
167     caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
168     if (caps == NULL || strstr (caps, ":capture:") == NULL)
169     {
170       GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)"
171                    "Removing it from device list.", device_file);
172       return;
173     }
174     product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT");
175   }
176   else if (v4l_version == 0)
177   {
178     GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file);
179     return;
180   }
181   else
182   {
183     g_assert_not_reached ();
184   }
185
186   g_signal_emit (monitor, monitor_signals[ADDED], 0,
187                  devpath,
188                  device_file,
189                  product_name,
190                  v4l_version);
191 }
192
193 static void
194 cheese_camera_device_monitor_removed (CheeseCameraDeviceMonitor *monitor,
195                                       GUdevDevice               *udevice)
196 {
197   g_signal_emit (monitor, monitor_signals[REMOVED], 0,
198                  g_udev_device_get_property (udevice, "DEVPATH"));
199 }
200
201 static void
202 cheese_camera_device_monitor_uevent_cb (GUdevClient               *client,
203                                         const gchar               *action,
204                                         GUdevDevice               *udevice,
205                                         CheeseCameraDeviceMonitor *monitor)
206 {
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);
211 }
212
213 /**
214  * cheese_camera_device_monitor_coldplug:
215  * @monitor: a #CheeseCameraDeviceMonitor object.
216  *
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.
221  */
222 void
223 cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
224 {
225   CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
226   GList                            *devices, *l;
227   gint                              i = 0;
228
229   if (priv->client == NULL)
230     return;
231
232   GST_INFO ("Probing devices with udev...");
233
234   devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
235
236   /* Initialize camera structures */
237   for (l = devices; l != NULL; l = l->next)
238   {
239     cheese_camera_device_monitor_added (monitor, l->data);
240     g_object_unref (l->data);
241     i++;
242   }
243   g_list_free (devices);
244
245   if (i == 0) GST_WARNING ("No device found");
246 }
247
248 #else /* HAVE_UDEV */
249 void
250 cheese_camera_device_monitor_coldplug (CheeseCameraDeviceMonitor *monitor)
251 {
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;
257
258   if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0)
259   {
260     g_warning ("Failed to open %s: %s", device_path, strerror (errno));
261     return;
262   }
263   ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
264   if (ok < 0)
265   {
266     ok = ioctl (fd, VIDIOCGCAP, &v1cap);
267     if (ok < 0)
268     {
269       g_warning ("Error while probing v4l capabilities for %s: %s",
270                  device_path, strerror (errno));
271       close (fd);
272       return;
273     }
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;
278   }
279   else
280   {
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);
284
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))
288     {
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;
293     }
294     gstreamer_src = "v4l2src";
295     product_name  = (char *) v2cap.card;
296   }
297   close (fd);
298
299   GList *devices, *l;
300
301   g_print ("Probing devices with udev...\n");
302
303   if (priv->client == NULL)
304     return;
305
306   devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
307
308   /* Initialize camera structures */
309   for (l = devices; l != NULL; l = l->next)
310   {
311     cheese_camera_device_monitor_added (monitor, l->data);
312     g_object_unref (l->data);
313   }
314   g_list_free (devices);
315   #endif
316 }
317
318 #endif /* HAVE_UDEV */
319
320 static void
321 cheese_camera_device_monitor_finalize (GObject *object)
322 {
323 #ifdef HAVE_UDEV
324   CheeseCameraDeviceMonitor *monitor = CHEESE_CAMERA_DEVICE_MONITOR (object);
325   CheeseCameraDeviceMonitorPrivate *priv = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
326
327   if (priv->client != NULL)
328   {
329     g_object_unref (priv->client);
330     priv->client = NULL;
331   }
332 #endif /* HAVE_UDEV */
333   G_OBJECT_CLASS (cheese_camera_device_monitor_parent_class)->finalize (object);
334 }
335
336 static void
337 cheese_camera_device_monitor_class_init (CheeseCameraDeviceMonitorClass *klass)
338 {
339   GObjectClass *object_class = G_OBJECT_CLASS (klass);
340
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");
345
346   object_class->finalize = cheese_camera_device_monitor_finalize;
347
348   /**
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.
355    *
356    * The ::added signal is emitted when a camera is added, or on start-up
357    * after #cheese_camera_device_monitor_colplug is called.
358    **/
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);
365
366   /**
367    * CheeseCameraDeviceMonitor::removed:
368    * @device: A private object representing the newly added camera
369    * @id: Device unique identifier.
370    *
371    * The ::removed signal is emitted when a camera is un-plugged, or
372    * disabled on the system.
373    **/
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);
380
381   g_type_class_add_private (klass, sizeof (CheeseCameraDeviceMonitorPrivate));
382 }
383
384 static void
385 cheese_camera_device_monitor_init (CheeseCameraDeviceMonitor *monitor)
386 {
387 #ifdef HAVE_UDEV
388   CheeseCameraDeviceMonitorPrivate *priv         = CHEESE_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
389   const gchar *const                subsystems[] = {"video4linux", NULL};
390
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 */
395 }
396
397 /**
398  * cheese_camera_device_monitor_new:
399  *
400  * Returns a new #CheeseCameraDeviceMonitor object.
401  *
402  * Return value: a new #CheeseCameraDeviceMonitor object.
403  **/
404 CheeseCameraDeviceMonitor *
405 cheese_camera_device_monitor_new (void)
406 {
407   return g_object_new (CHEESE_TYPE_CAMERA_DEVICE_MONITOR, NULL);
408 }
409
410 /*
411  * vim: sw=2 ts=8 cindent noai bs=2
412  */