]> git.0d.be Git - empathy.git/blob - libempathy/cheese-camera-device-monitor.c
account-settings: allow to change the service
[empathy.git] / libempathy / cheese-camera-device-monitor.c
1 /* This file is a copy of cheese-camera-device-monitor.c from Empathy. 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  * Licensed under the GNU General Public License Version 2
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
23  */
24 #ifdef HAVE_CONFIG_H
25   #include <config.h>
26 #endif
27
28 #include <glib-object.h>
29 #include <string.h>
30
31 #ifdef HAVE_UDEV
32   #define G_UDEV_API_IS_SUBJECT_TO_CHANGE 1
33   #include <gudev/gudev.h>
34 #else
35   #include <fcntl.h>
36   #include <unistd.h>
37   #include <sys/ioctl.h>
38   #if USE_SYS_VIDEOIO_H > 0
39     #include <sys/types.h>
40     #include <sys/videoio.h>
41   #elif defined (__sun)
42     #include <sys/types.h>
43     #include <sys/videodev2.h>
44   #endif /* USE_SYS_VIDEOIO_H */
45 #endif
46
47 #include "cheese-camera-device-monitor.h"
48
49 /**
50  * SECTION:cheese-camera-device-monitor
51  * @short_description: Simple object to enumerate v4l devices
52  * @include: cheese/cheese-camera-device-monitor.h
53  *
54  * #EmpathyCameraDeviceMonitor provides a basic interface for
55  * video4linux device enumeration and hotplugging.
56  *
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  * EmpathyCameraDeviceMonitor::added or
61  * EmpathyCameraDeviceMonitor::removed signal when an event happens.
62  */
63
64 G_DEFINE_TYPE (EmpathyCameraDeviceMonitor, empathy_camera_device_monitor, G_TYPE_OBJECT)
65
66 #define EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),                               \
67                                                                                   EMPATHY_TYPE_CAMERA_DEVICE_MONITOR, \
68                                                                                   EmpathyCameraDeviceMonitorPrivate))
69
70 #define EMPATHY_CAMERA_DEVICE_MONITOR_ERROR empathy_camera_device_monitor_error_quark ()
71
72 GST_DEBUG_CATEGORY (empathy_device_monitor_cat);
73 #define GST_CAT_DEFAULT empathy_device_monitor_cat
74
75 enum EmpathyCameraDeviceMonitorError
76 {
77   EMPATHY_CAMERA_DEVICE_MONITOR_ERROR_UNKNOWN,
78   EMPATHY_CAMERA_DEVICE_MONITOR_ERROR_ELEMENT_NOT_FOUND
79 };
80
81 typedef struct
82 {
83 #ifdef HAVE_UDEV
84   GUdevClient *client;
85 #else
86   guint filler;
87 #endif /* HAVE_UDEV */
88 } EmpathyCameraDeviceMonitorPrivate;
89
90 enum
91 {
92   ADDED,
93   REMOVED,
94   LAST_SIGNAL
95 };
96
97 static guint monitor_signals[LAST_SIGNAL];
98
99 #if 0
100 GQuark
101 empathy_camera_device_monitor_error_quark (void)
102 {
103   return g_quark_from_static_string ("empathy-camera-error-quark");
104 }
105 #endif
106
107 #ifdef HAVE_UDEV
108 static void
109 empathy_camera_device_monitor_added (EmpathyCameraDeviceMonitor *monitor,
110                                     GUdevDevice               *udevice)
111 {
112   const char *device_file;
113   const char *product_name;
114   const char *vendor;
115   const char *product;
116   const char *bus;
117   gint        vendor_id   = 0;
118   gint        product_id  = 0;
119   gint        v4l_version = 0;
120
121   const gchar *devpath = g_udev_device_get_property (udevice, "DEVPATH");
122
123   GST_INFO ("Checking udev device '%s'", devpath);
124
125   bus = g_udev_device_get_property (udevice, "ID_BUS");
126   if (g_strcmp0 (bus, "usb") == 0)
127   {
128     vendor = g_udev_device_get_property (udevice, "ID_VENDOR_ID");
129     if (vendor != NULL)
130       vendor_id = g_ascii_strtoll (vendor, NULL, 16);
131     product = g_udev_device_get_property (udevice, "ID_MODEL_ID");
132     if (product != NULL)
133       product_id = g_ascii_strtoll (product, NULL, 16);
134     if (vendor_id == 0 || product_id == 0)
135     {
136       GST_WARNING ("Error getting vendor and product id");
137     }
138     else
139     {
140       GST_INFO ("Found device %04x:%04x, getting capabilities...", vendor_id, product_id);
141     }
142   }
143   else
144   {
145     GST_INFO ("Not an usb device, skipping vendor and model id retrieval");
146   }
147
148   device_file = g_udev_device_get_device_file (udevice);
149   if (device_file == NULL)
150   {
151     GST_WARNING ("Error getting V4L device");
152     return;
153   }
154
155   /* vbi devices support capture capability too, but cannot be used,
156    * so detect them by device name */
157   if (strstr (device_file, "vbi"))
158   {
159     GST_INFO ("Skipping vbi device: %s", device_file);
160     return;
161   }
162
163   v4l_version = g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION");
164   if (v4l_version == 2 || v4l_version == 1)
165   {
166     const char *caps;
167
168     caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
169     if (caps == NULL || strstr (caps, ":capture:") == NULL)
170     {
171       GST_WARNING ("Device %s seems to not have the capture capability, (radio tuner?)"
172                    "Removing it from device list.", device_file);
173       return;
174     }
175     product_name = g_udev_device_get_property (udevice, "ID_V4L_PRODUCT");
176   }
177   else if (v4l_version == 0)
178   {
179     GST_ERROR ("Fix your udev installation to include v4l_id, ignoring %s", device_file);
180     return;
181   }
182   else
183   {
184     g_assert_not_reached ();
185   }
186
187   g_signal_emit (monitor, monitor_signals[ADDED], 0,
188                  devpath,
189                  device_file,
190                  product_name,
191                  v4l_version);
192 }
193
194 static void
195 empathy_camera_device_monitor_removed (EmpathyCameraDeviceMonitor *monitor,
196                                       GUdevDevice               *udevice)
197 {
198   g_signal_emit (monitor, monitor_signals[REMOVED], 0,
199                  g_udev_device_get_property (udevice, "DEVPATH"));
200 }
201
202 static void
203 empathy_camera_device_monitor_uevent_cb (GUdevClient               *client,
204                                         const gchar               *action,
205                                         GUdevDevice               *udevice,
206                                         EmpathyCameraDeviceMonitor *monitor)
207 {
208   if (g_str_equal (action, "remove"))
209     empathy_camera_device_monitor_removed (monitor, udevice);
210   else if (g_str_equal (action, "add"))
211     empathy_camera_device_monitor_added (monitor, udevice);
212 }
213
214 /**
215  * empathy_camera_device_monitor_coldplug:
216  * @monitor: a #EmpathyCameraDeviceMonitor object.
217  *
218  * Will actively look for plugged in cameras and emit
219  * ::added for those new cameras.
220  * This is only required when your program starts, so as to connect
221  * to those signals before they are emitted.
222  */
223 void
224 empathy_camera_device_monitor_coldplug (EmpathyCameraDeviceMonitor *monitor)
225 {
226   EmpathyCameraDeviceMonitorPrivate *priv = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
227   GList                            *devices, *l;
228   gint                              i = 0;
229
230   if (priv->client == NULL)
231     return;
232
233   GST_INFO ("Probing devices with udev...");
234
235   devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
236
237   /* Initialize camera structures */
238   for (l = devices; l != NULL; l = l->next)
239   {
240     empathy_camera_device_monitor_added (monitor, l->data);
241     g_object_unref (l->data);
242     i++;
243   }
244   g_list_free (devices);
245
246   if (i == 0) GST_WARNING ("No device found");
247 }
248
249 #else /* HAVE_UDEV */
250 void
251 empathy_camera_device_monitor_coldplug (EmpathyCameraDeviceMonitor *monitor)
252 {
253   #if 0
254   EmpathyCameraDeviceMonitorPrivate *priv = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
255   struct v4l2_capability            v2cap;
256   struct video_capability           v1cap;
257   int                               fd, ok;
258
259   if ((fd = open (device_path, O_RDONLY | O_NONBLOCK)) < 0)
260   {
261     g_warning ("Failed to open %s: %s", device_path, strerror (errno));
262     return;
263   }
264   ok = ioctl (fd, VIDIOC_QUERYCAP, &v2cap);
265   if (ok < 0)
266   {
267     ok = ioctl (fd, VIDIOCGCAP, &v1cap);
268     if (ok < 0)
269     {
270       g_warning ("Error while probing v4l capabilities for %s: %s",
271                  device_path, strerror (errno));
272       close (fd);
273       return;
274     }
275     g_print ("Detected v4l device: %s\n", v1cap.name);
276     g_print ("Device type: %d\n", v1cap.type);
277     gstreamer_src = "v4lsrc";
278     product_name  = v1cap.name;
279   }
280   else
281   {
282     guint cap = v2cap.capabilities;
283     g_print ("Detected v4l2 device: %s\n", v2cap.card);
284     g_print ("Driver: %s, version: %d\n", v2cap.driver, v2cap.version);
285
286     /* g_print ("Bus info: %s\n", v2cap.bus_info); */ /* Doesn't seem anything useful */
287     g_print ("Capabilities: 0x%08X\n", v2cap.capabilities);
288     if (!(cap & V4L2_CAP_VIDEO_CAPTURE))
289     {
290       g_print ("Device %s seems to not have the capture capability, (radio tuner?)\n"
291                "Removing it from device list.\n", device_path);
292       close (fd);
293       return;
294     }
295     gstreamer_src = "v4l2src";
296     product_name  = (char *) v2cap.card;
297   }
298   close (fd);
299
300   GList *devices, *l;
301
302   g_print ("Probing devices with udev...\n");
303
304   if (priv->client == NULL)
305     return;
306
307   devices = g_udev_client_query_by_subsystem (priv->client, "video4linux");
308
309   /* Initialize camera structures */
310   for (l = devices; l != NULL; l = l->next)
311   {
312     empathy_camera_device_monitor_added (monitor, l->data);
313     g_object_unref (l->data);
314   }
315   g_list_free (devices);
316   #endif
317 }
318
319 #endif /* HAVE_UDEV */
320
321 static void
322 empathy_camera_device_monitor_finalize (GObject *object)
323 {
324 #ifdef HAVE_UDEV
325   EmpathyCameraDeviceMonitor *monitor = EMPATHY_CAMERA_DEVICE_MONITOR (object);
326   EmpathyCameraDeviceMonitorPrivate *priv = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
327
328   if (priv->client != NULL)
329   {
330     g_object_unref (priv->client);
331     priv->client = NULL;
332   }
333 #endif /* HAVE_UDEV */
334   G_OBJECT_CLASS (empathy_camera_device_monitor_parent_class)->finalize (object);
335 }
336
337 static void
338 empathy_camera_device_monitor_class_init (EmpathyCameraDeviceMonitorClass *klass)
339 {
340   GObjectClass *object_class = G_OBJECT_CLASS (klass);
341
342   if (empathy_device_monitor_cat == NULL)
343     GST_DEBUG_CATEGORY_INIT (empathy_device_monitor_cat,
344                              "empathy-device-monitor",
345                              0, "Empathy Camera Device Monitor");
346
347   object_class->finalize = empathy_camera_device_monitor_finalize;
348
349   /**
350    * EmpathyCameraDeviceMonitor::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.
356    *
357    * The ::added signal is emitted when a camera is added, or on start-up
358    * after #empathy_camera_device_monitor_colplug is called.
359    **/
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 (EmpathyCameraDeviceMonitorClass, added),
363                                          NULL, NULL,
364                                          g_cclosure_marshal_generic,
365                                          G_TYPE_NONE, 4, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INT);
366
367   /**
368    * EmpathyCameraDeviceMonitor::removed:
369    * @device: A private object representing the newly added camera
370    * @id: Device unique identifier.
371    *
372    * The ::removed signal is emitted when a camera is un-plugged, or
373    * disabled on the system.
374    **/
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 (EmpathyCameraDeviceMonitorClass, removed),
378                                            NULL, NULL,
379                                            g_cclosure_marshal_generic,
380                                            G_TYPE_NONE, 1, G_TYPE_STRING);
381
382   g_type_class_add_private (klass, sizeof (EmpathyCameraDeviceMonitorPrivate));
383 }
384
385 static void
386 empathy_camera_device_monitor_init (EmpathyCameraDeviceMonitor *monitor)
387 {
388 #ifdef HAVE_UDEV
389   EmpathyCameraDeviceMonitorPrivate *priv         = EMPATHY_CAMERA_DEVICE_MONITOR_GET_PRIVATE (monitor);
390   const gchar *const                subsystems[] = {"video4linux", NULL};
391
392   priv->client = g_udev_client_new (subsystems);
393   g_signal_connect (G_OBJECT (priv->client), "uevent",
394                     G_CALLBACK (empathy_camera_device_monitor_uevent_cb), monitor);
395 #endif /* HAVE_UDEV */
396 }
397
398 /**
399  * empathy_camera_device_monitor_new:
400  *
401  * Returns a new #EmpathyCameraDeviceMonitor object.
402  *
403  * Return value: a new #EmpathyCameraDeviceMonitor object.
404  **/
405 EmpathyCameraDeviceMonitor *
406 empathy_camera_device_monitor_new (void)
407 {
408   return g_object_new (EMPATHY_TYPE_CAMERA_DEVICE_MONITOR, NULL);
409 }
410
411 /*
412  * vim: sw=2 ts=8 cindent noai bs=2
413  */