Way, way, way too many files: Remove crack comment from the 2000 era.
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / gstv4l2element.c
1 /* G-Streamer generic V4L2 element
2  * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  */
19
20 #ifdef HAVE_CONFIG_H
21 #include <config.h>
22 #endif
23
24 #include <sys/stat.h>
25 #include <fcntl.h>
26 #include <errno.h>
27 #include <unistd.h>
28
29 #include "v4l2_calls.h"
30 #include "gstv4l2tuner.h"
31 #ifdef HAVE_XVIDEO
32 #include "gstv4l2xoverlay.h"
33 #endif
34 #include "gstv4l2colorbalance.h"
35
36 #include <gst/propertyprobe/propertyprobe.h>
37
38 /* elementfactory details */
39 static GstElementDetails gst_v4l2element_details = {
40   "Generic video4linux2 Element",
41   "Generic/Video",
42   "Generic plugin for handling common video4linux2 calls",
43   "Ronald Bultje <rbultje@ronald.bitfreak.net>"
44 };
45
46 /* V4l2Element signals and args */
47 enum
48 {
49   /* FILL ME */
50   SIGNAL_OPEN,
51   SIGNAL_CLOSE,
52   LAST_SIGNAL
53 };
54
55 enum
56 {
57   ARG_0,
58   ARG_DEVICE,
59   ARG_DEVICE_NAME,
60   ARG_NORM,
61   ARG_CHANNEL,
62   ARG_FREQUENCY,
63   ARG_FLAGS
64 };
65
66
67 static void gst_v4l2element_class_init (GstV4l2ElementClass * klass);
68 static void gst_v4l2element_base_init (GstV4l2ElementClass * klass);
69 static void gst_v4l2element_init (GstV4l2Element * v4lelement);
70 static void gst_v4l2element_dispose (GObject * object);
71 static void gst_v4l2element_set_property (GObject * object,
72     guint prop_id, const GValue * value, GParamSpec * pspec);
73 static void gst_v4l2element_get_property (GObject * object,
74     guint prop_id, GValue * value, GParamSpec * pspec);
75 static GstElementStateReturn
76 gst_v4l2element_change_state (GstElement * element);
77
78
79 static GstElementClass *parent_class = NULL;
80 static guint gst_v4l2element_signals[LAST_SIGNAL] = { 0 };
81
82
83 static gboolean
84 gst_v4l2_iface_supported (GstImplementsInterface * iface, GType iface_type)
85 {
86   GstV4l2Element *v4l2element = GST_V4L2ELEMENT (iface);
87
88 #ifdef HAVE_XVIDEO
89   g_assert (iface_type == GST_TYPE_TUNER ||
90       iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_COLOR_BALANCE);
91 #else
92   g_assert (iface_type == GST_TYPE_TUNER ||
93       iface_type == GST_TYPE_COLOR_BALANCE);
94 #endif
95
96   if (v4l2element->video_fd == -1)
97     return FALSE;
98
99 #ifdef HAVE_XVIDEO
100   if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2element))
101     return FALSE;
102 #endif
103
104   return TRUE;
105 }
106
107
108 static void
109 gst_v4l2_interface_init (GstImplementsInterfaceClass * klass)
110 {
111   /* default virtual functions */
112   klass->supported = gst_v4l2_iface_supported;
113 }
114
115
116 static const GList *
117 gst_v4l2_probe_get_properties (GstPropertyProbe * probe)
118 {
119   GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
120   static GList *list = NULL;
121
122   if (!list) {
123     list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
124   }
125
126   return list;
127 }
128
129 static gboolean
130 gst_v4l2_class_probe_devices (GstV4l2ElementClass * klass, gboolean check)
131 {
132   static gboolean init = FALSE;
133   static GList *devices = NULL;
134
135   if (!init && !check) {
136     gchar *dev_base[] = { "/dev/video", "/dev/v4l/video", NULL };
137     gint base, n, fd;
138
139     while (devices) {
140       GList *item = devices;
141       gchar *device = item->data;
142
143       devices = g_list_remove (devices, item);
144       g_free (device);
145     }
146
147     /* detect /dev entries */
148     for (n = 0; n < 64; n++) {
149       for (base = 0; dev_base[base] != NULL; base++) {
150         struct stat s;
151         gchar *device = g_strdup_printf ("%s%d",
152             dev_base[base], n);
153
154         /* does the /dev/ entry exist at all? */
155         if (stat (device, &s) == 0) {
156           /* yes: is a device attached? */
157           if ((fd = open (device, O_RDONLY)) > 0 || errno == EBUSY) {
158             if (fd > 0)
159               close (fd);
160
161             devices = g_list_append (devices, device);
162             break;
163           }
164         }
165         g_free (device);
166       }
167     }
168
169     init = TRUE;
170   }
171
172   klass->devices = devices;
173
174   return init;
175 }
176
177 static void
178 gst_v4l2_probe_probe_property (GstPropertyProbe * probe,
179     guint prop_id, const GParamSpec * pspec)
180 {
181   GstV4l2ElementClass *klass = GST_V4L2ELEMENT_GET_CLASS (probe);
182
183   switch (prop_id) {
184     case ARG_DEVICE:
185       gst_v4l2_class_probe_devices (klass, FALSE);
186       break;
187     default:
188       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
189       break;
190   }
191 }
192
193 static gboolean
194 gst_v4l2_probe_needs_probe (GstPropertyProbe * probe,
195     guint prop_id, const GParamSpec * pspec)
196 {
197   GstV4l2ElementClass *klass = GST_V4L2ELEMENT_GET_CLASS (probe);
198   gboolean ret = FALSE;
199
200   switch (prop_id) {
201     case ARG_DEVICE:
202       ret = !gst_v4l2_class_probe_devices (klass, TRUE);
203       break;
204     default:
205       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
206       break;
207   }
208
209   return ret;
210 }
211
212 static GValueArray *
213 gst_v4l2_class_list_devices (GstV4l2ElementClass * klass)
214 {
215   GValueArray *array;
216   GValue value = { 0 };
217   GList *item;
218
219   if (!klass->devices)
220     return NULL;
221
222   array = g_value_array_new (g_list_length (klass->devices));
223   item = klass->devices;
224   g_value_init (&value, G_TYPE_STRING);
225   while (item) {
226     gchar *device = item->data;
227
228     g_value_set_string (&value, device);
229     g_value_array_append (array, &value);
230
231     item = item->next;
232   }
233   g_value_unset (&value);
234
235   return array;
236 }
237
238 static GValueArray *
239 gst_v4l2_probe_get_values (GstPropertyProbe * probe,
240     guint prop_id, const GParamSpec * pspec)
241 {
242   GstV4l2ElementClass *klass = GST_V4L2ELEMENT_GET_CLASS (probe);
243   GValueArray *array = NULL;
244
245   switch (prop_id) {
246     case ARG_DEVICE:
247       array = gst_v4l2_class_list_devices (klass);
248       break;
249     default:
250       G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
251       break;
252   }
253
254   return array;
255 }
256
257
258 static void
259 gst_v4l2_property_probe_interface_init (GstPropertyProbeInterface * iface)
260 {
261   iface->get_properties = gst_v4l2_probe_get_properties;
262   iface->probe_property = gst_v4l2_probe_probe_property;
263   iface->needs_probe = gst_v4l2_probe_needs_probe;
264   iface->get_values = gst_v4l2_probe_get_values;
265 }
266
267
268 GType
269 gst_v4l2element_get_type (void)
270 {
271   static GType v4l2element_type = 0;
272
273   if (!v4l2element_type) {
274     static const GTypeInfo v4l2element_info = {
275       sizeof (GstV4l2ElementClass),
276       (GBaseInitFunc) gst_v4l2element_base_init,
277       NULL,
278       (GClassInitFunc) gst_v4l2element_class_init,
279       NULL,
280       NULL,
281       sizeof (GstV4l2Element),
282       0,
283       (GInstanceInitFunc) gst_v4l2element_init,
284       NULL
285     };
286     static const GInterfaceInfo v4l2iface_info = {
287       (GInterfaceInitFunc) gst_v4l2_interface_init,
288       NULL,
289       NULL,
290     };
291     static const GInterfaceInfo v4l2_tuner_info = {
292       (GInterfaceInitFunc) gst_v4l2_tuner_interface_init,
293       NULL,
294       NULL,
295     };
296 #ifdef HAVE_XVIDEO
297     static const GInterfaceInfo v4l2_xoverlay_info = {
298       (GInterfaceInitFunc) gst_v4l2_xoverlay_interface_init,
299       NULL,
300       NULL,
301     };
302 #endif
303     static const GInterfaceInfo v4l2_colorbalance_info = {
304       (GInterfaceInitFunc) gst_v4l2_color_balance_interface_init,
305       NULL,
306       NULL,
307     };
308     static const GInterfaceInfo v4l2_propertyprobe_info = {
309       (GInterfaceInitFunc) gst_v4l2_property_probe_interface_init,
310       NULL,
311       NULL,
312     };
313
314     v4l2element_type =
315         g_type_register_static (GST_TYPE_ELEMENT,
316         "GstV4l2Element", &v4l2element_info, 0);
317
318     g_type_add_interface_static (v4l2element_type,
319         GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info);
320     g_type_add_interface_static (v4l2element_type,
321         GST_TYPE_TUNER, &v4l2_tuner_info);
322 #ifdef HAVE_XVIDEO
323     g_type_add_interface_static (v4l2element_type,
324         GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info);
325 #endif
326     g_type_add_interface_static (v4l2element_type,
327         GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info);
328     g_type_add_interface_static (v4l2element_type,
329         GST_TYPE_PROPERTY_PROBE, &v4l2_propertyprobe_info);
330   }
331
332   return v4l2element_type;
333 }
334
335
336 #define GST_TYPE_V4L2_DEVICE_FLAGS (gst_v4l2_device_get_type ())
337 GType
338 gst_v4l2_device_get_type (void)
339 {
340   static GType v4l2_device_type = 0;
341
342   if (v4l2_device_type == 0) {
343     static const GFlagsValue values[] = {
344       {V4L2_CAP_VIDEO_CAPTURE, "CAPTURE",
345           "Device can capture"},
346       {V4L2_CAP_VIDEO_OUTPUT, "PLAYBACK",
347           "Device can playback"},
348       {V4L2_CAP_VIDEO_OVERLAY, "OVERLAY",
349           "Device can do overlay"},
350       {V4L2_CAP_TUNER, "TUNER",
351           "Device has a tuner"},
352       {V4L2_CAP_AUDIO, "AUDIO",
353           "Device handles audio"},
354       {0, NULL, NULL}
355     };
356
357     v4l2_device_type =
358         g_flags_register_static ("GstV4l2DeviceTypeFlags", values);
359   }
360
361   return v4l2_device_type;
362 }
363
364 static void
365 gst_v4l2element_base_init (GstV4l2ElementClass * klass)
366 {
367   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
368
369   klass->devices = NULL;
370
371   gst_element_class_set_details (gstelement_class, &gst_v4l2element_details);
372 }
373
374 static void
375 gst_v4l2element_class_init (GstV4l2ElementClass * klass)
376 {
377   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
378   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
379
380   parent_class = g_type_class_peek_parent (klass);
381
382   g_object_class_install_property (gobject_class, ARG_DEVICE,
383       g_param_spec_string ("device", "Device", "Device location",
384           NULL, G_PARAM_READWRITE));
385   g_object_class_install_property (gobject_class, ARG_DEVICE_NAME,
386       g_param_spec_string ("device_name", "Device name",
387           "Name of the device", NULL, G_PARAM_READABLE));
388   g_object_class_install_property (gobject_class, ARG_FLAGS,
389       g_param_spec_flags ("flags", "Flags", "Device type flags",
390           GST_TYPE_V4L2_DEVICE_FLAGS, 0, G_PARAM_READABLE));
391   g_object_class_install_property (gobject_class, ARG_NORM,
392       g_param_spec_string ("norm", "norm",
393           "Norm to use", NULL, G_PARAM_READWRITE));
394   g_object_class_install_property (gobject_class, ARG_CHANNEL,
395       g_param_spec_string ("channel", "channel",
396           "input/output to switch to", NULL, G_PARAM_READWRITE));
397   g_object_class_install_property (gobject_class, ARG_FREQUENCY,
398       g_param_spec_ulong ("frequency", "frequency",
399           "frequency to tune to (in Hz)", 0, G_MAXULONG, 0, G_PARAM_READWRITE));
400
401   /* signals */
402   gst_v4l2element_signals[SIGNAL_OPEN] =
403       g_signal_new ("open", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
404       G_STRUCT_OFFSET (GstV4l2ElementClass, open),
405       NULL, NULL, g_cclosure_marshal_VOID__STRING,
406       G_TYPE_NONE, 1, G_TYPE_STRING);
407   gst_v4l2element_signals[SIGNAL_CLOSE] =
408       g_signal_new ("close", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST,
409       G_STRUCT_OFFSET (GstV4l2ElementClass, close),
410       NULL, NULL, g_cclosure_marshal_VOID__STRING,
411       G_TYPE_NONE, 1, G_TYPE_STRING);
412
413   gobject_class->set_property = gst_v4l2element_set_property;
414   gobject_class->get_property = gst_v4l2element_get_property;
415   gobject_class->dispose = gst_v4l2element_dispose;
416
417   gstelement_class->change_state = gst_v4l2element_change_state;
418 }
419
420
421 static void
422 gst_v4l2element_init (GstV4l2Element * v4l2element)
423 {
424   /* some default values */
425   v4l2element->video_fd = -1;
426   v4l2element->buffer = NULL;
427   v4l2element->device = g_strdup ("/dev/video0");
428
429   v4l2element->channels = NULL;
430   v4l2element->norms = NULL;
431   v4l2element->colors = NULL;
432 }
433
434
435 static void
436 gst_v4l2element_dispose (GObject * object)
437 {
438   GstV4l2Element *v4l2element = GST_V4L2ELEMENT (object);
439
440   g_free (v4l2element->device);
441   v4l2element->device = NULL;
442   g_free (v4l2element->norm);
443   v4l2element->norm = NULL;
444   g_free (v4l2element->channel);
445   v4l2element->channel = NULL;
446
447   if (((GObjectClass *) parent_class)->dispose)
448     ((GObjectClass *) parent_class)->dispose (object);
449 }
450
451 static void
452 gst_v4l2element_set_property (GObject * object,
453     guint prop_id, const GValue * value, GParamSpec * pspec)
454 {
455   GstV4l2Element *v4l2element;
456
457   g_return_if_fail (GST_IS_V4L2ELEMENT (object));
458   v4l2element = GST_V4L2ELEMENT (object);
459
460   switch (prop_id) {
461     case ARG_DEVICE:
462       if (!GST_V4L2_IS_OPEN (v4l2element)) {
463         if (v4l2element->device)
464           g_free (v4l2element->device);
465         v4l2element->device = g_value_dup_string (value);
466       }
467       break;
468     case ARG_NORM:
469       if (GST_V4L2_IS_OPEN (v4l2element)) {
470         GstTuner *tuner = GST_TUNER (v4l2element);
471         GstTunerNorm *norm = gst_tuner_find_norm_by_name (tuner,
472             (gchar *) g_value_get_string (value));
473
474         if (norm) {
475           gst_tuner_set_norm (tuner, norm);
476         }
477       } else {
478         g_free (v4l2element->norm);
479         v4l2element->norm = g_value_dup_string (value);
480         g_object_notify (object, "norm");
481       }
482       break;
483     case ARG_CHANNEL:
484       if (GST_V4L2_IS_OPEN (v4l2element)) {
485         GstTuner *tuner = GST_TUNER (v4l2element);
486         GstTunerChannel *channel = gst_tuner_find_channel_by_name (tuner,
487             (gchar *) g_value_get_string (value));
488
489         if (channel) {
490           gst_tuner_set_channel (tuner, channel);
491         }
492       } else {
493         g_free (v4l2element->channel);
494         v4l2element->channel = g_value_dup_string (value);
495         g_object_notify (object, "channel");
496       }
497       break;
498     case ARG_FREQUENCY:
499       if (GST_V4L2_IS_OPEN (v4l2element)) {
500         GstTuner *tuner = GST_TUNER (v4l2element);
501         GstTunerChannel *channel = gst_tuner_get_channel (tuner);
502
503         if (channel &&
504             GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) {
505           gst_tuner_set_frequency (tuner, channel, g_value_get_ulong (value));
506         }
507       } else {
508         v4l2element->frequency = g_value_get_ulong (value);
509         g_object_notify (object, "frequency");
510       }
511       break;
512     default:
513       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
514       break;
515   }
516 }
517
518
519 static void
520 gst_v4l2element_get_property (GObject * object,
521     guint prop_id, GValue * value, GParamSpec * pspec)
522 {
523   GstV4l2Element *v4l2element;
524
525   g_return_if_fail (GST_IS_V4L2ELEMENT (object));
526   v4l2element = GST_V4L2ELEMENT (object);
527
528   switch (prop_id) {
529     case ARG_DEVICE:
530       g_value_set_string (value, v4l2element->device);
531       break;
532     case ARG_DEVICE_NAME:{
533       gchar *new = NULL;
534
535       if (GST_V4L2_IS_OPEN (v4l2element))
536         new = v4l2element->vcap.card;
537       g_value_set_string (value, new);
538       break;
539     }
540     case ARG_FLAGS:{
541       guint flags = 0;
542
543       if (GST_V4L2_IS_OPEN (v4l2element)) {
544         flags |= v4l2element->vcap.capabilities & 0x30007;
545       }
546       g_value_set_flags (value, flags);
547       break;
548     }
549     case ARG_NORM:
550       g_value_set_string (value, v4l2element->norm);
551       break;
552     case ARG_CHANNEL:
553       g_value_set_string (value, v4l2element->channel);
554       break;
555     case ARG_FREQUENCY:
556       g_value_set_ulong (value, v4l2element->frequency);
557       break;
558     default:
559       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
560       break;
561   }
562 }
563
564
565 static GstElementStateReturn
566 gst_v4l2element_change_state (GstElement * element)
567 {
568   GstV4l2Element *v4l2element;
569
570   g_return_val_if_fail (GST_IS_V4L2ELEMENT (element), GST_STATE_FAILURE);
571
572   v4l2element = GST_V4L2ELEMENT (element);
573
574   /* if going down into NULL state, close the device if it's open
575    * if going to READY, open the device (and set some options)
576    */
577   switch (GST_STATE_TRANSITION (element)) {
578     case GST_STATE_NULL_TO_READY:
579       if (!gst_v4l2_open (v4l2element))
580         return GST_STATE_FAILURE;
581
582 #ifdef HAVE_XVIDEO
583       gst_v4l2_xoverlay_open (v4l2element);
584 #endif
585
586       /* emit a signal! whoopie! */
587       g_signal_emit (G_OBJECT (v4l2element),
588           gst_v4l2element_signals[SIGNAL_OPEN], 0, v4l2element->device);
589       break;
590     case GST_STATE_READY_TO_NULL:
591 #ifdef HAVE_XVIDEO
592       gst_v4l2_xoverlay_close (v4l2element);
593 #endif
594
595       if (!gst_v4l2_close (v4l2element))
596         return GST_STATE_FAILURE;
597
598       /* emit yet another signal! wheehee! */
599       g_signal_emit (G_OBJECT (v4l2element),
600           gst_v4l2element_signals[SIGNAL_CLOSE], 0, v4l2element->device);
601       break;
602   }
603
604   if (GST_ELEMENT_CLASS (parent_class)->change_state)
605     return GST_ELEMENT_CLASS (parent_class)->change_state (element);
606
607   return GST_STATE_SUCCESS;
608 }