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