Initialize Tizen 2.3
[framework/multimedia/gst-plugins-base0.10.git] / mobile / sys / v4l / gstv4lelement.c
1 /* GStreamer
2  *
3  * gstv4lelement.c: base class for V4L elements
4  *
5  * Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Library General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Library General Public License for more details.
16  *
17  * You should have received a copy of the GNU Library General Public
18  * License along with this library; if not, write to the
19  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
20  * Boston, MA 02111-1307, USA.
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include <config.h>
25 #endif
26
27 #include <sys/stat.h>
28 #include <fcntl.h>
29 #include <errno.h>
30 #include <unistd.h>
31 #include <string.h>
32
33 #include <gst/interfaces/propertyprobe.h>
34
35 #ifdef HAVE_GUDEV
36 #include <gudev/gudev.h>
37 #endif
38
39 #include "v4l_calls.h"
40 #include "gstv4ltuner.h"
41 #ifdef HAVE_XVIDEO
42 #include "gstv4lxoverlay.h"
43 #endif
44 #include "gstv4lcolorbalance.h"
45
46
47 enum
48 {
49   PROP_0,
50   PROP_DEVICE,
51   PROP_DEVICE_NAME,
52   PROP_FLAGS
53 };
54
55 GST_DEBUG_CATEGORY (v4lelement_debug);
56 #define GST_CAT_DEFAULT v4lelement_debug
57
58 static void gst_v4lelement_init_interfaces (GType type);
59
60 GST_BOILERPLATE_FULL (GstV4lElement, gst_v4lelement, GstPushSrc,
61     GST_TYPE_PUSH_SRC, gst_v4lelement_init_interfaces);
62
63 static void gst_v4lelement_dispose (GObject * object);
64 static void gst_v4lelement_set_property (GObject * object,
65     guint prop_id, const GValue * value, GParamSpec * pspec);
66 static void gst_v4lelement_get_property (GObject * object,
67     guint prop_id, GValue * value, GParamSpec * pspec);
68
69 /* element methods */
70 static GstStateChangeReturn gst_v4lelement_change_state (GstElement * element,
71     GstStateChange transition);
72
73 static gboolean
74 gst_v4l_iface_supported (GstImplementsInterface * iface, GType iface_type)
75 {
76   GstV4lElement *v4lelement = GST_V4LELEMENT (iface);
77
78 #ifdef HAVE_XVIDEO
79   g_assert (iface_type == GST_TYPE_TUNER ||
80       iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_COLOR_BALANCE);
81 #else
82   g_assert (iface_type == GST_TYPE_TUNER ||
83       iface_type == GST_TYPE_COLOR_BALANCE);
84 #endif
85
86   if (v4lelement->video_fd == -1)
87     return FALSE;
88
89 #ifdef HAVE_XVIDEO
90   if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L_IS_OVERLAY (v4lelement))
91     return FALSE;
92 #endif
93
94   return TRUE;
95 }
96
97 static void
98 gst_v4l_interface_init (GstImplementsInterfaceClass * klass)
99 {
100   /* default virtual functions */
101   klass->supported = gst_v4l_iface_supported;
102 }
103
104 static const GList *
105 gst_v4l_probe_get_properties (GstPropertyProbe * probe)
106 {
107   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
108   static GList *list = NULL;
109
110   if (!list) {
111     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
112   }
113
114   return list;
115 }
116
117 static gboolean init = FALSE;
118 static GList *devices = NULL;
119
120 #ifdef HAVE_GUDEV
121 static gboolean
122 gst_v4l_class_probe_devices_with_udev (GstV4lElementClass * klass,
123     gboolean check)
124 {
125   GUdevClient *client = NULL;
126   GList *item;
127
128   if (!check) {
129     while (devices) {
130       gchar *device = devices->data;
131       devices = g_list_remove (devices, device);
132       g_free (device);
133     }
134
135     GST_INFO ("Enumerating video4linux devices from udev");
136     client = g_udev_client_new (NULL);
137     if (!client) {
138       GST_WARNING ("Failed to initialize gudev client");
139       goto finish;
140     }
141
142     item = g_udev_client_query_by_subsystem (client, "video4linux");
143     while (item) {
144       GUdevDevice *device = item->data;
145       gchar *devnode = g_strdup (g_udev_device_get_device_file (device));
146       gint api = g_udev_device_get_property_as_int (device, "ID_V4L_VERSION");
147       GST_INFO ("Found new device: %s, API: %d", devnode, api);
148       /* Append v4l1 devices only. If api is 0 probably v4l_id has
149          been stripped out of the current udev installation, append
150          anyway */
151       if (api == 0) {
152         GST_WARNING
153             ("Couldn't retrieve ID_V4L_VERSION, silly udev installation?");
154       }
155       if ((api == 1 || api == 0)) {
156         devices = g_list_append (devices, devnode);
157       } else {
158         g_free (devnode);
159       }
160       g_object_unref (device);
161       item = item->next;
162     }
163     g_list_free (item);
164     init = TRUE;
165   }
166
167 finish:
168   if (client) {
169     g_object_unref (client);
170   }
171
172   klass->devices = devices;
173
174   return init;
175 }
176 #endif /* HAVE_GUDEV */
177
178 static gboolean
179 gst_v4l_class_probe_devices (GstV4lElementClass * klass, gboolean check)
180 {
181   if (!check) {
182     const gchar *dev_base[] = { "/dev/video", "/dev/v4l/video", NULL };
183     gint base, n, fd;
184
185     while (devices) {
186       gchar *device = devices->data;
187       devices = g_list_remove (devices, device);
188       g_free (device);
189     }
190
191     /* detect /dev entries */
192     for (n = 0; n < 64; n++) {
193       for (base = 0; dev_base[base] != NULL; base++) {
194         struct stat s;
195         gchar *device = g_strdup_printf ("%s%d", dev_base[base], n);
196
197         /* does the /dev/ entry exist at all? */
198         if (stat (device, &s) == 0) {
199           /* yes: is a device attached? */
200           if ((fd = open (device, O_RDONLY)) > 0 || errno == EBUSY) {
201             if (fd > 0)
202               close (fd);
203
204             devices = g_list_append (devices, device);
205             break;
206           }
207         }
208         g_free (device);
209       }
210     }
211
212     init = TRUE;
213   }
214
215   klass->devices = devices;
216
217   return init;
218 }
219
220 static void
221 gst_v4l_probe_probe_property (GstPropertyProbe * probe,
222     guint prop_id, const GParamSpec * pspec)
223 {
224   GstV4lElementClass *klass = GST_V4LELEMENT_GET_CLASS (probe);
225
226   switch (prop_id) {
227     case PROP_DEVICE:
228 #ifdef HAVE_GUDEV
229       if (!gst_v4l_class_probe_devices_with_udev (klass, FALSE))
230         gst_v4l_class_probe_devices (klass, FALSE);
231 #else /* !HAVE_GUDEV */
232       gst_v4l_class_probe_devices (klass, FALSE);
233 #endif /* HAVE_GUDEV */
234       break;
235     default:
236       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
237       break;
238   }
239 }
240
241 static gboolean
242 gst_v4l_probe_needs_probe (GstPropertyProbe * probe,
243     guint prop_id, const GParamSpec * pspec)
244 {
245   GstV4lElementClass *klass = GST_V4LELEMENT_GET_CLASS (probe);
246   gboolean ret = FALSE;
247
248   switch (prop_id) {
249     case PROP_DEVICE:
250 #ifdef HAVE_GUDEV
251       ret = !gst_v4l_class_probe_devices_with_udev (klass, FALSE);
252 #else /* !HAVE_GUDEV */
253       ret = !gst_v4l_class_probe_devices (klass, TRUE);
254 #endif /* HAVE_GUDEV */
255       ret = !gst_v4l_class_probe_devices (klass, TRUE);
256       break;
257     default:
258       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
259       break;
260   }
261
262   return ret;
263 }
264
265 static GValueArray *
266 gst_v4l_class_list_devices (GstV4lElementClass * klass)
267 {
268   GValueArray *array;
269   GValue value = { 0 };
270   GList *item;
271
272   if (!klass->devices)
273     return NULL;
274
275   array = g_value_array_new (g_list_length (klass->devices));
276   item = klass->devices;
277   g_value_init (&value, G_TYPE_STRING);
278   while (item) {
279     gchar *device = item->data;
280
281     g_value_set_string (&value, device);
282     g_value_array_append (array, &value);
283
284     item = item->next;
285   }
286   g_value_unset (&value);
287
288   return array;
289 }
290
291 static GValueArray *
292 gst_v4l_probe_get_values (GstPropertyProbe * probe,
293     guint prop_id, const GParamSpec * pspec)
294 {
295   GstV4lElementClass *klass = GST_V4LELEMENT_GET_CLASS (probe);
296   GValueArray *array = NULL;
297
298   switch (prop_id) {
299     case PROP_DEVICE:
300       array = gst_v4l_class_list_devices (klass);
301       break;
302     default:
303       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
304       break;
305   }
306
307   return array;
308 }
309
310 static void
311 gst_v4l_property_probe_interface_init (GstPropertyProbeInterface * iface)
312 {
313   iface->get_properties = gst_v4l_probe_get_properties;
314   iface->probe_property = gst_v4l_probe_probe_property;
315   iface->needs_probe = gst_v4l_probe_needs_probe;
316   iface->get_values = gst_v4l_probe_get_values;
317 }
318
319 #define GST_TYPE_V4L_DEVICE_FLAGS (gst_v4l_device_get_type ())
320 static GType
321 gst_v4l_device_get_type (void)
322 {
323   static GType v4l_device_type = 0;
324
325   if (v4l_device_type == 0) {
326     static const GFlagsValue values[] = {
327       {VID_TYPE_CAPTURE, "CAPTURE", "Device can capture"},
328       {VID_TYPE_TUNER, "TUNER", "Device has a tuner"},
329       {VID_TYPE_OVERLAY, "OVERLAY", "Device can do overlay"},
330       {VID_TYPE_MPEG_DECODER, "MPEG_DECODER", "Device can decode MPEG"},
331       {VID_TYPE_MPEG_ENCODER, "MPEG_ENCODER", "Device can encode MPEG"},
332       {VID_TYPE_MJPEG_DECODER, "MJPEG_DECODER", "Device can decode MJPEG"},
333       {VID_TYPE_MJPEG_ENCODER, "MJPEG_ENCODER", "Device can encode MJPEG"},
334       {0x10000, "AUDIO", "Device handles audio"},
335       {0, NULL, NULL}
336     };
337
338     v4l_device_type = g_flags_register_static ("GstV4lDeviceTypeFlags", values);
339   }
340
341   return v4l_device_type;
342 }
343
344 static void
345 gst_v4lelement_init_interfaces (GType type)
346 {
347   static const GInterfaceInfo v4liface_info = {
348     (GInterfaceInitFunc) gst_v4l_interface_init,
349     NULL,
350     NULL,
351   };
352   static const GInterfaceInfo v4l_tuner_info = {
353     (GInterfaceInitFunc) gst_v4l_tuner_interface_init,
354     NULL,
355     NULL,
356   };
357 #ifdef HAVE_XVIDEO
358   static const GInterfaceInfo v4l_xoverlay_info = {
359     (GInterfaceInitFunc) gst_v4l_xoverlay_interface_init,
360     NULL,
361     NULL,
362   };
363 #endif
364   static const GInterfaceInfo v4l_colorbalance_info = {
365     (GInterfaceInitFunc) gst_v4l_color_balance_interface_init,
366     NULL,
367     NULL,
368   };
369   static const GInterfaceInfo v4l_propertyprobe_info = {
370     (GInterfaceInitFunc) gst_v4l_property_probe_interface_init,
371     NULL,
372     NULL,
373   };
374
375   g_type_add_interface_static (type,
376       GST_TYPE_IMPLEMENTS_INTERFACE, &v4liface_info);
377   g_type_add_interface_static (type, GST_TYPE_TUNER, &v4l_tuner_info);
378 #ifdef HAVE_XVIDEO
379   g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l_xoverlay_info);
380 #endif
381   g_type_add_interface_static (type,
382       GST_TYPE_COLOR_BALANCE, &v4l_colorbalance_info);
383   g_type_add_interface_static (type,
384       GST_TYPE_PROPERTY_PROBE, &v4l_propertyprobe_info);
385 }
386
387
388 static void
389 gst_v4lelement_base_init (gpointer g_class)
390 {
391   GstV4lElementClass *klass = GST_V4LELEMENT_CLASS (g_class);
392
393   klass->devices = NULL;
394
395   GST_DEBUG_CATEGORY_INIT (v4lelement_debug, "v4lelement", 0,
396       "V4L Base Class debug");
397 }
398
399 static void
400 gst_v4lelement_class_init (GstV4lElementClass * klass)
401 {
402   GObjectClass *gobject_class;
403   GstElementClass *element_class;
404
405   gobject_class = (GObjectClass *) klass;
406   element_class = GST_ELEMENT_CLASS (klass);
407
408   gobject_class->set_property = gst_v4lelement_set_property;
409   gobject_class->get_property = gst_v4lelement_get_property;
410   gobject_class->dispose = gst_v4lelement_dispose;
411
412   element_class->change_state = gst_v4lelement_change_state;
413
414   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEVICE,
415       g_param_spec_string ("device", "Device", "Device location",
416           NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
417   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEVICE_NAME,
418       g_param_spec_string ("device-name", "Device name", "Name of the device",
419           NULL, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
420   g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FLAGS,
421       g_param_spec_flags ("flags", "Flags", "Device type flags",
422           GST_TYPE_V4L_DEVICE_FLAGS, 0,
423           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
424
425 }
426
427
428 static void
429 gst_v4lelement_init (GstV4lElement * v4lelement, GstV4lElementClass * klass)
430 {
431   /* some default values */
432   v4lelement->video_fd = -1;
433   v4lelement->buffer = NULL;
434   v4lelement->videodev = g_strdup ("/dev/video0");
435
436   v4lelement->norms = NULL;
437   v4lelement->channels = NULL;
438   v4lelement->colors = NULL;
439
440   v4lelement->xwindow_id = 0;
441 }
442
443
444 static void
445 gst_v4lelement_dispose (GObject * object)
446 {
447   GstV4lElement *v4lelement = GST_V4LELEMENT (object);
448
449   if (v4lelement->videodev) {
450     g_free (v4lelement->videodev);
451     v4lelement->videodev = NULL;
452   }
453
454   if (((GObjectClass *) parent_class)->dispose)
455     ((GObjectClass *) parent_class)->dispose (object);
456 }
457
458
459 static void
460 gst_v4lelement_set_property (GObject * object,
461     guint prop_id, const GValue * value, GParamSpec * pspec)
462 {
463   GstV4lElement *v4lelement = GST_V4LELEMENT (object);
464
465   switch (prop_id) {
466     case PROP_DEVICE:
467       if (v4lelement->videodev)
468         g_free (v4lelement->videodev);
469       v4lelement->videodev = g_strdup (g_value_get_string (value));
470       break;
471     default:
472       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
473       break;
474   }
475 }
476
477
478 static void
479 gst_v4lelement_get_property (GObject * object,
480     guint prop_id, GValue * value, GParamSpec * pspec)
481 {
482   GstV4lElement *v4lelement = GST_V4LELEMENT (object);
483
484   switch (prop_id) {
485     case PROP_DEVICE:
486       g_value_set_string (value, v4lelement->videodev);
487       break;
488     case PROP_DEVICE_NAME:{
489       gchar *new = NULL;
490
491       if (GST_V4L_IS_OPEN (v4lelement)) {
492         new = v4lelement->vcap.name;
493       } else if (gst_v4l_open (v4lelement)) {
494         new = v4lelement->vcap.name;
495         gst_v4l_close (v4lelement);
496       }
497       g_value_set_string (value, new);
498       break;
499     }
500     case PROP_FLAGS:{
501       guint flags = 0;
502
503       if (GST_V4L_IS_OPEN (v4lelement)) {
504         flags |= v4lelement->vcap.type & 0x3C0B;
505         if (v4lelement->vcap.audios)
506           flags |= 0x10000;
507       }
508       g_value_set_flags (value, flags);
509       break;
510     }
511     default:
512       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
513       break;
514   }
515 }
516
517 static GstStateChangeReturn
518 gst_v4lelement_change_state (GstElement * element, GstStateChange transition)
519 {
520   GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
521   GstV4lElement *v4lelement = GST_V4LELEMENT (element);
522
523   switch (transition) {
524     case GST_STATE_CHANGE_NULL_TO_READY:
525       /* open the device */
526       if (!gst_v4l_open (v4lelement))
527         return GST_STATE_CHANGE_FAILURE;
528 #ifdef HAVE_XVIDEO
529       gst_v4l_xoverlay_start (v4lelement);
530 #endif
531       break;
532     default:
533       break;
534   }
535
536   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
537
538   switch (transition) {
539     case GST_STATE_CHANGE_READY_TO_NULL:
540       /* close the device */
541 #ifdef HAVE_XVIDEO
542       gst_v4l_xoverlay_stop (v4lelement);
543 #endif
544       if (!gst_v4l_close (v4lelement))
545         return GST_STATE_CHANGE_FAILURE;
546       break;
547     default:
548       break;
549   }
550
551   return ret;
552 }