From: Ronald B. <rb...@us...> - 2002-09-09 07:14:48
|
CVS Root: /cvsroot/gstreamer Module: gst-plugins Changes by: rbultje Date: Mon Sep 09 2002 00:14:48 PDT Log message: this adds video4linux2 source and element plugins. The division in v4l2* plugins is the same as for v4l1 - i.e. an element, a src and a sink, but there won't be separate encoding plugins (like v4lmjpegsrc) - all functionality is (thanks to video4linux2) integrated in one plugin: v4l2src. v4l2sink is still to be done, that'll come later. Added files: sys/v4l2 : Makefile.am README gstv4l2element.c gstv4l2element.h gstv4l2src.c gstv4l2src.h v4l2-overlay_calls.c v4l2_calls.c v4l2_calls.h v4l2src_calls.c v4l2src_calls.h Links: http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/Makefile.am?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/README?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/gstv4l2element.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/gstv4l2element.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/gstv4l2src.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/gstv4l2src.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/v4l2-overlay_calls.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/v4l2_calls.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/v4l2_calls.h?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/v4l2src_calls.c?rev=1.1&content-type=text/vnd.viewcvs-markup http://cvs.sf.net/cgi-bin/viewcvs.cgi/gstreamer/gst-plugins/sys/v4l2/v4l2src_calls.h?rev=1.1&content-type=text/vnd.viewcvs-markup ====Begin Diffs==== --- NEW FILE: Makefile.am --- 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 --- NEW FILE: README --- 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/. --- NEW FILE: gstv4l2element.c --- /* G-Streamer generic V4L2 element * Copyright (C) 2002 Ronald Bultje <rb...@ro...> * * 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 <rb...@ro...>", "(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 }; --- NEW FILE: gstv4l2element.h --- /* G-Streamer generic V4L2 element * Copyright (C) 2002 Ronald Bultje <rb...@ro...> * * 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__ */ --- NEW FILE: gstv4l2src.c --- /* G-Streamer Video4linux2 video-capture plugin * Copyright (C) 2002 Ronald Bultje <rb...@ro...> * * 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 <rb...@ro...>", "(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 }; --- NEW FILE: gstv4l2src.h --- /* G-Streamer Video4linux2 video-capture plugin * Copyright (C) 2002 Ronald Bultje <rb...@ro...> * * 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__ */ --- NEW FILE: v4l2-overlay_calls.c --- /* G-Streamer generic V4L2 element - generic V4L2 overlay handling * Copyright (C) 2002 Ronald Bultje <rb...@ro...> * * 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; } --- NEW FILE: v4l2_calls.c --- /* G-Streamer generic V4L2 element - generic V4L2 calls handling * Copyright (C) 2002 Ronald Bultje <rb...@ro...> * * 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 ... [truncated message content] |