1 /*
2 * Clutter-GStreamer.
3 *
4 * GStreamer integration library for Clutter.
5 *
6 * clutter-gst-camera-manager.c - a component to list available cameras
7 *
8 * Authored By Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
9 *
10 * Copyright (C) 2013 Intel Corporation
11 *
12 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2 of the License, or (at your option) any later version.
16 *
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
21 *
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the
24 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25 * Boston, MA 02111-1307, USA.
26 */
27
28 /**
29 * SECTION:clutter-gst-camera-manager
30 * @short_description: A component to list available cameras.
31 *
32 * #ClutterGstCameraManager lists available cameras.
33 */
34
35 #ifdef HAVE_CONFIG_H
36 #include "config.h"
37 #endif
38
39 #include <string.h>
40
41 #include <gst/gst.h>
42 #ifdef HAVE_GUDEV
43 # include <gudev/gudev.h>
44 #endif
45
46 #include "clutter-gst-camera-manager.h"
47 #include "clutter-gst-camera-device.h"
48
49 G_DEFINE_TYPE (ClutterGstCameraManager, clutter_gst_camera_manager, G_TYPE_OBJECT)
50
51 #define GST_CAMERA_MANAGER_PRIVATE(o) \
52 (G_TYPE_INSTANCE_GET_PRIVATE ((o), CLUTTER_GST_TYPE_CAMERA_MANAGER, ClutterGstCameraManagerPrivate))
53
54 struct _ClutterGstCameraManagerPrivate
55 {
56 GPtrArray *camera_devices;
57 #ifdef HAVE_GUDEV
58 GUdevClient *udev_client;
59 #endif
60 };
61
62 enum
63 {
64 CAMERA_ADDED,
65 CAMERA_REMOVED,
66 LAST_SIGNAL
67 };
68
69 static int signals[LAST_SIGNAL] = { 0 };
70
71 static void
clutter_gst_camera_manager_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)72 clutter_gst_camera_manager_get_property (GObject *object,
73 guint property_id,
74 GValue *value,
75 GParamSpec *pspec)
76 {
77 switch (property_id)
78 {
79 default:
80 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
81 }
82 }
83
84 static void
clutter_gst_camera_manager_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)85 clutter_gst_camera_manager_set_property (GObject *object,
86 guint property_id,
87 const GValue *value,
88 GParamSpec *pspec)
89 {
90 switch (property_id)
91 {
92 default:
93 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
94 }
95 }
96
97 static void
clutter_gst_camera_manager_dispose(GObject * object)98 clutter_gst_camera_manager_dispose (GObject *object)
99 {
100 ClutterGstCameraManagerPrivate *priv = CLUTTER_GST_CAMERA_MANAGER (object)->priv;
101
102 if (priv->camera_devices)
103 {
104 g_ptr_array_unref (priv->camera_devices);
105 priv->camera_devices = NULL;
106 }
107
108 #if HAVE_GUDEV
109 g_clear_object (&priv->udev_client);
110 #endif
111
112 G_OBJECT_CLASS (clutter_gst_camera_manager_parent_class)->dispose (object);
113 }
114
115 static void
clutter_gst_camera_manager_finalize(GObject * object)116 clutter_gst_camera_manager_finalize (GObject *object)
117 {
118 G_OBJECT_CLASS (clutter_gst_camera_manager_parent_class)->finalize (object);
119 }
120
121 static void
clutter_gst_camera_manager_class_init(ClutterGstCameraManagerClass * klass)122 clutter_gst_camera_manager_class_init (ClutterGstCameraManagerClass *klass)
123 {
124 GObjectClass *object_class = G_OBJECT_CLASS (klass);
125
126 g_type_class_add_private (klass, sizeof (ClutterGstCameraManagerPrivate));
127
128 object_class->get_property = clutter_gst_camera_manager_get_property;
129 object_class->set_property = clutter_gst_camera_manager_set_property;
130 object_class->dispose = clutter_gst_camera_manager_dispose;
131 object_class->finalize = clutter_gst_camera_manager_finalize;
132
133 /**
134 * ClutterGstCameraManager::camera-added:
135 * @self: the actor camera manager
136 * @camera_device: a camera device added
137 *
138 * The ::camera-added signal is emitted whenever a new camera device
139 * is available.
140 */
141 signals[CAMERA_ADDED] =
142 g_signal_new ("camera-added",
143 G_TYPE_FROM_CLASS (object_class),
144 G_SIGNAL_RUN_LAST,
145 0,
146 NULL, NULL,
147 g_cclosure_marshal_VOID__OBJECT,
148 G_TYPE_NONE, 1,
149 CLUTTER_GST_TYPE_CAMERA_DEVICE);
150
151 /**
152 * ClutterGstCameraManager::camera-removed:
153 * @self: the actor camera manager
154 * @camera_device: a camera device
155 *
156 * The ::camera-removed signal is emitted whenever a camera device
157 * is unplugged/removed from the system.
158 */
159 signals[CAMERA_REMOVED] =
160 g_signal_new ("camera-removed",
161 G_TYPE_FROM_CLASS (object_class),
162 G_SIGNAL_RUN_LAST,
163 0,
164 NULL, NULL,
165 g_cclosure_marshal_VOID__OBJECT,
166 G_TYPE_NONE, 1,
167 CLUTTER_GST_TYPE_CAMERA_DEVICE);
168
169 }
170
171 static void
add_device(ClutterGstCameraManager * self,const gchar * device_node,const gchar * device_name)172 add_device (ClutterGstCameraManager *self,
173 const gchar *device_node,
174 const gchar *device_name)
175 {
176 ClutterGstCameraManagerPrivate *priv = self->priv;
177 ClutterGstCameraDevice *device;
178 GstElement *videosrc;
179 GstElementFactory *factory;
180 GParamSpec *pspec;
181
182 videosrc = gst_element_factory_make ("v4l2src", "v4l2src");
183 if (!videosrc)
184 {
185 g_warning ("Unable to get available camera devices, "
186 "v4l2src element missing");
187 return;
188 }
189
190 pspec = g_object_class_find_property (
191 G_OBJECT_GET_CLASS (G_OBJECT (videosrc)), "device");
192 if (!G_IS_PARAM_SPEC_STRING (pspec))
193 {
194 g_warning ("Unable to get available camera devices, "
195 "v4l2src has no 'device' property");
196 goto out;
197 }
198
199 factory = gst_element_get_factory (videosrc);
200
201 device = g_object_new (CLUTTER_GST_TYPE_CAMERA_DEVICE,
202 "element-factory", factory,
203 "node", device_node,
204 "name", device_name,
205 NULL);
206 g_ptr_array_add (priv->camera_devices, device);
207
208 g_signal_emit (self, signals[CAMERA_ADDED], 0, device);
209
210 out:
211 gst_object_unref (videosrc);
212 }
213
214 #ifdef HAVE_GUDEV
215 static void
remove_device(ClutterGstCameraManager * self,const gchar * device_node,const gchar * device_name)216 remove_device (ClutterGstCameraManager *self,
217 const gchar *device_node,
218 const gchar *device_name)
219 {
220 ClutterGstCameraManagerPrivate *priv = self->priv;
221 guint i;
222
223 for (i = 0; i < priv->camera_devices->len; i++)
224 {
225 ClutterGstCameraDevice *device =
226 g_ptr_array_index (priv->camera_devices, i);
227
228 if (!g_strcmp0 (clutter_gst_camera_device_get_node (device), device_node) &&
229 !g_strcmp0 (clutter_gst_camera_device_get_name (device), device_name))
230 {
231 g_signal_emit (self, signals[CAMERA_REMOVED], 0, device);
232 g_ptr_array_remove_index (priv->camera_devices, i);
233 break;
234 }
235 }
236 }
237
238 static gboolean
is_supported_device(GUdevDevice * udevice)239 is_supported_device (GUdevDevice *udevice)
240 {
241 const char *caps;
242
243 if (g_strcmp0 (g_udev_device_get_subsystem (udevice), "video4linux") != 0)
244 return FALSE;
245
246 if (g_udev_device_get_property_as_int (udevice, "ID_V4L_VERSION") != 2)
247 return FALSE;
248
249 caps = g_udev_device_get_property (udevice, "ID_V4L_CAPABILITIES");
250 if (caps == NULL || strstr (caps, ":capture:") == NULL)
251 return FALSE;
252
253 return TRUE;
254 }
255
256 static void
udev_event(GUdevClient * client,gchar * action,GUdevDevice * udevice,ClutterGstCameraManager * self)257 udev_event (GUdevClient *client,
258 gchar *action,
259 GUdevDevice *udevice,
260 ClutterGstCameraManager *self)
261 {
262 if (!is_supported_device (udevice))
263 return;
264
265 if (!g_strcmp0 (action, "add"))
266 add_device (self,
267 g_udev_device_get_device_file (udevice),
268 g_udev_device_get_property (udevice, "ID_V4L_PRODUCT"));
269 else if (!g_strcmp0 (action, "remove"))
270 remove_device (self,
271 g_udev_device_get_device_file (udevice),
272 g_udev_device_get_property (udevice, "ID_V4L_PRODUCT"));
273 }
274 #endif
275
276 static gboolean
probe_camera_devices(ClutterGstCameraManager * self)277 probe_camera_devices (ClutterGstCameraManager *self)
278 {
279 static const gchar *subsystems[] = { "video4linux", NULL };
280 ClutterGstCameraManagerPrivate *priv = self->priv;
281 #ifdef HAVE_GUDEV
282 GList *udevices, *l;
283 #else
284 gchar *device_node;
285 gchar *device_name;
286 GstElement *videosrc;
287 #endif
288
289 #ifdef HAVE_GUDEV
290 priv->udev_client = g_udev_client_new (subsystems);
291 g_signal_connect (priv->udev_client, "uevent",
292 G_CALLBACK (udev_event), self);
293
294 udevices = g_udev_client_query_by_subsystem (priv->udev_client, "video4linux");
295 for (l = udevices; l != NULL; l = l->next)
296 {
297 GUdevDevice *udevice = (GUdevDevice *) l->data;
298
299 udev_event (priv->udev_client, (gchar *) "add", udevice, self);
300
301 g_object_unref (udevice);
302 }
303 g_list_free (udevices);
304 #else
305 videosrc = gst_element_factory_make ("v4l2src", "v4l2src");
306 if (!videosrc)
307 {
308 g_warning ("Unable to get available camera devices, "
309 "v4l2src element missing");
310 return FALSE;
311 }
312
313 /* GStreamer 1.0 does not support property probe, adding default detected
314 * device as only known device */
315 g_object_get (videosrc, "device", &device_node, NULL);
316 g_object_get (videosrc, "device-name", &device_name, NULL);
317 add_device (self, device_node, device_name);
318
319 g_free (device_node);
320 g_free (device_name);
321
322 out:
323 gst_object_unref (videosrc);
324 #endif
325
326 return (priv->camera_devices != NULL);
327 }
328
329 static void
clutter_gst_camera_manager_init(ClutterGstCameraManager * self)330 clutter_gst_camera_manager_init (ClutterGstCameraManager *self)
331 {
332 self->priv = GST_CAMERA_MANAGER_PRIVATE (self);
333
334 self->priv->camera_devices =
335 g_ptr_array_new_with_free_func ((GDestroyNotify) g_object_unref);
336
337 probe_camera_devices (self);
338 }
339
340 /**
341 * clutter_gst_camera_manager_get_default:
342 *
343 * Get the camera manager.
344 *
345 * <note>This function has to be called from Clutter's main
346 * thread.</note>
347 *
348 * Return value: (transfer none): the default camera manager.
349 */
350 ClutterGstCameraManager *
clutter_gst_camera_manager_get_default(void)351 clutter_gst_camera_manager_get_default (void)
352 {
353 static ClutterGstCameraManager *manager = NULL;
354
355 if (G_UNLIKELY (manager == NULL))
356 manager = g_object_new (CLUTTER_GST_TYPE_CAMERA_MANAGER, NULL);
357
358 return manager;
359 }
360
361 /**
362 * clutter_gst_camera_manager_get_camera_devices:
363 * @self: a #ClutterGstCameraManager
364 *
365 * Retrieve an array of supported camera devices.
366 *
367 * Return value: (transfer none) (element-type ClutterGst.CameraDevice): An array of #ClutterGstCameraDevice representing
368 * the supported camera devices
369 */
370 const GPtrArray *
clutter_gst_camera_manager_get_camera_devices(ClutterGstCameraManager * self)371 clutter_gst_camera_manager_get_camera_devices (ClutterGstCameraManager *self)
372 {
373 g_return_val_if_fail (CLUTTER_GST_IS_CAMERA_MANAGER (self), NULL);
374
375 return self->priv->camera_devices;
376 }
377