diff options
m--------- | common | 0 | ||||
-rw-r--r-- | sys/v4l2/Makefile.am | 18 | ||||
-rw-r--r-- | sys/v4l2/README | 24 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2element.c | 493 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2element.h | 110 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2src.c | 837 | ||||
-rw-r--r-- | sys/v4l2/gstv4l2src.h | 69 | ||||
-rw-r--r-- | sys/v4l2/v4l2-overlay_calls.c | 168 | ||||
-rw-r--r-- | sys/v4l2/v4l2_calls.c | 863 | ||||
-rw-r--r-- | sys/v4l2/v4l2_calls.h | 135 | ||||
-rw-r--r-- | sys/v4l2/v4l2src_calls.c | 397 | ||||
-rw-r--r-- | sys/v4l2/v4l2src_calls.h | 45 |
12 files changed, 3159 insertions, 0 deletions
diff --git a/common b/common -Subproject 355c616d5f6779ea194f8b61704229c6fb04ae7 +Subproject 2f6d9cfdaaa83ab454d263d6eba88046debadc2 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__ */ |