1 /* G-Streamer generic V4L2 element
2 * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
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.
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.
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.
29 #include "v4l2_calls.h"
30 #include "gstv4l2tuner.h"
31 #include "gstv4l2xoverlay.h"
32 #include "gstv4l2colorbalance.h"
34 #include <gst/propertyprobe/propertyprobe.h>
36 /* elementfactory details */
37 static GstElementDetails gst_v4l2element_details = {
38 "Generic video4linux2 Element",
40 "Generic plugin for handling common video4linux2 calls",
41 "Ronald Bultje <rbultje@ronald.bitfreak.net>"
44 /* V4l2Element signals and args */
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);
77 static GstElementClass *parent_class = NULL;
78 static guint gst_v4l2element_signals[LAST_SIGNAL] = { 0 };
82 gst_v4l2_iface_supported (GstImplementsInterface * iface, GType iface_type)
84 GstV4l2Element *v4l2element = GST_V4L2ELEMENT (iface);
86 g_assert (iface_type == GST_TYPE_TUNER ||
87 iface_type == GST_TYPE_X_OVERLAY || iface_type == GST_TYPE_COLOR_BALANCE);
89 if (v4l2element->video_fd == -1)
92 if (iface_type == GST_TYPE_X_OVERLAY && !GST_V4L2_IS_OVERLAY (v4l2element))
100 gst_v4l2_interface_init (GstImplementsInterfaceClass * klass)
102 /* default virtual functions */
103 klass->supported = gst_v4l2_iface_supported;
108 gst_v4l2_probe_get_properties (GstPropertyProbe * probe)
110 GObjectClass *klass = G_OBJECT_GET_CLASS (probe);
111 static GList *list = NULL;
114 list = g_list_append (NULL, g_object_class_find_property (klass, "device"));
121 gst_v4l2_class_probe_devices (GstV4l2ElementClass * klass, gboolean check)
123 static gboolean init = FALSE;
124 static GList *devices = NULL;
126 if (!init && !check) {
127 gchar *dev_base[] = { "/dev/video", "/dev/v4l/video", NULL };
131 GList *item = devices;
132 gchar *device = item->data;
134 devices = g_list_remove (devices, item);
138 /* detect /dev entries */
139 for (n = 0; n < 64; n++) {
140 for (base = 0; dev_base[base] != NULL; base++) {
142 gchar *device = g_strdup_printf ("%s%d",
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) {
152 devices = g_list_append (devices, device);
163 klass->devices = devices;
169 gst_v4l2_probe_probe_property (GstPropertyProbe * probe,
170 guint prop_id, const GParamSpec * pspec)
172 GstV4l2ElementClass *klass = GST_V4L2ELEMENT_GET_CLASS (probe);
176 gst_v4l2_class_probe_devices (klass, FALSE);
179 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
185 gst_v4l2_probe_needs_probe (GstPropertyProbe * probe,
186 guint prop_id, const GParamSpec * pspec)
188 GstV4l2ElementClass *klass = GST_V4L2ELEMENT_GET_CLASS (probe);
189 gboolean ret = FALSE;
193 ret = !gst_v4l2_class_probe_devices (klass, TRUE);
196 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
204 gst_v4l2_class_list_devices (GstV4l2ElementClass * klass)
207 GValue value = { 0 };
213 array = g_value_array_new (g_list_length (klass->devices));
214 item = klass->devices;
215 g_value_init (&value, G_TYPE_STRING);
217 gchar *device = item->data;
219 g_value_set_string (&value, device);
220 g_value_array_append (array, &value);
224 g_value_unset (&value);
230 gst_v4l2_probe_get_values (GstPropertyProbe * probe,
231 guint prop_id, const GParamSpec * pspec)
233 GstV4l2ElementClass *klass = GST_V4L2ELEMENT_GET_CLASS (probe);
234 GValueArray *array = NULL;
238 array = gst_v4l2_class_list_devices (klass);
241 G_OBJECT_WARN_INVALID_PROPERTY_ID (probe, prop_id, pspec);
250 gst_v4l2_property_probe_interface_init (GstPropertyProbeInterface * iface)
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;
260 gst_v4l2element_get_type (void)
262 static GType v4l2element_type = 0;
264 if (!v4l2element_type) {
265 static const GTypeInfo v4l2element_info = {
266 sizeof (GstV4l2ElementClass),
267 (GBaseInitFunc) gst_v4l2element_base_init,
269 (GClassInitFunc) gst_v4l2element_class_init,
272 sizeof (GstV4l2Element),
274 (GInstanceInitFunc) gst_v4l2element_init,
277 static const GInterfaceInfo v4l2iface_info = {
278 (GInterfaceInitFunc) gst_v4l2_interface_init,
282 static const GInterfaceInfo v4l2_tuner_info = {
283 (GInterfaceInitFunc) gst_v4l2_tuner_interface_init,
287 static const GInterfaceInfo v4l2_xoverlay_info = {
288 (GInterfaceInitFunc) gst_v4l2_xoverlay_interface_init,
292 static const GInterfaceInfo v4l2_colorbalance_info = {
293 (GInterfaceInitFunc) gst_v4l2_color_balance_interface_init,
297 static const GInterfaceInfo v4l2_propertyprobe_info = {
298 (GInterfaceInitFunc) gst_v4l2_property_probe_interface_init,
304 g_type_register_static (GST_TYPE_ELEMENT,
305 "GstV4l2Element", &v4l2element_info, 0);
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);
319 return v4l2element_type;
323 #define GST_TYPE_V4L2_DEVICE_FLAGS (gst_v4l2_device_get_type ())
325 gst_v4l2_device_get_type (void)
327 static GType v4l2_device_type = 0;
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"},
345 g_flags_register_static ("GstV4l2DeviceTypeFlags", values);
348 return v4l2_device_type;
352 gst_v4l2element_base_init (GstV4l2ElementClass * klass)
354 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
356 klass->devices = NULL;
358 gst_element_class_set_details (gstelement_class, &gst_v4l2element_details);
362 gst_v4l2element_class_init (GstV4l2ElementClass * klass)
364 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
365 GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass);
367 parent_class = g_type_class_peek_parent (klass);
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));
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);
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;
404 gstelement_class->change_state = gst_v4l2element_change_state;
409 gst_v4l2element_init (GstV4l2Element * v4l2element)
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"));
417 v4l2element->channels = NULL;
418 v4l2element->norms = NULL;
419 v4l2element->colors = NULL;
421 v4l2element->overlay = gst_v4l2_xoverlay_new (v4l2element);
426 gst_v4l2element_dispose (GObject * object)
428 GstV4l2Element *v4l2element = GST_V4L2ELEMENT (object);
430 if (v4l2element->overlay) {
431 gst_v4l2_xoverlay_free (v4l2element);
434 if (v4l2element->display) {
435 g_free (v4l2element->display);
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;
445 if (((GObjectClass *) parent_class)->dispose)
446 ((GObjectClass *) parent_class)->dispose (object);
450 gst_v4l2element_set_property (GObject * object,
451 guint prop_id, const GValue * value, GParamSpec * pspec)
453 GstV4l2Element *v4l2element;
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;
464 if (!GST_V4L2_IS_OPEN (v4l2element)) {
465 if (v4l2element->device)
466 g_free (v4l2element->device);
467 v4l2element->device = g_value_dup_string (value);
471 if (GST_V4L2_IS_OPEN (v4l2element)) {
472 GstTunerNorm *norm = gst_tuner_get_norm (tuner);
475 gst_tuner_set_norm (tuner, norm);
478 g_free (v4l2element->norm);
479 v4l2element->norm = g_value_dup_string (value);
480 g_object_notify (object, "norm");
484 if (GST_V4L2_IS_OPEN (v4l2element)) {
485 GstTunerChannel *channel = gst_tuner_get_channel (tuner);
488 gst_tuner_set_channel (tuner, channel);
491 g_free (v4l2element->channel);
492 v4l2element->channel = g_value_dup_string (value);
493 g_object_notify (object, "channel");
497 if (GST_V4L2_IS_OPEN (v4l2element)) {
498 GstTunerChannel *channel;
500 if (!v4l2element->channel)
502 channel = gst_tuner_get_channel (tuner);
504 gst_tuner_set_frequency (tuner, channel, g_value_get_ulong (value));
506 v4l2element->frequency = g_value_get_ulong (value);
507 g_object_notify (object, "frequency");
511 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
518 gst_v4l2element_get_property (GObject * object,
519 guint prop_id, GValue * value, GParamSpec * pspec)
521 GstV4l2Element *v4l2element;
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);
529 g_value_set_string (value, v4l2element->device);
531 case ARG_DEVICE_NAME:{
534 if (GST_V4L2_IS_OPEN (v4l2element))
535 new = v4l2element->vcap.card;
536 g_value_set_string (value, new);
542 if (GST_V4L2_IS_OPEN (v4l2element)) {
543 flags |= v4l2element->vcap.capabilities & 30007;
545 g_value_set_flags (value, flags);
549 g_value_set_string (value, v4l2element->norm);
552 g_value_set_string (value, v4l2element->channel);
555 g_value_set_ulong (value, v4l2element->frequency);
558 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
564 static GstElementStateReturn
565 gst_v4l2element_change_state (GstElement * element)
567 GstV4l2Element *v4l2element;
569 g_return_val_if_fail (GST_IS_V4L2ELEMENT (element), GST_STATE_FAILURE);
571 v4l2element = GST_V4L2ELEMENT (element);
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)
576 switch (GST_STATE_TRANSITION (element)) {
577 case GST_STATE_NULL_TO_READY:
578 gst_v4l2_set_display (v4l2element);
580 if (!gst_v4l2_open (v4l2element))
581 return GST_STATE_FAILURE;
583 gst_v4l2_xoverlay_open (v4l2element);
585 /* emit a signal! whoopie! */
586 g_signal_emit (G_OBJECT (v4l2element),
587 gst_v4l2element_signals[SIGNAL_OPEN], 0, v4l2element->device);
589 case GST_STATE_READY_TO_NULL:
590 gst_v4l2_xoverlay_close (v4l2element);
592 if (!gst_v4l2_close (v4l2element))
593 return GST_STATE_FAILURE;
595 /* emit yet another signal! wheehee! */
596 g_signal_emit (G_OBJECT (v4l2element),
597 gst_v4l2element_signals[SIGNAL_CLOSE], 0, v4l2element->device);
601 if (GST_ELEMENT_CLASS (parent_class)->change_state)
602 return GST_ELEMENT_CLASS (parent_class)->change_state (element);
604 return GST_STATE_SUCCESS;