summaryrefslogtreecommitdiffstats
path: root/sys/v4l2
diff options
context:
space:
mode:
authorRonald S. Bultje <rbultje@ronald.bitfreak.net>2002-09-09 07:14:35 +0000
committerRonald S. Bultje <rbultje@ronald.bitfreak.net>2002-09-09 07:14:35 +0000
commit3ec5a0a054fd36290f006aa2101da35d03d9f5ed (patch)
treee7a928b150417982d8998a2849f541d3223218af /sys/v4l2
parent86c99507a557afbf84459d85e1d1d4b287b457d5 (diff)
downloadgst-plugins-bad-3ec5a0a054fd36290f006aa2101da35d03d9f5ed.tar.gz
gst-plugins-bad-3ec5a0a054fd36290f006aa2101da35d03d9f5ed.tar.bz2
gst-plugins-bad-3ec5a0a054fd36290f006aa2101da35d03d9f5ed.zip
this adds video4linux2 source and element plugins. The division in v4l2* plugins is the same as for v4l1 - i.e. an el...
Original commit message from CVS: this adds video4linux2 source and element plugins. The division in v4l2* plugins is the same as for v4l1 - i.e. an element, a src and a sink, but there won't be separate encoding plugins (like v4lmjpegsrc) - all functionality is (thanks to video4linux2) integrated in one plugin: v4l2src. v4l2sink is still to be done, that'll come later.
Diffstat (limited to 'sys/v4l2')
-rw-r--r--sys/v4l2/Makefile.am18
-rw-r--r--sys/v4l2/README24
-rw-r--r--sys/v4l2/gstv4l2element.c493
-rw-r--r--sys/v4l2/gstv4l2element.h110
-rw-r--r--sys/v4l2/gstv4l2src.c837
-rw-r--r--sys/v4l2/gstv4l2src.h69
-rw-r--r--sys/v4l2/v4l2-overlay_calls.c168
-rw-r--r--sys/v4l2/v4l2_calls.c863
-rw-r--r--sys/v4l2/v4l2_calls.h135
-rw-r--r--sys/v4l2/v4l2src_calls.c397
-rw-r--r--sys/v4l2/v4l2src_calls.h45
11 files changed, 3159 insertions, 0 deletions
diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am
new file mode 100644
index 00000000..18fe742a
--- /dev/null
+++ b/sys/v4l2/Makefile.am
@@ -0,0 +1,18 @@
+plugindir = $(libdir)/gst
+
+plugin_LTLIBRARIES = \
+ libgstv4l2element.la \
+ libgstv4l2src.la
+
+libgstv4l2element_la_SOURCES = gstv4l2element.c v4l2_calls.c v4l2-overlay_calls.c
+libgstv4l2element_la_CFLAGS = $(GST_CFLAGS)
+libgstv4l2element_la_LIBADD =
+libgstv4l2element_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+libgstv4l2src_la_SOURCES = gstv4l2src.c v4l2src_calls.c
+libgstv4l2src_la_CFLAGS = $(GST_CFLAGS)
+libgstv4l2src_la_LIBADD = libgstv4l2element.la
+libgstv4l2src_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+
+noinst_HEADERS = gstv4l2element.h v4l2_calls.h \
+ gstv4l2src.h v4l2src_calls.h
diff --git a/sys/v4l2/README b/sys/v4l2/README
new file mode 100644
index 00000000..f10c27c2
--- /dev/null
+++ b/sys/v4l2/README
@@ -0,0 +1,24 @@
+v4l2 plugins
+============
+
+The idea is a bit the same as the idea for the v4l1 plugins. We want
+one generic v4l2element, and a few child objects (probably only two:
+v4l2src and v4l2sink):
+
+ /-------- v4l2src
+v4l2element ---=
+ \-------- v4l2sink
+
+Both v4l2src and v4l2sink have a uncompressed and a compressed
+recording-/playback-mode. Since this is all part of v4l2, the 'client'
+of these elements, i.e. an applicaiton using v4l2src/v4l2sink, will
+hardly notice this. All capsnego stuff is done inside, and the plugin
+knows which formats are compressed and which are not.
+
+Please note that the v4l1 and the v4l2 plugins are *not* compatible
+concerning properties. Naming has been kept the same where possible,
+but in some cases, properties had to be removed or added to make
+full use of v4l2.
+
+V4L2 API: http://thedirks.org/v4l2/. Kernel patches available from
+ http://bytesex.org/patches/.
diff --git a/sys/v4l2/gstv4l2element.c b/sys/v4l2/gstv4l2element.c
new file mode 100644
index 00000000..2ab68a38
--- /dev/null
+++ b/sys/v4l2/gstv4l2element.c
@@ -0,0 +1,493 @@
+/* G-Streamer generic V4L2 element
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "v4l2_calls.h"
+
+
+static GstElementDetails gst_v4l2element_details = {
+ "Generic video4linux2 Element",
+ "None/Video",
+ "Generic plugin for handling common video4linux2 calls",
+ VERSION,
+ "Ronald Bultje <rbultje@ronald.bitfreak.net>",
+ "(C) 2002",
+};
+
+/* V4l2Element signals and args */
+enum {
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+enum {
+ ARG_0,
+ ARG_CHANNEL,
+ ARG_CHANNEL_NAMES,
+ ARG_OUTPUT,
+ ARG_OUTPUT_NAMES,
+ ARG_NORM,
+ ARG_NORM_NAMES,
+ ARG_HAS_TUNER,
+ ARG_FREQUENCY,
+ ARG_SIGNAL_STRENGTH,
+ ARG_HAS_AUDIO,
+ ARG_ATTRIBUTE,
+ ARG_ATTRIBUTE_SETS,
+ ARG_DEVICE,
+ ARG_DEVICE_NAME,
+ ARG_DEVICE_HAS_CAPTURE,
+ ARG_DEVICE_HAS_OVERLAY,
+ ARG_DEVICE_HAS_CODEC,
+ ARG_DISPLAY,
+ ARG_VIDEOWINDOW,
+ ARG_CLIPPING,
+ ARG_DO_OVERLAY,
+};
+
+
+static void gst_v4l2element_class_init (GstV4l2ElementClass *klass);
+static void gst_v4l2element_init (GstV4l2Element *v4lelement);
+static void gst_v4l2element_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gst_v4l2element_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static GstElementStateReturn gst_v4l2element_change_state (GstElement *element);
+
+
+static GstElementClass *parent_class = NULL;
+/*static guint gst_v4l2element_signals[LAST_SIGNAL] = { 0 }; */
+
+
+GType
+gst_v4l2element_get_type (void)
+{
+ static GType v4l2element_type = 0;
+
+ if (!v4l2element_type) {
+ static const GTypeInfo v4l2element_info = {
+ sizeof(GstV4l2ElementClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_v4l2element_class_init,
+ NULL,
+ NULL,
+ sizeof(GstV4l2Element),
+ 0,
+ (GInstanceInitFunc) gst_v4l2element_init,
+ NULL
+ };
+ v4l2element_type = g_type_register_static(GST_TYPE_ELEMENT,
+ "GstV4l2Element", &v4l2element_info, 0);
+ }
+ return v4l2element_type;
+}
+
+
+
+static void
+gst_v4l2element_class_init (GstV4l2ElementClass *klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass*)klass;
+ gstelement_class = (GstElementClass*)klass;
+
+ parent_class = g_type_class_ref(GST_TYPE_ELEMENT);
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNEL,
+ g_param_spec_int("channel","channel","channel",
+ G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CHANNEL_NAMES,
+ g_param_spec_pointer("channel_names","channel_names","channel_names",
+ G_PARAM_READABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_OUTPUT,
+ g_param_spec_int("output","output","output",
+ G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_OUTPUT_NAMES,
+ g_param_spec_pointer("output_names","output_names","output_names",
+ G_PARAM_READABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NORM,
+ g_param_spec_int("norm","norm","norm",
+ G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NORM_NAMES,
+ g_param_spec_pointer("norm_names","norm_names","norm_names",
+ G_PARAM_READABLE));
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HAS_TUNER,
+ g_param_spec_boolean("has_tuner","has_tuner","has_tuner",
+ 0,G_PARAM_READABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FREQUENCY,
+ g_param_spec_ulong("frequency","frequency","frequency",
+ 0,G_MAXULONG,0,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SIGNAL_STRENGTH,
+ g_param_spec_ulong("signal_strength","signal_strength","signal_strength",
+ 0,G_MAXULONG,0,G_PARAM_READABLE));
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HAS_AUDIO,
+ g_param_spec_boolean("has_audio","has_audio","has_audio",
+ 0,G_PARAM_READABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_ATTRIBUTE,
+ g_param_spec_pointer("attribute","attribute","attribute",
+ G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_ATTRIBUTE_SETS,
+ g_param_spec_pointer("attribute_sets","attribute_sets","attribute_sets",
+ G_PARAM_READABLE));
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE,
+ g_param_spec_string("device","device","device",
+ NULL, G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE_NAME,
+ g_param_spec_string("device_name","device_name","device_name",
+ NULL, G_PARAM_READABLE));
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE_HAS_CAPTURE,
+ g_param_spec_boolean("can_capture","can_capture","can_capture",
+ 0,G_PARAM_READABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE_HAS_OVERLAY,
+ g_param_spec_boolean("has_overlay","has_overlay","has_overlay",
+ 0,G_PARAM_READABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE_HAS_CODEC,
+ g_param_spec_boolean("has_compression","has_compression","has_compression",
+ 0,G_PARAM_READABLE));
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DISPLAY,
+ g_param_spec_string("display","display","display",
+ NULL, G_PARAM_WRITABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DO_OVERLAY,
+ g_param_spec_boolean("do_overlay","do_overlay","do_overlay",
+ 0,G_PARAM_WRITABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_VIDEOWINDOW,
+ g_param_spec_pointer("videowindow","videowindow","videowindow",
+ G_PARAM_WRITABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CLIPPING,
+ g_param_spec_pointer("videowindowclip","videowindowclip","videowindowclip",
+ G_PARAM_WRITABLE));
+
+ gobject_class->set_property = gst_v4l2element_set_property;
+ gobject_class->get_property = gst_v4l2element_get_property;
+
+ gstelement_class->change_state = gst_v4l2element_change_state;
+}
+
+
+static void
+gst_v4l2element_init (GstV4l2Element *v4l2element)
+{
+ /* some default values */
+ v4l2element->video_fd = -1;
+ v4l2element->buffer = NULL;
+ v4l2element->device = NULL;
+
+ v4l2element->norm = -1;
+ v4l2element->channel = -1;
+ v4l2element->output = -1;
+ v4l2element->frequency = 0;
+
+ v4l2element->controls = NULL;
+ v4l2element->formats = NULL;
+ v4l2element->outputs = NULL;
+ v4l2element->inputs = NULL;
+ v4l2element->norms = NULL;
+}
+
+
+static void
+gst_v4l2element_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstV4l2Element *v4l2element;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail(GST_IS_V4L2ELEMENT(object));
+ v4l2element = GST_V4L2ELEMENT(object);
+
+ switch (prop_id) {
+ case ARG_CHANNEL:
+ v4l2element->channel = g_value_get_int(value);
+ if (GST_V4L2_IS_OPEN(v4l2element) && !GST_V4L2_IS_ACTIVE(v4l2element)) {
+ if (!gst_v4l2_set_input(v4l2element, g_value_get_int(value)))
+ return;
+ }
+ break;
+ case ARG_OUTPUT:
+ v4l2element->output = g_value_get_int(value);
+ if (GST_V4L2_IS_OPEN(v4l2element) && !GST_V4L2_IS_ACTIVE(v4l2element)) {
+ if (!gst_v4l2_set_output(v4l2element, g_value_get_int(value)))
+ return;
+ }
+ break;
+ case ARG_NORM:
+ v4l2element->norm = g_value_get_int(value);
+ if (GST_V4L2_IS_OPEN(v4l2element) && !GST_V4L2_IS_ACTIVE(v4l2element)) {
+ if (!gst_v4l2_set_norm(v4l2element, g_value_get_int(value)))
+ return;
+ }
+ break;
+ case ARG_FREQUENCY:
+ v4l2element->frequency = g_value_get_ulong(value);
+ if (GST_V4L2_IS_OPEN(v4l2element) && !GST_V4L2_IS_ACTIVE(v4l2element)) {
+ if (!gst_v4l2_set_frequency(v4l2element, g_value_get_ulong(value)))
+ return;
+ }
+ break;
+ case ARG_ATTRIBUTE:
+ if (GST_V4L2_IS_OPEN(v4l2element)) {
+ gst_v4l2_set_attribute(v4l2element,
+ ((GstV4l2Attribute*)g_value_get_pointer(value))->index,
+ ((GstV4l2Attribute*)g_value_get_pointer(value))->value);
+ }
+ break;
+ case ARG_DEVICE:
+ if (!GST_V4L2_IS_OPEN(v4l2element)) {
+ if (v4l2element->device)
+ g_free(v4l2element->device);
+ v4l2element->device = g_strdup(g_value_get_string(value));
+ }
+ break;
+ case ARG_DISPLAY:
+ if (!gst_v4l2_set_display(v4l2element, g_value_get_string(value)))
+ return;
+ break;
+ case ARG_VIDEOWINDOW:
+ if (GST_V4L2_IS_OPEN(v4l2element)) {
+ gst_v4l2_set_window(v4l2element,
+ ((GstV4l2Rect*)g_value_get_pointer(value))->x,
+ ((GstV4l2Rect*)g_value_get_pointer(value))->y,
+ ((GstV4l2Rect*)g_value_get_pointer(value))->w,
+ ((GstV4l2Rect*)g_value_get_pointer(value))->h);
+ }
+ break;
+ case ARG_CLIPPING:
+ if (GST_V4L2_IS_OPEN(v4l2element)) {
+ gint i;
+ struct v4l2_clip *clips;
+ GList *list = (GList*)g_value_get_pointer(value);
+ clips = g_malloc(sizeof(struct v4l2_clip) * g_list_length(list));
+ for (i=0;i<g_list_length(list);i++)
+ {
+ clips[i].x = ((GstV4l2Rect*)g_list_nth_data(list, i))->x;
+ clips[i].y = ((GstV4l2Rect*)g_list_nth_data(list, i))->y;
+ clips[i].width = ((GstV4l2Rect*)g_list_nth_data(list, i))->w;
+ clips[i].height = ((GstV4l2Rect*)g_list_nth_data(list, i))->h;
+ }
+ gst_v4l2_set_clips(v4l2element, clips, g_list_length(list));
+ g_free(clips);
+ }
+ break;
+ case ARG_DO_OVERLAY:
+ if (GST_V4L2_IS_OPEN(v4l2element)) {
+ if (!gst_v4l2_enable_overlay(v4l2element, g_value_get_boolean(value)))
+ return;
+ }
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_v4l2element_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstV4l2Element *v4l2element;
+ gint temp_i = 0;
+ gulong temp_ul = 0;
+ GList *list = NULL;
+
+ /* it's not null if we got it, but it might not be ours */
+ g_return_if_fail(GST_IS_V4L2ELEMENT(object));
+ v4l2element = GST_V4L2ELEMENT(object);
+
+ switch (prop_id) {
+ case ARG_CHANNEL:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ gst_v4l2_get_input(v4l2element, &temp_i);
+ g_value_set_int(value, temp_i);
+ break;
+ case ARG_CHANNEL_NAMES:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ list = gst_v4l2_get_input_names(v4l2element);
+ g_value_set_pointer(value, list);
+ break;
+ case ARG_OUTPUT:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ gst_v4l2_get_output(v4l2element, &temp_i);
+ g_value_set_int(value, temp_i);
+ break;
+ case ARG_OUTPUT_NAMES:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ list = gst_v4l2_get_output_names(v4l2element);
+ g_value_set_pointer(value, list);
+ break;
+ case ARG_NORM:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ gst_v4l2_get_norm(v4l2element, &temp_i);
+ g_value_set_int(value, temp_i);
+ break;
+ case ARG_NORM_NAMES:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ list = gst_v4l2_get_norm_names(v4l2element);
+ g_value_set_pointer(value, list);
+ break;
+ case ARG_HAS_TUNER:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ temp_i = gst_v4l2_has_tuner(v4l2element);
+ g_value_set_boolean(value, temp_i>0?TRUE:FALSE);
+ break;
+ case ARG_FREQUENCY:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ gst_v4l2_get_frequency(v4l2element, &temp_ul);
+ g_value_set_ulong(value, temp_ul);
+ break;
+ case ARG_SIGNAL_STRENGTH:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ gst_v4l2_signal_strength(v4l2element, &temp_ul);
+ g_value_set_ulong(value, temp_ul);
+ break;
+ case ARG_HAS_AUDIO:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ temp_i = gst_v4l2_has_audio(v4l2element);
+ g_value_set_boolean(value, temp_i>0?TRUE:FALSE);
+ break;
+ case ARG_ATTRIBUTE:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ gst_v4l2_get_attribute(v4l2element,
+ ((GstV4l2Attribute*)g_value_get_pointer(value))->index, &temp_i);
+ ((GstV4l2Attribute*)g_value_get_pointer(value))->value = temp_i;
+ break;
+ case ARG_ATTRIBUTE_SETS:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ list = gst_v4l2_get_attributes(v4l2element);
+ g_value_set_pointer(value, list);
+ break;
+ case ARG_DEVICE:
+ g_value_set_string(value, g_strdup(v4l2element->device));
+ break;
+ case ARG_DEVICE_NAME:
+ if (GST_V4L2_IS_OPEN(v4l2element))
+ g_value_set_string(value, g_strdup(v4l2element->vcap.name));
+ break;
+ case ARG_DEVICE_HAS_CAPTURE:
+ if (GST_V4L2_IS_OPEN(v4l2element) &&
+ (v4l2element->vcap.type == V4L2_TYPE_CODEC ||
+ v4l2element->vcap.type == V4L2_TYPE_CAPTURE) &&
+ v4l2element->vcap.flags & V4L2_FLAG_STREAMING)
+ temp_i = 1;
+ g_value_set_boolean(value, temp_i>0?TRUE:FALSE);
+ break;
+ case ARG_DEVICE_HAS_OVERLAY:
+ if (GST_V4L2_IS_OPEN(v4l2element) &&
+ v4l2element->vcap.flags & V4L2_FLAG_PREVIEW)
+ temp_i = 1;
+ g_value_set_boolean(value, temp_i>0?TRUE:FALSE);
+ break;
+ case ARG_DEVICE_HAS_CODEC:
+ if (GST_V4L2_IS_OPEN(v4l2element) &&
+ v4l2element->vcap.type == V4L2_TYPE_CODEC)
+ temp_i = 1;
+ g_value_set_boolean(value, temp_i>0?TRUE:FALSE);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static GstElementStateReturn
+gst_v4l2element_change_state (GstElement *element)
+{
+ GstV4l2Element *v4l2element;
+
+ g_return_val_if_fail(GST_IS_V4L2ELEMENT(element), GST_STATE_FAILURE);
+
+ v4l2element = GST_V4L2ELEMENT(element);
+
+ /* if going down into NULL state, close the device if it's open
+ * if going to READY, open the device (and set some options)
+ */
+ switch (GST_STATE_TRANSITION(element)) {
+ case GST_STATE_NULL_TO_READY:
+ if (!gst_v4l2_open(v4l2element))
+ return GST_STATE_FAILURE;
+
+ /* now, sync options */
+ if (v4l2element->norm >= 0)
+ if (!gst_v4l2_set_norm(v4l2element, v4l2element->norm))
+ return GST_STATE_FAILURE;
+ if (v4l2element->channel >= 0)
+ if (!gst_v4l2_set_input(v4l2element, v4l2element->channel))
+ return GST_STATE_FAILURE;
+ if (v4l2element->output >= 0)
+ if (!gst_v4l2_set_output(v4l2element, v4l2element->output))
+ return GST_STATE_FAILURE;
+ if (v4l2element->frequency > 0)
+ if (!gst_v4l2_set_frequency(v4l2element, v4l2element->frequency))
+ return GST_STATE_FAILURE;
+ break;
+ case GST_STATE_READY_TO_NULL:
+ if (!gst_v4l2_close(v4l2element))
+ return GST_STATE_FAILURE;
+ break;
+ }
+
+ if (GST_ELEMENT_CLASS(parent_class)->change_state)
+ return GST_ELEMENT_CLASS(parent_class)->change_state(element);
+
+ return GST_STATE_SUCCESS;
+}
+
+
+static gboolean
+plugin_init (GModule *module,
+ GstPlugin *plugin)
+{
+ GstElementFactory *factory;
+
+ /* create an elementfactory for the v4l2element */
+ factory = gst_element_factory_new("v4l2element", GST_TYPE_V4L2ELEMENT,
+ &gst_v4l2element_details);
+ g_return_val_if_fail(factory != NULL, FALSE);
+ gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory));
+
+ return TRUE;
+}
+
+
+GstPluginDesc plugin_desc = {
+ GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "v4l2element",
+ plugin_init
+};
diff --git a/sys/v4l2/gstv4l2element.h b/sys/v4l2/gstv4l2element.h
new file mode 100644
index 00000000..617aad6d
--- /dev/null
+++ b/sys/v4l2/gstv4l2element.h
@@ -0,0 +1,110 @@
+/* G-Streamer generic V4L2 element
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_V4L2ELEMENT_H__
+#define __GST_V4L2ELEMENT_H__
+
+#include <gst/gst.h>
+#include <sys/types.h>
+#include <linux/types.h>
+#include <linux/videodev2.h>
+
+
+#define GST_TYPE_V4L2ELEMENT \
+ (gst_v4l2element_get_type())
+#define GST_V4L2ELEMENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_V4L2ELEMENT, GstV4l2Element))
+#define GST_V4L2ELEMENT_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_V4L2ELEMENT, GstV4l2ElementClass))
+#define GST_IS_V4L2ELEMENT(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_V4L2ELEMENT))
+#define GST_IS_V4L2ELEMENT_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_V4L2ELEMENT))
+
+
+typedef struct _GstV4l2Element GstV4l2Element;
+typedef struct _GstV4l2ElementClass GstV4l2ElementClass;
+
+typedef struct _GstV4l2Rect {
+ gint x, y, w, h;
+} GstV4l2Rect;
+
+typedef enum {
+ GST_V4L2_ATTRIBUTE_VALUE_TYPE_INT,
+ GST_V4L2_ATTRIBUTE_VALUE_TYPE_BOOLEAN,
+ GST_V4L2_ATTRIBUTE_VALUE_TYPE_BUTTON,
+ GST_V4L2_ATTRIBUTE_VALUE_TYPE_LIST,
+} GstV4l2AttributeValueType;
+
+typedef enum {
+ GST_V4L2_ATTRIBUTE_TYPE_VIDEO,
+ GST_V4L2_ATTRIBUTE_TYPE_AUDIO,
+ GST_V4L2_ATTRIBUTE_TYPE_EFFECT,
+} GstV4l2AttributeType;
+
+typedef struct _GstV4l2Attribute {
+ gint index;
+ gchar *name;
+ GstV4l2AttributeType type;
+ GstV4l2AttributeValueType val_type;
+ gint min, max, value;
+ GList *list_items; /* in case of 'list' */
+} GstV4l2Attribute;
+
+struct _GstV4l2Element {
+ GstElement element;
+
+ /* the video device */
+ char *device;
+
+ /* the video-device's file descriptor */
+ gint video_fd;
+
+ /* the video buffer (mmap()'ed) */
+ guint8 **buffer;
+
+ /* the video-device's capabilities */
+ struct v4l2_capability vcap;
+
+ /* the toys available to us */
+ GList /*v4l2_fmtdesc*/ *formats; /* list of available capture formats */
+ GList /*v4l2_input*/ *inputs;
+ GList /*v4l2_output*/ *outputs;
+ GList /*v4l2_enumstd*/ *norms;
+ GList /*v4l2_queryctrl*/ *controls;
+ GList /*GList:v4l2_querymenu*/ *menus;
+
+ /* and last but not least, the current video window */
+ struct v4l2_window vwin;
+
+ /* caching values */
+ gint channel;
+ gint output;
+ gint norm;
+ gulong frequency;
+};
+
+struct _GstV4l2ElementClass {
+ GstElementClass parent_class;
+};
+
+
+GType gst_v4l2element_get_type (void);
+
+#endif /* __GST_V4L2ELEMENT_H__ */
diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c
new file mode 100644
index 00000000..7dce792d
--- /dev/null
+++ b/sys/v4l2/gstv4l2src.c
@@ -0,0 +1,837 @@
+/* G-Streamer Video4linux2 video-capture plugin
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <string.h>
+#include <sys/time.h>
+#include "v4l2src_calls.h"
+
+
+static GstElementDetails gst_v4l2src_details = {
+ "Video (video4linux2) Source",
+ "Source/Video",
+ "Reads frames (compressed or uncompressed) from a video4linux2 device",
+ VERSION,
+ "Ronald Bultje <rbultje@ronald.bitfreak.net>",
+ "(C) 2002",
+};
+
+/* V4l2Src signals and args */
+enum {
+ /* FILL ME */
+ LAST_SIGNAL
+};
+
+/* arguments */
+enum {
+ ARG_0,
+ ARG_WIDTH,
+ ARG_HEIGHT,
+ ARG_PALETTE,
+ ARG_PALETTE_NAMES,
+ ARG_FOURCC,
+ ARG_FOURCC_LIST,
+ ARG_NUMBUFS,
+ ARG_BUFSIZE
+};
+
+
+/* init functions */
+static void gst_v4l2src_class_init (GstV4l2SrcClass *klass);
+static void gst_v4l2src_init (GstV4l2Src *v4l2src);
+
+/* pad/buffer functions */
+static GstPadConnectReturn gst_v4l2src_srcconnect (GstPad *pad,
+ GstCaps *caps);
+static GstBuffer * gst_v4l2src_get (GstPad *pad);
+
+/* get/set params */
+static void gst_v4l2src_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gst_v4l2src_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+
+/* state handling */
+static GstElementStateReturn gst_v4l2src_change_state (GstElement *element);
+
+/* bufferpool functions */
+static GstBuffer * gst_v4l2src_buffer_new (GstBufferPool *pool,
+ guint64 offset,
+ guint size,
+ gpointer user_data);
+static GstBuffer * gst_v4l2src_buffer_copy (GstBufferPool *pool,
+ const GstBuffer *srcbuf,
+ gpointer user_data);
+static void gst_v4l2src_buffer_free (GstBufferPool *pool,
+ GstBuffer *buf,
+ gpointer user_data);
+
+
+static GstPadTemplate *src_template;
+
+static GstElementClass *parent_class = NULL;
+/*static guint gst_v4l2src_signals[LAST_SIGNAL] = { 0 }; */
+
+
+GType
+gst_v4l2src_get_type (void)
+{
+ static GType v4l2src_type = 0;
+
+ if (!v4l2src_type) {
+ static const GTypeInfo v4l2src_info = {
+ sizeof(GstV4l2SrcClass),
+ NULL,
+ NULL,
+ (GClassInitFunc) gst_v4l2src_class_init,
+ NULL,
+ NULL,
+ sizeof(GstV4l2Src),
+ 0,
+ (GInstanceInitFunc) gst_v4l2src_init,
+ NULL
+ };
+ v4l2src_type = g_type_register_static(GST_TYPE_V4L2ELEMENT,
+ "GstV4l2Src", &v4l2src_info, 0);
+ }
+ return v4l2src_type;
+}
+
+
+static void
+gst_v4l2src_class_init (GstV4l2SrcClass *klass)
+{
+ GObjectClass *gobject_class;
+ GstElementClass *gstelement_class;
+
+ gobject_class = (GObjectClass*)klass;
+ gstelement_class = (GstElementClass*)klass;
+
+ parent_class = g_type_class_ref(GST_TYPE_V4L2ELEMENT);
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_WIDTH,
+ g_param_spec_int("width","width","width",
+ G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HEIGHT,
+ g_param_spec_int("height","height","height",
+ G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PALETTE,
+ g_param_spec_int("palette","palette","palette",
+ G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PALETTE_NAMES,
+ g_param_spec_pointer("palette_name","palette_name","palette_name",
+ G_PARAM_READABLE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FOURCC,
+ g_param_spec_string("fourcc","fourcc","fourcc",
+ NULL,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FOURCC_LIST,
+ g_param_spec_pointer("fourcc_list","fourcc_list","fourcc_list",
+ G_PARAM_READABLE));
+
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NUMBUFS,
+ g_param_spec_int("num_buffers","num_buffers","num_buffers",
+ G_MININT,G_MAXINT,0,G_PARAM_READWRITE));
+ g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFSIZE,
+ g_param_spec_int("buffer_size","buffer_size","buffer_size",
+ G_MININT,G_MAXINT,0,G_PARAM_READABLE));
+
+ gobject_class->set_property = gst_v4l2src_set_property;
+ gobject_class->get_property = gst_v4l2src_get_property;
+
+ gstelement_class->change_state = gst_v4l2src_change_state;
+}
+
+
+static void
+gst_v4l2src_init (GstV4l2Src *v4l2src)
+{
+ v4l2src->srcpad = gst_pad_new_from_template(src_template, "src");
+ gst_element_add_pad(GST_ELEMENT(v4l2src), v4l2src->srcpad);
+
+ gst_pad_set_get_function(v4l2src->srcpad, gst_v4l2src_get);
+ gst_pad_set_connect_function(v4l2src->srcpad, gst_v4l2src_srcconnect);
+
+ v4l2src->bufferpool = gst_buffer_pool_new(NULL, NULL,
+ gst_v4l2src_buffer_new,
+ gst_v4l2src_buffer_copy,
+ gst_v4l2src_buffer_free,
+ v4l2src);
+
+ v4l2src->palette = 0; /* means 'any' - user can specify a specific palette */
+ v4l2src->width = 160;
+ v4l2src->height = 120;
+ v4l2src->breq.count = 0;
+}
+
+
+static GstCaps *
+gst_v4l2src_v4l2fourcc_to_caps (guint32 fourcc,
+ gint width,
+ gint height,
+ gboolean compressed)
+{
+ GstCaps *capslist = NULL, *caps;
+
+ switch (fourcc) {
+ case V4L2_PIX_FMT_MJPEG: /* v4l2_fourcc('M','J','P','G') */
+ caps = gst_caps_new("v4l2src_caps",
+ "video/jpeg",
+ gst_props_new(
+ "width", GST_PROPS_INT(width),
+ "height", GST_PROPS_INT(height),
+ NULL));
+ capslist = gst_caps_append(capslist, caps);
+ break;
+ case V4L2_PIX_FMT_RGB332:
+ case V4L2_PIX_FMT_RGB555:
+ case V4L2_PIX_FMT_RGB555X:
+ case V4L2_PIX_FMT_RGB565:
+ case V4L2_PIX_FMT_RGB565X:
+ case V4L2_PIX_FMT_RGB24:
+ case V4L2_PIX_FMT_BGR24:
+ case V4L2_PIX_FMT_RGB32:
+ case V4L2_PIX_FMT_BGR32: {
+ guint depth=0, bpp=0;
+ gint endianness=0;
+ gulong r_mask=0, b_mask=0, g_mask=0;
+ switch (fourcc) {
+ case V4L2_PIX_FMT_RGB332:
+ bpp = depth = 8;
+ endianness = G_BYTE_ORDER; /* 'like, whatever' */
+ r_mask = 0xe0; g_mask = 0x1c; b_mask = 0x03;
+ break;
+ case V4L2_PIX_FMT_RGB555:
+ bpp = 16; depth = 15;
+ endianness = G_LITTLE_ENDIAN;
+ r_mask = 0x7c00; g_mask = 0x03e0; b_mask = 0x001f;
+ break;
+ case V4L2_PIX_FMT_RGB555X:
+ bpp = 16; depth = 15;
+ endianness = G_BIG_ENDIAN;
+ r_mask = 0x7c00; g_mask = 0x03e0; b_mask = 0x001f;
+ break;
+ case V4L2_PIX_FMT_RGB565:
+ bpp = depth = 16;
+ endianness = G_LITTLE_ENDIAN;
+ r_mask = 0xf800; g_mask = 0x07e0; b_mask = 0x001f;
+ break;
+ case V4L2_PIX_FMT_RGB565X:
+ bpp = depth = 16;
+ endianness = G_BIG_ENDIAN;
+ r_mask = 0xf800; g_mask = 0x07e0; b_mask = 0x001f;
+ break;
+ case V4L2_PIX_FMT_RGB24:
+ bpp = depth = 24;
+ endianness = G_BIG_ENDIAN;
+ r_mask = 0xff0000; g_mask = 0x00ff00; b_mask = 0x0000ff;
+ break;
+ case V4L2_PIX_FMT_BGR24:
+ bpp = depth = 24;
+ endianness = G_LITTLE_ENDIAN;
+ r_mask = 0xff0000; g_mask = 0x00ff00; b_mask = 0x0000ff;
+ break;
+ case V4L2_PIX_FMT_RGB32:
+ bpp = depth = 32;
+ endianness = G_BIG_ENDIAN;
+ r_mask = 0x00ff0000; g_mask = 0x0000ff00; b_mask = 0x000000ff;
+ break;
+ case V4L2_PIX_FMT_BGR32:
+ endianness = G_LITTLE_ENDIAN;
+ bpp = depth = 32;
+ r_mask = 0x00ff0000; g_mask = 0x0000ff00; b_mask = 0x000000ff;
+ break;
+ }
+ caps = gst_caps_new("v4l2src_caps",
+ "video/raw",
+ gst_props_new(
+ "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')),
+ "width", GST_PROPS_INT(width),
+ "height", GST_PROPS_INT(height),
+ "bpp", GST_PROPS_INT(bpp),
+ "depth", GST_PROPS_INT(depth),
+ "red_mask", GST_PROPS_INT(r_mask),
+ "green_mask", GST_PROPS_INT(g_mask),
+ "blue_mask", GST_PROPS_INT(b_mask),
+ "endianness", GST_PROPS_INT(endianness),
+ NULL));
+ capslist = gst_caps_append(capslist, caps);
+ break; }
+ case V4L2_PIX_FMT_YUV420: /* I420/IYUV */
+ caps = gst_caps_new("v4l2src_caps",
+ "video/raw",
+ gst_props_new(
+ "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','4','2','0')),
+ "width", GST_PROPS_INT(width),
+ "height", GST_PROPS_INT(height),
+ NULL));
+ capslist = gst_caps_append(capslist, caps);
+ caps = gst_caps_new("v4l2src_caps",
+ "video/raw",
+ gst_props_new(
+ "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','Y','U','V')),
+ "width", GST_PROPS_INT(width),
+ "height", GST_PROPS_INT(height),
+ NULL));
+ capslist = gst_caps_append(capslist, caps);
+ break;
+ case V4L2_PIX_FMT_YUYV:
+ caps = gst_caps_new("v4l2src_caps",
+ "video/raw",
+ gst_props_new(
+ "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','U','Y','2')),
+ "width", GST_PROPS_INT(width),
+ "height", GST_PROPS_INT(height),
+ NULL));
+ capslist = gst_caps_append(capslist, caps);
+ break;
+ default:
+ break;
+ }
+
+ /* add the standard one */
+ if (compressed) {
+ guint32 print_format = GUINT32_FROM_LE(fourcc);
+ gchar *print_format_str = (gchar *) &print_format, *string_format;
+ gint i;
+ for (i=0;i<4;i++)
+ print_format_str[i] = g_ascii_tolower(print_format_str[i]);
+ string_format = g_strdup_printf("video/%4.4s", print_format_str);
+ caps = gst_caps_new("v4l2src_caps",
+ string_format,
+ gst_props_new(
+ "width", GST_PROPS_INT(width),
+ "height", GST_PROPS_INT(height),
+ NULL));
+ capslist = gst_caps_append(capslist, caps);
+ g_free(string_format);
+ } else {
+ caps = gst_caps_new("v4l2src_caps",
+ "video/raw",
+ gst_props_new(
+ "format", GST_PROPS_FOURCC(fourcc),
+ "width", GST_PROPS_INT(width),
+ "height", GST_PROPS_INT(height),
+ NULL));
+ capslist = gst_caps_append(capslist, caps);
+ }
+
+ return capslist;
+}
+
+
+static GList *
+gst_v4l2_caps_to_v4l2fourcc (GstV4l2Src *v4l2src,
+ GstCaps *capslist)
+{
+ GList *fourcclist = NULL;
+ GstCaps *caps;
+ guint32 fourcc;
+ gint i;
+
+ for (caps = capslist;caps != NULL; caps = caps->next) {
+ const gchar *format = gst_caps_get_mime(caps);
+
+ if (!strcmp(format, "video/raw")) {
+ /* non-compressed */
+ gst_caps_get_fourcc_int(caps, "format", &fourcc);
+ switch (fourcc) {
+ case GST_MAKE_FOURCC('I','4','2','0'):
+ case GST_MAKE_FOURCC('I','Y','U','V'):
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_YUV420);
+ break;
+ case GST_MAKE_FOURCC('Y','U','Y','2'):
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_YUYV);
+ break;
+ case GST_MAKE_FOURCC('R','G','B',' '): {
+ gint depth, endianness;
+ gst_caps_get_int(caps, "depth", &depth);
+ gst_caps_get_int(caps, "endianness", &endianness);
+ if (depth == 8) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB332);
+ } else if (depth == 15 && endianness == G_LITTLE_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB555);
+ } else if (depth == 15 && endianness == G_BIG_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB555X);
+ } else if (depth == 16 && endianness == G_LITTLE_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB565);
+ } else if (depth == 16 && endianness == G_BIG_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB565X);
+ } else if (depth == 24 && endianness == G_LITTLE_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_BGR24);
+ } else if (depth == 24 && endianness == G_BIG_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB24);
+ } else if (depth == 32 && endianness == G_LITTLE_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_BGR32);
+ } else if (depth == 32 && endianness == G_BIG_ENDIAN) {
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB32);
+ }
+ break; }
+ }
+
+ for (i=0;i<g_list_length(GST_V4L2ELEMENT(v4l2src)->formats);i++) {
+ struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *) g_list_nth_data(GST_V4L2ELEMENT(v4l2src)->formats, i);
+ if (fmt->pixelformat == fourcc)
+ fourcclist = g_list_append(fourcclist, (gpointer)fourcc);
+ }
+ } else {
+ /* compressed */
+ gchar *format_us;
+ gint i;
+ if (strncmp(format, "video/", 6))
+ continue;
+ format = &format[6];
+ if (strlen(format) != 4)
+ continue;
+ format_us = g_strdup(format);
+ for (i=0;i<4;i++)
+ format_us[i] = g_ascii_toupper(format_us[i]);
+ fourcc = GST_MAKE_FOURCC(format_us[0],format_us[1],format_us[2],format_us[3]);
+ switch (fourcc) {
+ case GST_MAKE_FOURCC('J','P','E','G'):
+ fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_MJPEG);
+ break;
+ }
+ fourcclist = g_list_append(fourcclist, (gpointer)fourcc);
+ }
+ }
+
+ return fourcclist;
+}
+
+
+static GstCaps *
+gst_v4l2src_caps_intersect (GstCaps *caps1,
+ GstCaps *_caps2)
+{
+ GstCaps *_list = NULL;
+
+ if (!_caps2)
+ return caps1;
+
+ for (;caps1!=NULL;caps1=caps1->next) {
+ GstCaps *caps2 = _caps2;
+
+ for (;caps2!=NULL;caps2=caps2->next) {
+ if (!strcmp(gst_caps_get_mime(caps1), gst_caps_get_mime(caps2))) {
+ if (!strcmp(gst_caps_get_mime(caps1), "video/raw")) {
+ guint32 fmt1, fmt2;
+ gst_caps_get_fourcc_int(caps1, "format", &fmt1);
+ gst_caps_get_fourcc_int(caps2, "format", &fmt2);
+ if (fmt1 == fmt2)
+ goto try_it_out;
+ } else if (!strncmp(gst_caps_get_mime(caps1), "video/", 6)) {
+ goto try_it_out;
+ }
+ continue;
+ }
+ continue;
+
+ try_it_out:
+ if (!strcmp(gst_caps_get_mime(caps1), "video/raw")) {
+ GstCaps *list = _list;
+ for (;list!=NULL;list=list->next) {
+ if (!strcmp(gst_caps_get_mime(list), gst_caps_get_mime(caps1))) {
+ guint32 fmt1, fmt2;
+ gst_caps_get_fourcc_int(caps1, "format", &fmt1);
+ gst_caps_get_fourcc_int(list, "format", &fmt2);
+ if (fmt1 == fmt2)
+ break;
+ }
+ }
+ if (list == NULL)
+ goto add_it;
+ } else {
+ GstCaps *list = _list;
+ for (;list!=NULL;list=list->next) {
+ if (!strcmp(gst_caps_get_mime(list), gst_caps_get_mime(caps1))) {
+ break;
+ }
+ }
+ if (list == NULL)
+ goto add_it;
+ }
+ continue;
+
+ add_it:
+ _list = gst_caps_append(_list, gst_caps_copy_1(caps1));
+ break;
+ }
+ }
+
+ return _list;
+}
+
+
+static GstPadConnectReturn
+gst_v4l2src_srcconnect (GstPad *pad,
+ GstCaps *vscapslist)
+{
+ GstV4l2Src *v4l2src;
+ GstV4l2Element *v4l2element;
+ GstCaps *owncapslist;
+ GstCaps *caps;
+ GList *fourccs;
+ gint i;
+
+ v4l2src = GST_V4L2SRC(gst_pad_get_parent (pad));
+ v4l2element = GST_V4L2ELEMENT(v4l2src);
+
+ /* clean up if we still haven't cleaned up our previous
+ * capture session */
+ if (GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src)))
+ if (!gst_v4l2src_capture_deinit(v4l2src))
+ return GST_PAD_CONNECT_REFUSED;
+
+ /* build our own capslist */
+ if (v4l2src->palette) {
+ struct v4l2_fmtdesc *format = g_list_nth_data(v4l2element->formats, v4l2src->palette);
+ owncapslist = gst_v4l2src_v4l2fourcc_to_caps(format->pixelformat,
+ v4l2src->width, v4l2src->height,
+ format->flags & V4L2_FMT_FLAG_COMPRESSED);
+ } else {
+ gint i;
+ owncapslist = NULL;
+ for (i=0;i<g_list_length(v4l2element->formats);i++) {
+ struct v4l2_fmtdesc *format = g_list_nth_data(v4l2element->formats, i);
+ caps = gst_v4l2src_v4l2fourcc_to_caps(format->pixelformat,
+ v4l2src->width, v4l2src->height,
+ format->flags & V4L2_FMT_FLAG_COMPRESSED);
+ owncapslist = gst_caps_append(owncapslist, caps);
+ }
+ }
+
+ /* and now, get the caps that we have in common */
+ if (!(caps = gst_v4l2src_caps_intersect(owncapslist, vscapslist)))
+ return GST_PAD_CONNECT_REFUSED;
+
+ /* and get them back to V4L2-compatible fourcc codes */
+ if (!(fourccs = gst_v4l2_caps_to_v4l2fourcc(v4l2src, caps)))
+ return GST_PAD_CONNECT_REFUSED;
+
+ /* we now have the fourcc codes. try out each of them */
+ for (i=0;i<g_list_length(fourccs);i++) {
+ guint32 fourcc = (guint32)g_list_nth_data(fourccs, i);
+ gint n;
+ for (n=0;n<g_list_length(v4l2element->formats);n++) {
+ struct v4l2_fmtdesc *format = g_list_nth_data(v4l2element->formats, n);
+ if (format->pixelformat == fourcc) {
+ /* we found the pixelformat! - try it out */
+ if (gst_v4l2src_set_capture(v4l2src, format,
+ v4l2src->width, v4l2src->height)) {
+ /* it fits! Now, get the proper counterpart and retry
+ * it on the other side (again...) - if it works, we're
+ * done -> GST_PAD_CONNECT_OK */
+ GstCaps *lastcaps = gst_v4l2src_v4l2fourcc_to_caps(format->pixelformat,
+ v4l2src->format.fmt.pix.width, v4l2src->format.fmt.pix.height,
+ format->flags & V4L2_FMT_FLAG_COMPRESSED);
+ GstCaps *onecaps;
+ for (;lastcaps != NULL; lastcaps = lastcaps->next) {
+ onecaps = gst_caps_copy_1(lastcaps);
+ if (gst_pad_try_set_caps(v4l2src->srcpad, onecaps))
+ if (gst_v4l2src_capture_init(v4l2src))
+ return GST_PAD_CONNECT_OK;
+ }
+ }
+ }
+ }
+ }
+
+ return GST_PAD_CONNECT_REFUSED;
+}
+
+
+static GstBuffer*
+gst_v4l2src_get (GstPad *pad)
+{
+ GstV4l2Src *v4l2src;
+ GstBuffer *buf;
+ gint num;
+
+ g_return_val_if_fail (pad != NULL, NULL);
+
+ v4l2src = GST_V4L2SRC(gst_pad_get_parent (pad));
+
+ buf = gst_buffer_new_from_pool(v4l2src->bufferpool, 0, 0);
+ if (!buf) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Failed to create a new GstBuffer");
+ return NULL;
+ }
+
+ /* grab a frame from the device */
+ if (!gst_v4l2src_grab_frame(v4l2src, &num))
+ return NULL;
+ GST_BUFFER_DATA(buf) = GST_V4L2ELEMENT(v4l2src)->buffer[num];
+ GST_BUFFER_SIZE(buf) = v4l2src->bufsettings.bytesused;
+ GST_BUFFER_MAXSIZE(buf) = v4l2src->bufsettings.length;
+ if (!v4l2src->first_timestamp)
+ v4l2src->first_timestamp = v4l2src->bufsettings.timestamp;
+ GST_BUFFER_TIMESTAMP(buf) = v4l2src->bufsettings.length - v4l2src->first_timestamp;
+
+ return buf;
+}
+
+
+static void
+gst_v4l2src_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GstV4l2Src *v4l2src;
+
+ g_return_if_fail(GST_IS_V4L2SRC(object));
+ v4l2src = GST_V4L2SRC(object);
+
+ switch (prop_id) {
+ case ARG_WIDTH:
+ if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) {
+ v4l2src->width = g_value_get_int(value);
+ }
+ break;
+
+ case ARG_HEIGHT:
+ if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) {
+ v4l2src->height = g_value_get_int(value);
+ }
+ break;
+
+ case ARG_PALETTE:
+ if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) {
+ v4l2src->palette = g_value_get_int(value);
+ }
+ break;
+
+ case ARG_FOURCC:
+ if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) {
+ gint i;
+ const gchar *formatstr = g_value_get_string(value);
+ guint32 fourcc = GST_MAKE_FOURCC(formatstr[0],formatstr[1],formatstr[2],formatstr[3]);
+ for (i=0;i<g_list_length(GST_V4L2ELEMENT(v4l2src)->formats);i++) {
+ struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *) g_list_nth_data(GST_V4L2ELEMENT(v4l2src)->formats, i);
+ if (fmt->pixelformat == fourcc)
+ v4l2src->palette = i;
+ }
+ }
+ break;
+
+ case ARG_NUMBUFS:
+ if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) {
+ v4l2src->breq.count = g_value_get_int(value);
+ }
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static void
+gst_v4l2src_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GstV4l2Src *v4l2src;
+
+ g_return_if_fail(GST_IS_V4L2SRC(object));
+ v4l2src = GST_V4L2SRC(object);
+
+ switch (prop_id) {
+ case ARG_WIDTH:
+ g_value_set_int(value, v4l2src->width);
+ break;
+
+ case ARG_HEIGHT:
+ g_value_set_int(value, v4l2src->height);
+ break;
+
+ case ARG_PALETTE:
+ g_value_set_int(value, v4l2src->palette);
+ break;
+
+ case ARG_PALETTE_NAMES:
+ g_value_set_pointer(value, gst_v4l2src_get_format_list(v4l2src));
+ break;
+
+ case ARG_FOURCC: {
+ struct v4l2_fmtdesc *fmt = g_list_nth_data(GST_V4L2ELEMENT(v4l2src)->formats, v4l2src->palette);
+ guint32 print_format = GUINT32_FROM_LE(fmt->pixelformat);
+ gchar *print_format_str = (gchar *) &print_format;
+ g_value_set_string(value, g_strndup(print_format_str, 4));
+ break; }
+
+ case ARG_FOURCC_LIST:
+ g_value_set_pointer(value, gst_v4l2src_get_fourcc_list(v4l2src));
+ break;
+
+ case ARG_NUMBUFS:
+ g_value_set_int(value, v4l2src->breq.count);
+ break;
+
+ case ARG_BUFSIZE:
+ g_value_set_int(value, v4l2src->format.fmt.pix.sizeimage);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+static GstElementStateReturn
+gst_v4l2src_change_state (GstElement *element)
+{
+ GstV4l2Src *v4l2src;
+ gint transition = GST_STATE_TRANSITION (element);
+ GstElementStateReturn parent_return;
+
+ g_return_val_if_fail(GST_IS_V4L2SRC(element), GST_STATE_FAILURE);
+ v4l2src = GST_V4L2SRC(element);
+
+ if (GST_ELEMENT_CLASS (parent_class)->change_state) {
+ parent_return = GST_ELEMENT_CLASS (parent_class)->change_state (element);
+ if (parent_return != GST_STATE_SUCCESS)
+ return parent_return;
+ }
+
+ switch (transition) {
+ case GST_STATE_NULL_TO_READY:
+ if (!gst_v4l2src_get_capture(v4l2src))
+ return GST_STATE_FAILURE;
+ break;
+ case GST_STATE_READY_TO_PAUSED:
+ v4l2src->first_timestamp = 0;
+ /* buffer setup moved to capsnego */
+ break;
+ case GST_STATE_PAUSED_TO_PLAYING:
+ /* queue all buffer, start streaming capture */
+ if (!gst_v4l2src_capture_start(v4l2src))
+ return GST_STATE_FAILURE;
+ break;
+ case GST_STATE_PLAYING_TO_PAUSED:
+ /* de-queue all queued buffers */
+ if (!gst_v4l2src_capture_stop(v4l2src))
+ return GST_STATE_FAILURE;
+ break;
+ case GST_STATE_PAUSED_TO_READY:
+ /* stop capturing, unmap all buffers */
+ if (!gst_v4l2src_capture_deinit(v4l2src))
+ return GST_STATE_FAILURE;
+ break;
+ case GST_STATE_READY_TO_NULL:
+ break;
+ }
+
+ return GST_STATE_SUCCESS;
+}
+
+
+static GstBuffer*
+gst_v4l2src_buffer_new (GstBufferPool *pool,
+ guint64 offset,
+ guint size,
+ gpointer user_data)
+{
+ GstBuffer *buffer;
+
+ buffer = gst_buffer_new();
+ if (!buffer) return NULL;
+ /* TODO: add interlacing info to buffer as metadata (height>288 or 240 = topfieldfirst, else noninterlaced) */
+
+ return buffer;
+}
+
+
+static GstBuffer*
+gst_v4l2src_buffer_copy (GstBufferPool *pool,
+ const GstBuffer *srcbuf,
+ gpointer user_data)
+{
+ GstBuffer *buffer;
+
+ buffer = gst_buffer_new();
+ if (!buffer) return NULL;
+ GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(srcbuf));
+ if (!GST_BUFFER_DATA(buffer)) return NULL;
+ GST_BUFFER_MAXSIZE(buffer) = GST_BUFFER_SIZE(srcbuf);
+ GST_BUFFER_SIZE(buffer) = GST_BUFFER_SIZE(srcbuf);
+ memcpy(GST_BUFFER_DATA(buffer), GST_BUFFER_DATA(srcbuf), GST_BUFFER_SIZE(srcbuf));
+ GST_BUFFER_TIMESTAMP(buffer) = GST_BUFFER_TIMESTAMP(srcbuf);
+
+ return buffer;
+}
+
+
+static void
+gst_v4l2src_buffer_free (GstBufferPool *pool,
+ GstBuffer *buf,
+ gpointer user_data)
+{
+ GstV4l2Src *v4l2src = GST_V4L2SRC(user_data);
+ int n;
+
+ for (n=0;n<v4l2src->breq.count;n++)
+ if (GST_BUFFER_DATA(buf) == GST_V4L2ELEMENT(v4l2src)->buffer[n]) {
+ gst_v4l2src_requeue_frame(v4l2src, n);
+ return;
+ }
+
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Couldn\'t find the buffer");
+}
+
+
+static gboolean
+plugin_init (GModule *module,
+ GstPlugin *plugin)
+{
+ GstElementFactory *factory;
+
+ /* create an elementfactory for the v4l2src */
+ factory = gst_element_factory_new("v4l2src", GST_TYPE_V4L2SRC,
+ &gst_v4l2src_details);
+ g_return_val_if_fail(factory != NULL, FALSE);
+
+ src_template = gst_pad_template_new("src",
+ GST_PAD_SRC,
+ GST_PAD_ALWAYS,
+ NULL);
+
+ gst_element_factory_add_pad_template(factory, src_template);
+
+ gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE(factory));
+
+ return TRUE;
+}
+
+
+GstPluginDesc plugin_desc = {
+ GST_VERSION_MAJOR,
+ GST_VERSION_MINOR,
+ "v4l2src",
+ plugin_init
+};
diff --git a/sys/v4l2/gstv4l2src.h b/sys/v4l2/gstv4l2src.h
new file mode 100644
index 00000000..004347ab
--- /dev/null
+++ b/sys/v4l2/gstv4l2src.h
@@ -0,0 +1,69 @@
+/* G-Streamer Video4linux2 video-capture plugin
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __GST_V4L2SRC_H__
+#define __GST_V4L2SRC_H__
+
+#include <gstv4l2element.h>
+
+
+#define GST_TYPE_V4L2SRC \
+ (gst_v4l2src_get_type())
+#define GST_V4L2SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2SRC,GstV4l2Src))
+#define GST_V4L2SRC_CLASS(klass) \
+ (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2SRC,GstV4l2SrcClass))
+#define GST_IS_V4L2SRC(obj) \
+ (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2SRC))
+#define GST_IS_V4L2SRC_CLASS(obj) \
+ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2SRC))
+
+
+typedef struct _GstV4l2Src GstV4l2Src;
+typedef struct _GstV4l2SrcClass GstV4l2SrcClass;
+
+struct _GstV4l2Src {
+ GstV4l2Element v4l2element;
+
+ /* pads */
+ GstPad *srcpad;
+
+ /* buffer properties */
+ struct v4l2_buffer bufsettings;
+ struct v4l2_requestbuffers breq;
+ struct v4l2_format format;
+ stamp_t first_timestamp;
+
+ /* bufferpool for the buffers we're gonna use */
+ GstBufferPool *bufferpool;
+
+ /* caching values */
+ gint width;
+ gint height;
+ gint palette;
+};
+
+struct _GstV4l2SrcClass {
+ GstV4l2ElementClass parent_class;
+};
+
+
+GType gst_v4l2src_get_type(void);
+
+#endif /* __GST_V4L2SRC_H__ */
diff --git a/sys/v4l2/v4l2-overlay_calls.c b/sys/v4l2/v4l2-overlay_calls.c
new file mode 100644
index 00000000..89cae62b
--- /dev/null
+++ b/sys/v4l2/v4l2-overlay_calls.c
@@ -0,0 +1,168 @@
+/* G-Streamer generic V4L2 element - generic V4L2 overlay handling
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+#include "v4l2_calls.h"
+
+#define DEBUG(format, args...) \
+ GST_DEBUG_ELEMENT(GST_CAT_PLUGIN_INFO, \
+ GST_ELEMENT(v4l2element), \
+ "V4L2-overlay: " format "\n", ##args)
+
+
+/******************************************************
+ * gst_v4l2_set_display():
+ * calls v4l-conf
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_display (GstV4l2Element *v4l2element,
+ const gchar *display)
+{
+ gchar *buff;
+
+ DEBUG("trying to set overlay to '%s'", display);
+
+ /* start v4l-conf */
+ buff = g_strdup_printf("v4l-conf -q -c %s -d %s",
+ v4l2element->device?v4l2element->device:"/dev/video", display);
+
+ switch (system(buff)) {
+ case -1:
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Could not start v4l-conf: %s", sys_errlist[errno]);
+ g_free(buff);
+ return FALSE;
+ case 0:
+ break;
+ default:
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "v4l-conf failed to run correctly: %s", sys_errlist[errno]);
+ g_free(buff);
+ return FALSE;
+ }
+
+ g_free(buff);
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_set_vwin():
+ * does the VIDIOC_S_WIN ioctl()
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+static gboolean
+gst_v4l2_set_vwin (GstV4l2Element *v4l2element)
+{
+ if (ioctl(v4l2element->video_fd, VIDIOC_S_WIN, &(v4l2element->vwin)) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to set the video window on device %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_set_window():
+ * sets the window where to display the video overlay
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_window (GstV4l2Element *v4l2element,
+ gint x, gint y,
+ gint w, gint h)
+{
+ DEBUG("trying to set video window to %dx%d,%d,%d", x,y,w,h);
+ GST_V4L2_CHECK_OVERLAY(v4l2element);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ v4l2element->vwin.clipcount = 0;
+ v4l2element->vwin.x = x;
+ v4l2element->vwin.y = y;
+ v4l2element->vwin.width = w;
+ v4l2element->vwin.height = h;
+
+ return gst_v4l2_set_vwin(v4l2element);
+}
+
+
+/******************************************************
+ * gst_v4l_set_clips():
+ * sets video overlay clips
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_clips (GstV4l2Element *v4l2element,
+ struct v4l2_clip *clips,
+ gint num_clips)
+{
+ DEBUG("trying to set video clipping information");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+ GST_V4L2_CHECK_OVERLAY(v4l2element);
+
+ v4l2element->vwin.clips = clips;
+ v4l2element->vwin.clipcount = num_clips;
+
+ return gst_v4l2_set_vwin(v4l2element);
+}
+
+
+/******************************************************
+ * gst_v4l_set_overlay():
+ * enables/disables actual video overlay display
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_enable_overlay (GstV4l2Element *v4l2element,
+ gboolean enable)
+{
+ gint doit = enable?1:0;
+
+ DEBUG("trying to %s overlay display", enable?"enable":"disable");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+ GST_V4L2_CHECK_OVERLAY(v4l2element);
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_PREVIEW, &doit) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to %s overlay display for device %s: %s",
+ enable?"enable":"disable", v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c
new file mode 100644
index 00000000..c24d0e4d
--- /dev/null
+++ b/sys/v4l2/v4l2_calls.c
@@ -0,0 +1,863 @@
+/* G-Streamer generic V4L2 element - generic V4L2 calls handling
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+#include "v4l2_calls.h"
+
+#define DEBUG(format, args...) \
+ GST_DEBUG_ELEMENT(GST_CAT_PLUGIN_INFO, \
+ GST_ELEMENT(v4l2element), \
+ "V4L2: " format "\n", ##args)
+
+
+/******************************************************
+ * gst_v4l2_get_capabilities():
+ * get the device's capturing capabilities
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+static gboolean
+gst_v4l2_get_capabilities (GstV4l2Element *v4l2element)
+{
+ DEBUG("getting capabilities");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_QUERYCAP, &(v4l2element->vcap)) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Error getting %s capabilities: %s",
+ v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_empty_lists() and gst_v4l2_fill_lists():
+ * fill/empty the lists of enumerations
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+static gboolean
+gst_v4l2_fill_lists (GstV4l2Element *v4l2element)
+{
+ gint n;
+
+ DEBUG("getting enumerations");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ /* create enumeration lists - let's start with format enumeration */
+ for (n=0;;n++) {
+ struct v4l2_fmtdesc format, *fmtptr;
+ format.index = n;
+ if (ioctl(v4l2element->video_fd, VIDIOC_ENUM_PIXFMT, &format) < 0) {
+ if (errno == EINVAL)
+ break; /* end of enumeration */
+ else {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get no. %d in pixelformat enumeration for %s: %s",
+ n, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+ }
+ fmtptr = g_malloc(sizeof(format));
+ memcpy(fmtptr, &format, sizeof(format));
+ v4l2element->formats = g_list_append(v4l2element->formats, fmtptr);
+ }
+
+ /* and now, the inputs */
+ for (n=0;;n++) {
+ struct v4l2_input input, *inpptr;
+ input.index = n;
+ if (ioctl(v4l2element->video_fd, VIDIOC_ENUMINPUT, &input) < 0) {
+ if (errno == EINVAL)
+ break; /* end of enumeration */
+ else {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get no. %d in input enumeration for %s: %s",
+ n, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+ }
+ inpptr = g_malloc(sizeof(input));
+ memcpy(inpptr, &input, sizeof(input));
+ v4l2element->inputs = g_list_append(v4l2element->inputs, inpptr);
+ }
+
+ /* outputs */
+ for (n=0;;n++) {
+ struct v4l2_output output, *outptr;
+ output.index = n;
+ if (ioctl(v4l2element->video_fd, VIDIOC_ENUMOUTPUT, &output) < 0) {
+ if (errno == EINVAL)
+ break; /* end of enumeration */
+ else {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get no. %d in output enumeration for %s: %s",
+ n, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+ }
+ outptr = g_malloc(sizeof(output));
+ memcpy(outptr, &output, sizeof(output));
+ v4l2element->outputs = g_list_append(v4l2element->outputs, outptr);
+ }
+
+ /* norms... */
+ for (n=0;;n++) {
+ struct v4l2_enumstd standard, *stdptr;
+ standard.index = n;
+ if (ioctl(v4l2element->video_fd, VIDIOC_ENUMSTD, &standard) < 0) {
+ if (errno == EINVAL)
+ break; /* end of enumeration */
+ else {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get no. %d in norm enumeration for %s: %s",
+ n, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+ }
+ stdptr = g_malloc(sizeof(standard));
+ memcpy(stdptr, &standard, sizeof(standard));
+ v4l2element->norms = g_list_append(v4l2element->norms, stdptr);
+ }
+
+ /* and lastly, controls+menus (if appropriate) */
+ for (n=0;;n++) {
+ struct v4l2_queryctrl control, *ctrlptr;
+ GList *menus = NULL;
+ control.id = n;
+ if (ioctl(v4l2element->video_fd, VIDIOC_QUERYCTRL, &control) < 0) {
+ if (errno == EINVAL)
+ break; /* end of enumeration */
+ else {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get no. %d in control enumeration for %s: %s",
+ n, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+ }
+ ctrlptr = g_malloc(sizeof(control));
+ memcpy(ctrlptr, &control, sizeof(control));
+ v4l2element->controls = g_list_append(v4l2element->controls, ctrlptr);
+ if (control.type == V4L2_CTRL_TYPE_MENU) {
+ struct v4l2_querymenu menu, *mptr;
+ int i;
+ menu.id = n;
+ for (i=0;;i++) {
+ menu.index = i;
+ if (ioctl(v4l2element->video_fd, VIDIOC_QUERYMENU, &menu) < 0) {
+ if (errno == EINVAL)
+ break; /* end of enumeration */
+ else {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get no. %d in menu %d enumeration for %s: %s",
+ i, n, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+ }
+ mptr = g_malloc(sizeof(menu));
+ memcpy(mptr, &menu, sizeof(menu));
+ menus = g_list_append(menus, mptr);
+ }
+ }
+ v4l2element->menus = g_list_append(v4l2element->menus, menus);
+ }
+
+ return TRUE;
+}
+
+
+static void
+gst_v4l2_empty_lists (GstV4l2Element *v4l2element)
+{
+ DEBUG("deleting enumerations");
+
+ /* empty lists */
+ while (g_list_length(v4l2element->inputs) > 0) {
+ gpointer data = g_list_nth_data(v4l2element->inputs, 0);
+ v4l2element->inputs = g_list_remove(v4l2element->inputs, data);
+ g_free(data);
+ }
+ while (g_list_length(v4l2element->outputs) > 0) {
+ gpointer data = g_list_nth_data(v4l2element->outputs, 0);
+ v4l2element->outputs = g_list_remove(v4l2element->outputs, data);
+ g_free(data);
+ }
+ while (g_list_length(v4l2element->norms) > 0) {
+ gpointer data = g_list_nth_data(v4l2element->norms, 0);
+ v4l2element->norms = g_list_remove(v4l2element->norms, data);
+ g_free(data);
+ }
+ while (g_list_length(v4l2element->formats) > 0) {
+ gpointer data = g_list_nth_data(v4l2element->formats, 0);
+ v4l2element->formats = g_list_remove(v4l2element->formats, data);
+ g_free(data);
+ }
+ while (g_list_length(v4l2element->controls) > 0) {
+ gpointer data = g_list_nth_data(v4l2element->controls, 0);
+ v4l2element->controls = g_list_remove(v4l2element->controls, data);
+ g_free(data);
+ }
+ v4l2element->menus = g_list_remove_all(v4l2element->menus, NULL);
+ while (g_list_length(v4l2element->menus) > 0) {
+ GList *items = (GList *) g_list_nth_data(v4l2element->menus, 0);
+ v4l2element->inputs = g_list_remove(v4l2element->inputs, items);
+ while (g_list_length(items) > 0) {
+ gpointer data = g_list_nth_data(v4l2element->menus, 0);
+ items = g_list_remove(items, data);
+ g_free(data);
+ }
+ }
+}
+
+
+/******************************************************
+ * gst_v4l2_open():
+ * open the video device (v4l2element->device)
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_open (GstV4l2Element *v4l2element)
+{
+ DEBUG("Trying to open device %s", v4l2element->device);
+ GST_V4L2_CHECK_NOT_OPEN(v4l2element);
+ GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+
+ /* be sure we have a device */
+ if (!v4l2element->device)
+ v4l2element->device = g_strdup("/dev/video");
+
+ /* open the device */
+ v4l2element->video_fd = open(v4l2element->device, O_RDWR);
+ if (!GST_V4L2_IS_OPEN(v4l2element)) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to open device %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ goto error;
+ }
+
+ /* get capabilities */
+ if (!gst_v4l2_get_capabilities(v4l2element)) {
+ goto error;
+ }
+
+ /* and get the video window */
+ if (GST_V4L2_IS_OVERLAY(v4l2element)) {
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_WIN, &(v4l2element->vwin)) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get video window properties of %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ goto error;
+ }
+ }
+
+ /* create enumerations */
+ if (!gst_v4l2_fill_lists(v4l2element))
+ goto error;
+
+ gst_info("Opened device '%s' (%s) successfully\n",
+ v4l2element->vcap.name, v4l2element->device);
+
+ return TRUE;
+
+error:
+ if (GST_V4L2_IS_OPEN(v4l2element)) {
+ /* close device */
+ close(v4l2element->video_fd);
+ v4l2element->video_fd = -1;
+ }
+ /* empty lists */
+ gst_v4l2_empty_lists(v4l2element);
+
+ return FALSE;
+}
+
+
+/******************************************************
+ * gst_v4l2_close():
+ * close the video device (v4l2element->video_fd)
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_close (GstV4l2Element *v4l2element)
+{
+ DEBUG("Trying to close %s", v4l2element->device);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+ GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+
+ /* close device */
+ close(v4l2element->video_fd);
+ v4l2element->video_fd = -1;
+
+ /* empty lists */
+ gst_v4l2_empty_lists(v4l2element);
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_get_norm()
+ * Get the norm of the current device
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_get_norm (GstV4l2Element *v4l2element,
+ gint *norm)
+{
+ struct v4l2_standard standard;
+ gint n;
+
+ DEBUG("getting norm");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_STD, &standard) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get the current norm for device %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ /* try to find out what norm number this actually is */
+ for (n=0;n<g_list_length(v4l2element->norms);n++) {
+ struct v4l2_enumstd *stdptr = (struct v4l2_enumstd *) g_list_nth_data(v4l2element->norms, n);
+ if (!strcmp(stdptr->std.name, standard.name)) {
+ *norm = n;
+ return TRUE;
+ }
+ }
+
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to find norm '%s' in our list of available norms for device %s",
+ standard.name, v4l2element->device);
+ return FALSE;
+}
+
+
+/******************************************************
+ * gst_v4l2_set_norm()
+ * Set the norm of the current device
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_norm (GstV4l2Element *v4l2element,
+ gint norm)
+{
+ struct v4l2_enumstd *standard;
+
+ DEBUG("trying to set norm to %d", norm);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+ GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+
+ if (norm < 0 || norm >= g_list_length(v4l2element->norms)) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Invalid norm number %d (%d-%d)",
+ norm, 0, g_list_length(v4l2element->norms));
+ return FALSE;
+ }
+
+ standard = (struct v4l2_enumstd *) g_list_nth_data(v4l2element->norms, norm);
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_S_STD, &standard->std) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to set norm '%s' (%d) for device %s: %s",
+ standard->std.name, norm, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_get_norm_names()
+ * Get the list of available norms
+ * return value: the list
+ ******************************************************/
+
+GList *
+gst_v4l2_get_norm_names (GstV4l2Element *v4l2element)
+{
+ GList *names = NULL;
+ gint n;
+
+ DEBUG("getting a list of norm names");
+
+ for (n=0;n<g_list_length(v4l2element->norms);n++) {
+ struct v4l2_enumstd *standard = (struct v4l2_enumstd *) g_list_nth_data(v4l2element->norms, n);
+ names = g_list_append(names, g_strdup(standard->std.name));
+ }
+
+ return names;
+}
+
+
+/******************************************************
+ * gst_v4l2_get_input()
+ * Get the input of the current device
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_get_input (GstV4l2Element *v4l2element,
+ gint *input)
+{
+ gint n;
+
+ DEBUG("trying to get input");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_INPUT, &n) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get current input on device %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ *input = n;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_set_input()
+ * Set the input of the current device
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_input (GstV4l2Element *v4l2element,
+ gint input)
+{
+ DEBUG("trying to set input to %d", input);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+ GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+
+ if (input < 0 || input >= g_list_length(v4l2element->inputs)) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Invalid input number %d (%d-%d)",
+ input, 0, g_list_length(v4l2element->inputs));
+ return FALSE;
+ }
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_S_INPUT, &input) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to set input %d on device %s: %s",
+ input, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_get_input_names()
+ * Get the list of available input channels
+ * return value: the list
+ ******************************************************/
+
+GList *
+gst_v4l2_get_input_names (GstV4l2Element *v4l2element)
+{
+ GList *names = NULL;
+ gint n;
+
+ DEBUG("getting a list of input names");
+
+ for (n=0;n<g_list_length(v4l2element->inputs);n++) {
+ struct v4l2_input *input = (struct v4l2_input *) g_list_nth_data(v4l2element->inputs, n);
+ names = g_list_append(names, g_strdup(input->name));
+ }
+
+ return names;
+}
+
+
+/******************************************************
+ * gst_v4l2_get_output()
+ * Get the output of the current device
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_get_output (GstV4l2Element *v4l2element,
+ gint *output)
+{
+ gint n;
+
+ DEBUG("trying to get output");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_OUTPUT, &n) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get current output on device %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ *output = n;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_set_output()
+ * Set the output of the current device
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_output (GstV4l2Element *v4l2element,
+ gint output)
+{
+ DEBUG("trying to set output to %d", output);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+ GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+
+ if (output < 0 || output >= g_list_length(v4l2element->outputs)) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Invalid output number %d (%d-%d)",
+ output, 0, g_list_length(v4l2element->outputs));
+ return FALSE;
+ }
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_S_OUTPUT, &output) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to set output %d on device %s: %s",
+ output, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2_get_output_names()
+ * Get the list of available output channels
+ * return value: the list, or NULL on error
+ ******************************************************/
+
+GList *
+gst_v4l2_get_output_names (GstV4l2Element *v4l2element)
+{
+ GList *names = NULL;
+ gint n;
+
+ DEBUG("getting a list of output names");
+
+ for (n=0;n<g_list_length(v4l2element->outputs);n++) {
+ struct v4l2_output *output = (struct v4l2_output *) g_list_nth_data(v4l2element->outputs, n);
+ names = g_list_append(names, g_strdup(output->name));
+ }
+
+ return names;
+}
+
+
+/******************************************************
+ * gst_v4l_has_tuner():
+ * Check whether the device has a tuner
+ * return value: TRUE if it has a tuner, else FALSE
+ ******************************************************/
+
+gboolean
+gst_v4l2_has_tuner (GstV4l2Element *v4l2element)
+{
+ gint input_num;
+ struct v4l2_input *input;
+
+ DEBUG("detecting whether device has a tuner");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (!gst_v4l2_get_input(v4l2element, &input_num))
+ return FALSE;
+
+ input = (struct v4l2_input *) g_list_nth_data(v4l2element->inputs, input_num);
+
+ return (input->type == V4L2_INPUT_TYPE_TUNER &&
+ v4l2element->vcap.flags & V4L2_FLAG_TUNER);
+}
+
+
+/******************************************************
+ * gst_v4l_get_frequency():
+ * get the current frequency
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_get_frequency (GstV4l2Element *v4l2element,
+ gulong *frequency)
+{
+ gint n;
+
+ DEBUG("getting current tuner frequency");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (!gst_v4l2_has_tuner(v4l2element))
+ return FALSE;
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_FREQ, &n) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get current tuner frequency for device %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ *frequency = n;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l_set_frequency():
+ * set frequency
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_frequency (GstV4l2Element *v4l2element,
+ gulong frequency)
+{
+ gint n = frequency;
+
+ DEBUG("setting current tuner frequency to %lu", frequency);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+ GST_V4L2_CHECK_NOT_ACTIVE(v4l2element);
+
+ if (!gst_v4l2_has_tuner(v4l2element))
+ return FALSE;
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_FREQ, &n) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to set tuner frequency to %lu for device %s: %s",
+ frequency, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l_signal_strength():
+ * get the strength of the signal on the current input
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_signal_strength (GstV4l2Element *v4l2element,
+ gulong *signal_strength)
+{
+ struct v4l2_tuner tuner;
+
+ DEBUG("trying to get signal strength");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_TUNER, &tuner) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to set signal strength for device %s: %s",
+ v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ *signal_strength = tuner.signal;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l_has_audio():
+ * Check whether the device has audio capabilities
+ * return value: TRUE if it has a tuner, else FALSE
+ ******************************************************/
+
+gboolean
+gst_v4l2_has_audio (GstV4l2Element *v4l2element)
+{
+ gint input_num;
+ struct v4l2_input *input;
+
+ DEBUG("detecting whether device has audio");
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (!gst_v4l2_get_input(v4l2element, &input_num))
+ return FALSE;
+
+ input = (struct v4l2_input *) g_list_nth_data(v4l2element->inputs, input_num);
+
+ return (input->capability & V4L2_INPUT_CAP_AUDIO);
+}
+
+
+/******************************************************
+ * gst_v4l_get_attributes():
+ * get a list of attributes available on this device
+ * return value: the list
+ ******************************************************/
+
+GList *
+gst_v4l2_get_attributes (GstV4l2Element *v4l2element)
+{
+ gint i;
+ GList *list = NULL;
+
+ DEBUG("getting a list of available attributes");
+
+ for (i=0;i<g_list_length(v4l2element->controls);i++) {
+ struct v4l2_queryctrl *control = (struct v4l2_queryctrl *) g_list_nth_data(v4l2element->controls, i);
+ GstV4l2Attribute* attribute = g_malloc(sizeof(GstV4l2Attribute));
+ attribute->name = g_strdup(control->name);
+ attribute->index = i;
+ attribute->list_items = NULL;
+ switch (control->type) {
+ case V4L2_CTRL_TYPE_INTEGER:
+ attribute->val_type = GST_V4L2_ATTRIBUTE_VALUE_TYPE_INT;
+ break;
+ case V4L2_CTRL_TYPE_BOOLEAN:
+ attribute->val_type = GST_V4L2_ATTRIBUTE_VALUE_TYPE_BOOLEAN;
+ break;
+ case V4L2_CTRL_TYPE_MENU: {
+ /* list items */
+ gint n;
+ GList *menus = (GList *) g_list_nth_data(v4l2element->menus, i);
+ for (n=0;n<g_list_length(menus);n++) {
+ struct v4l2_querymenu *menu = g_list_nth_data(menus, n);
+ attribute->list_items = g_list_append(attribute->list_items, g_strdup(menu->name));
+ }
+ attribute->val_type = GST_V4L2_ATTRIBUTE_VALUE_TYPE_LIST;
+ break; }
+ case V4L2_CTRL_TYPE_BUTTON:
+ attribute->val_type = GST_V4L2_ATTRIBUTE_VALUE_TYPE_BUTTON;
+ break;
+ }
+ switch (control->category) {
+ case V4L2_CTRL_CAT_VIDEO:
+ attribute->type = GST_V4L2_ATTRIBUTE_TYPE_VIDEO;
+ break;
+ case V4L2_CTRL_CAT_AUDIO:
+ attribute->type = GST_V4L2_ATTRIBUTE_TYPE_AUDIO;
+ break;
+ case V4L2_CTRL_CAT_EFFECT:
+ attribute->type = GST_V4L2_ATTRIBUTE_TYPE_EFFECT;
+ break;
+ }
+ gst_v4l2_get_attribute(v4l2element, i, &attribute->value);
+ attribute->min = control->minimum;
+ attribute->max = control->maximum;
+ }
+
+ return list;
+}
+
+
+/******************************************************
+ * gst_v4l_get_attribute():
+ * try to get the value of one specific attribute
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_get_attribute (GstV4l2Element *v4l2element,
+ gint attribute_num,
+ gint *value)
+{
+ struct v4l2_control control;
+
+ DEBUG("getting value of attribute %d", attribute_num);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (attribute_num < 0 || attribute_num >= g_list_length(v4l2element->controls)) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Invalid control ID %d", attribute_num);
+ return FALSE;
+ }
+
+ control.id = attribute_num;
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_G_CTRL, &control) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to get value for control %d on device %s: %s",
+ attribute_num, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ *value = control.value;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l_set_attribute():
+ * try to set the value of one specific attribute
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2_set_attribute (GstV4l2Element *v4l2element,
+ gint attribute_num,
+ gint value)
+{
+ struct v4l2_control control;
+
+ DEBUG("setting value of attribute %d to %d", attribute_num, value);
+ GST_V4L2_CHECK_OPEN(v4l2element);
+
+ if (attribute_num < 0 || attribute_num >= g_list_length(v4l2element->controls)) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Invalid control ID %d", attribute_num);
+ return FALSE;
+ }
+
+ control.id = attribute_num;
+ control.value = value;
+
+ if (ioctl(v4l2element->video_fd, VIDIOC_S_CTRL, &control) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2element),
+ "Failed to set value %d for control %d on device %s: %s",
+ value, attribute_num, v4l2element->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/sys/v4l2/v4l2_calls.h b/sys/v4l2/v4l2_calls.h
new file mode 100644
index 00000000..ce562d47
--- /dev/null
+++ b/sys/v4l2/v4l2_calls.h
@@ -0,0 +1,135 @@
+/* G-Streamer generic V4L2 element - generic V4L2 calls handling
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __V4L2_CALLS_H__
+#define __V4L2_CALLS_H__
+
+#include "gstv4l2element.h"
+
+
+/* simple check whether the device is open */
+#define GST_V4L2_IS_OPEN(v4l2element) \
+ (v4l2element->video_fd > 0)
+
+/* check whether the device is 'active' */
+#define GST_V4L2_IS_ACTIVE(v4l2element) \
+ (v4l2element->buffer != NULL)
+
+#define GST_V4L2_IS_OVERLAY(v4l2element) \
+ (v4l2element->vcap.flags & V4L2_FLAG_PREVIEW)
+
+/* checks whether the current v4lelement has already been open()'ed or not */
+#define GST_V4L2_CHECK_OPEN(v4l2element) \
+ if (v4l2element->video_fd <= 0) \
+ { \
+ gst_element_error(GST_ELEMENT(v4l2element), \
+ "Device is not open"); \
+ return FALSE; \
+ }
+
+/* checks whether the current v4lelement is close()'ed or whether it is still open */
+#define GST_V4L2_CHECK_NOT_OPEN(v4l2element) \
+ if (v4l2element->video_fd != -1) \
+ { \
+ gst_element_error(GST_ELEMENT(v4l2element), \
+ "Device is open"); \
+ return FALSE; \
+ }
+
+/* checks whether the current v4lelement does video overlay */
+#define GST_V4L2_CHECK_OVERLAY(v4l2element) \
+ if (!(v4l2element->vcap.flags & V4L2_FLAG_PREVIEW)) \
+ { \
+ gst_element_error(GST_ELEMENT(v4l2element), \
+ "Device doesn't do overlay"); \
+ return FALSE; \
+ }
+
+/* checks whether we're in capture mode or not */
+#define GST_V4L2_CHECK_ACTIVE(v4l2element) \
+ if (v4l2element->buffer == NULL) \
+ { \
+ gst_element_error(GST_ELEMENT(v4l2element), \
+ "Device is not in streaming mode"); \
+ return FALSE; \
+ }
+
+/* checks whether we're out of capture mode or not */
+#define GST_V4L2_CHECK_NOT_ACTIVE(v4l2element) \
+ if (v4l2element->buffer != NULL) \
+ { \
+ gst_element_error(GST_ELEMENT(v4l2element), \
+ "Device is in streaming mode"); \
+ return FALSE; \
+ }
+
+
+/* open/close the device */
+gboolean gst_v4l2_open (GstV4l2Element *v4l2element);
+gboolean gst_v4l2_close (GstV4l2Element *v4l2element);
+
+/* norm/input/output */
+gboolean gst_v4l2_get_norm (GstV4l2Element *v4l2element,
+ gint *norm);
+gboolean gst_v4l2_set_norm (GstV4l2Element *v4l2element,
+ gint norm);
+GList * gst_v4l2_get_norm_names (GstV4l2Element *v4l2element);
+gboolean gst_v4l2_get_input (GstV4l2Element *v4l2element,
+ gint *input);
+gboolean gst_v4l2_set_input (GstV4l2Element *v4l2element,
+ gint input);
+GList * gst_v4l2_get_input_names (GstV4l2Element *v4l2element);
+gboolean gst_v4l2_get_output (GstV4l2Element *v4l2element,
+ gint *output);
+gboolean gst_v4l2_set_output (GstV4l2Element *v4l2element,
+ gint output);
+GList * gst_v4l2_get_output_names (GstV4l2Element *v4l2element);
+
+/* frequency control */
+gboolean gst_v4l2_has_tuner (GstV4l2Element *v4l2element);
+gboolean gst_v4l2_get_frequency (GstV4l2Element *v4l2element,
+ gulong *frequency);
+gboolean gst_v4l2_set_frequency (GstV4l2Element *v4l2element,
+ gulong frequency);
+gboolean gst_v4l2_signal_strength (GstV4l2Element *v4l2element,
+ gulong *signal_strength);
+
+/* attribute control */
+gboolean gst_v4l2_has_audio (GstV4l2Element *v4l2element);
+GList * gst_v4l2_get_attributes (GstV4l2Element *v4l2element);
+gboolean gst_v4l2_get_attribute (GstV4l2Element *v4l2element,
+ gint attribute_num,
+ gint *value);
+gboolean gst_v4l2_set_attribute (GstV4l2Element *v4l2element,
+ gint attribute_num,
+ gint value);
+
+/* overlay */
+gboolean gst_v4l2_set_display (GstV4l2Element *v4l2element,
+ const gchar *display);
+gboolean gst_v4l2_set_window (GstV4l2Element *v4l2element,
+ gint x, gint y,
+ gint w, gint h);
+gboolean gst_v4l2_set_clips (GstV4l2Element *v4l2element,
+ struct v4l2_clip *clips,
+ gint num_clips);
+gboolean gst_v4l2_enable_overlay (GstV4l2Element *v4l2element,
+ gboolean enable);
+
+#endif /* __V4L2_CALLS_H__ */
diff --git a/sys/v4l2/v4l2src_calls.c b/sys/v4l2/v4l2src_calls.c
new file mode 100644
index 00000000..5dcff336
--- /dev/null
+++ b/sys/v4l2/v4l2src_calls.c
@@ -0,0 +1,397 @@
+/* G-Streamer Video4linux2 video-capture plugin - system calls
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <string.h>
+#include <errno.h>
+#include "v4l2src_calls.h"
+#include <sys/time.h>
+
+#define DEBUG(format, args...) \
+ GST_DEBUG_ELEMENT(GST_CAT_PLUGIN_INFO, \
+ GST_ELEMENT(v4l2src), \
+ "V4L2SRC: " format "\n", ##args)
+
+#define MIN_BUFFERS_QUEUED 2
+
+/* On some systems MAP_FAILED seems to be missing */
+#ifndef MAP_FAILED
+#define MAP_FAILED ( (caddr_t) -1 )
+#endif
+
+
+/******************************************************
+ * gst_v4l2src_queue_frame():
+ * queue a frame for capturing
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+static gboolean
+gst_v4l2src_queue_frame (GstV4l2Src *v4l2src,
+ gint num)
+{
+ DEBUG("queueing frame %d", num);
+
+ v4l2src->bufsettings.index = num;
+ if (ioctl(GST_V4L2ELEMENT(v4l2src)->video_fd, VIDIOC_QBUF, &v4l2src->bufsettings) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Error queueing buffer %d on device %s: %s",
+ num, GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4lsrc_sync_frame():
+ * sync on a frame for capturing
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+static gboolean
+gst_v4l2src_sync_next_frame (GstV4l2Src *v4l2src,
+ gint *num)
+{
+ if (ioctl(GST_V4L2ELEMENT(v4l2src)->video_fd, VIDIOC_DQBUF, &v4l2src->bufsettings) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Error syncing on a buffer on device %s: %s",
+ GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ return FALSE;
+ }
+ DEBUG("synced on frame %d", v4l2src->bufsettings.index);
+ *num = v4l2src->bufsettings.index;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_get_capture():
+ * get capture parameters
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_get_capture (GstV4l2Src *v4l2src)
+{
+ DEBUG("Getting capture format");
+
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+
+ if (ioctl(GST_V4L2ELEMENT(v4l2src)->video_fd, VIDIOC_G_FMT, &v4l2src->format) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Failed to get pixel format for device %s: %s",
+ GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_set_capture():
+ * set capture parameters
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_set_capture (GstV4l2Src *v4l2src,
+ struct v4l2_fmtdesc *fmt,
+ gint width,
+ gint height)
+{
+ DEBUG("Setting capture format to %dx%d, format %s",
+ width, height, fmt->description);
+
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+ GST_V4L2_CHECK_NOT_ACTIVE(GST_V4L2ELEMENT(v4l2src));
+
+ memset(&v4l2src->format, 0, sizeof(struct v4l2_format));
+ v4l2src->format.fmt.pix.width = width;
+ v4l2src->format.fmt.pix.height = height;
+ v4l2src->format.fmt.pix.pixelformat = fmt->pixelformat;
+ if (fmt->flags & V4L2_FMT_FLAG_COMPRESSED) {
+ v4l2src->format.fmt.pix.flags = V4L2_FMT_FLAG_COMPRESSED;
+ v4l2src->format.type = V4L2_BUF_TYPE_CODECIN;
+ } else {
+ v4l2src->format.type = V4L2_BUF_TYPE_CAPTURE;
+ }
+
+ if (ioctl(GST_V4L2ELEMENT(v4l2src)->video_fd, VIDIOC_S_FMT, &v4l2src->format) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Failed to set pixel format to %s @ %dx%d for device %s: %s",
+ fmt->description, width, height,
+ GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_capture_init():
+ * initialize the capture system
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_capture_init (GstV4l2Src *v4l2src)
+{
+ gint n;
+ gchar *desc = NULL;
+
+ DEBUG("initting the capture system");
+
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+ GST_V4L2_CHECK_NOT_ACTIVE(GST_V4L2ELEMENT(v4l2src));
+
+ /* request buffer info */
+ if (v4l2src->breq.count < MIN_BUFFERS_QUEUED)
+ v4l2src->breq.count = MIN_BUFFERS_QUEUED;
+ v4l2src->breq.type = v4l2src->format.type;
+ if (ioctl(GST_V4L2ELEMENT(v4l2src)->video_fd, VIDIOC_REQBUFS, &v4l2src->breq) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Error requesting buffers (%d) for %s: %s",
+ v4l2src->breq.count, GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ if (v4l2src->breq.count < MIN_BUFFERS_QUEUED) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Too little buffers. We got %d, we want at least %d",
+ v4l2src->breq.count, MIN_BUFFERS_QUEUED);
+ return FALSE;
+ }
+ v4l2src->bufsettings.type = v4l2src->format.type;
+
+ for (n=0;n<g_list_length(GST_V4L2ELEMENT(v4l2src)->formats);n++) {
+ struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *) g_list_nth_data(GST_V4L2ELEMENT(v4l2src)->formats, n);
+ if (v4l2src->format.fmt.pix.pixelformat == fmt->pixelformat) {
+ desc = fmt->description;
+ break;
+ }
+ }
+ gst_info("Got %d buffers (%s) of size %d KB\n",
+ v4l2src->breq.count, desc, v4l2src->format.fmt.pix.sizeimage/1024);
+
+ /* Map the buffers */
+ GST_V4L2ELEMENT(v4l2src)->buffer = (guint8 **) g_malloc(sizeof(guint8*) * v4l2src->breq.count);
+ for (n=0;n<v4l2src->breq.count;n++) {
+ GST_V4L2ELEMENT(v4l2src)->buffer[n] = mmap(0, v4l2src->format.fmt.pix.sizeimage,
+ PROT_READ|PROT_WRITE, MAP_SHARED, GST_V4L2ELEMENT(v4l2src)->video_fd, v4l2src->format.fmt.pix.sizeimage*n);
+ if (GST_V4L2ELEMENT(v4l2src)->buffer[n] == MAP_FAILED) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Error mapping video buffer %d on device %s: %s",
+ n, GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ GST_V4L2ELEMENT(v4l2src)->buffer[n] = NULL;
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_capture_start():
+ * start streaming capture
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_capture_start (GstV4l2Src *v4l2src)
+{
+ gint n;
+
+ DEBUG("starting the capturing");
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+ GST_V4L2_CHECK_ACTIVE(GST_V4L2ELEMENT(v4l2src));
+
+ /* queue all buffers, this starts streaming capture */
+ for (n=0;n<v4l2src->breq.count;n++)
+ if (!gst_v4l2src_queue_frame(v4l2src, n))
+ return FALSE;
+ n = 1;
+ if (ioctl(GST_V4L2ELEMENT(v4l2src)->video_fd, VIDIOC_STREAMON, &n) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Error starting streaming capture for %s: %s",
+ GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_grab_frame():
+ * capture one frame during streaming capture
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_grab_frame (GstV4l2Src *v4l2src,
+ gint *num)
+{
+ DEBUG("syncing on the next frame");
+
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+ GST_V4L2_CHECK_ACTIVE(GST_V4L2ELEMENT(v4l2src));
+
+ /* syncing on the buffer grabs it */
+ if (!gst_v4l2src_sync_next_frame(v4l2src, num))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_requeue_frame():
+ * re-queue a frame after we're done with the buffer
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_requeue_frame (GstV4l2Src *v4l2src,
+ gint num)
+{
+ DEBUG("requeueing frame %d", num);
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+ GST_V4L2_CHECK_ACTIVE(GST_V4L2ELEMENT(v4l2src));
+
+ /* and let's queue the buffer */
+ if (!gst_v4l2src_queue_frame(v4l2src, num))
+ return FALSE;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_capture_stop():
+ * stop streaming capture
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_capture_stop (GstV4l2Src *v4l2src)
+{
+ gint n = 0;
+
+ DEBUG("stopping capturing");
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+ GST_V4L2_CHECK_ACTIVE(GST_V4L2ELEMENT(v4l2src));
+
+ /* we actually need to sync on all queued buffers but not on the non-queued ones */
+ if (ioctl(GST_V4L2ELEMENT(v4l2src)->video_fd, VIDIOC_STREAMOFF, &n) < 0) {
+ gst_element_error(GST_ELEMENT(v4l2src),
+ "Error stopping streaming capture for %s: %s",
+ GST_V4L2ELEMENT(v4l2src)->device, sys_errlist[errno]);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_capture_deinit():
+ * deinitialize the capture system
+ * return value: TRUE on success, FALSE on error
+ ******************************************************/
+
+gboolean
+gst_v4l2src_capture_deinit (GstV4l2Src *v4l2src)
+{
+ gint n;
+
+ DEBUG("deinitting capture system");
+ GST_V4L2_CHECK_OPEN(GST_V4L2ELEMENT(v4l2src));
+ GST_V4L2_CHECK_ACTIVE(GST_V4L2ELEMENT(v4l2src));
+
+ /* unmap the buffer */
+ for (n=0;n<v4l2src->breq.count;n++) {
+ if (!GST_V4L2ELEMENT(v4l2src)->buffer[n])
+ break;
+ munmap(GST_V4L2ELEMENT(v4l2src)->buffer[n], v4l2src->format.fmt.pix.sizeimage);
+ GST_V4L2ELEMENT(v4l2src)->buffer[n] = NULL;
+ }
+ g_free(GST_V4L2ELEMENT(v4l2src)->buffer);
+ GST_V4L2ELEMENT(v4l2src)->buffer = NULL;
+
+ return TRUE;
+}
+
+
+/******************************************************
+ * gst_v4l2src_get_fourcc_list():
+ * create a list of all available fourccs
+ * return value: the list
+ ******************************************************/
+
+GList *
+gst_v4l2src_get_fourcc_list (GstV4l2Src *v4l2src)
+{
+ GList *list = NULL;
+ GstV4l2Element *v4l2element = GST_V4L2ELEMENT(v4l2src);
+ gint n;
+
+ for (n=0;n<g_list_length(v4l2element->formats);n++) {
+ struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *) g_list_nth_data(v4l2element->formats, n);
+ guint32 print_format = GUINT32_FROM_LE(fmt->pixelformat);
+ gchar *print_format_str = (gchar *) &print_format;
+
+ list = g_list_append(list, g_strndup(print_format_str, 4));
+ }
+
+ return list;
+}
+
+
+/******************************************************
+ * gst_v4l2src_get_format_list():
+ * create a list of all available capture formats
+ * return value: the list
+ ******************************************************/
+
+GList *
+gst_v4l2src_get_format_list (GstV4l2Src *v4l2src)
+{
+ GList *list = NULL;
+ GstV4l2Element *v4l2element = GST_V4L2ELEMENT(v4l2src);
+ gint n;
+
+ for (n=0;n<g_list_length(v4l2element->formats);n++) {
+ struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *) g_list_nth_data(v4l2element->formats, n);
+
+ list = g_list_append(list, g_strdup(fmt->description));
+ }
+
+ return list;
+}
diff --git a/sys/v4l2/v4l2src_calls.h b/sys/v4l2/v4l2src_calls.h
new file mode 100644
index 00000000..c9a108a7
--- /dev/null
+++ b/sys/v4l2/v4l2src_calls.h
@@ -0,0 +1,45 @@
+/* G-Streamer Video4linux2 video-capture plugin - system calls
+ * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifndef __V4L2_SRC_CALLS_H__
+#define __V4L2_SRC_CALLS_H__
+
+#include "gstv4l2src.h"
+#include "v4l2_calls.h"
+
+
+gboolean gst_v4l2src_get_capture (GstV4l2Src *v4l2src);
+gboolean gst_v4l2src_set_capture (GstV4l2Src *v4l2src,
+ struct v4l2_fmtdesc *fmt,
+ gint width,
+ gint height);
+gboolean gst_v4l2src_capture_init (GstV4l2Src *v4l2src);
+gboolean gst_v4l2src_capture_start (GstV4l2Src *v4l2src);
+gboolean gst_v4l2src_grab_frame (GstV4l2Src *v4l2src,
+ gint *num);
+gboolean gst_v4l2src_requeue_frame (GstV4l2Src *v4l2src,
+ gint num);
+gboolean gst_v4l2src_capture_stop (GstV4l2Src *v4l2src);
+gboolean gst_v4l2src_capture_deinit (GstV4l2Src *v4l2src);
+
+GList * gst_v4l2src_get_fourcc_list (GstV4l2Src *v4l2src);
+GList * gst_v4l2src_get_format_list (GstV4l2Src *v4l2src);
+
+
+#endif /* __V4L2_SRC_CALLS_H__ */