this adds video4linux2 source and element plugins. The division in v4l2* plugins is the same as for v4l1 - i.e. an el...

Original commit message from CVS:
this adds video4linux2 source and element plugins. The division in v4l2* plugins is the same as for v4l1 - i.e. an element, a src and a sink, but there won't be separate encoding plugins (like v4lmjpegsrc) - all functionality is (thanks to video4linux2) integrated in one plugin: v4l2src. v4l2sink is still to be done, that'll come later.
This commit is contained in:
Ronald S. Bultje 2002-09-09 07:14:35 +00:00
parent 86c99507a5
commit 3ec5a0a054
12 changed files with 3160 additions and 1 deletions

2
common

@ -1 +1 @@
Subproject commit 355c616d5f6779ea194f8b61704229c6fb04ae7b
Subproject commit 2f6d9cfdaaa83ab454d263d6eba88046debadc2d

18
sys/v4l2/Makefile.am Normal file
View file

@ -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

24
sys/v4l2/README Normal file
View file

@ -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/.

493
sys/v4l2/gstv4l2element.c Normal file
View file

@ -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
};

110
sys/v4l2/gstv4l2element.h Normal file
View file

@ -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__ */

837
sys/v4l2/gstv4l2src.c Normal file
View file

@ -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
};

69
sys/v4l2/gstv4l2src.h Normal file
View file

@ -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__ */

View file

@ -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;
}

863
sys/v4l2/v4l2_calls.c Normal file
View file

@ -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;
}

135
sys/v4l2/v4l2_calls.h Normal file
View file

@ -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__ */

397
sys/v4l2/v4l2src_calls.c Normal file
View file

@ -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;
}

45
sys/v4l2/v4l2src_calls.h Normal file
View file

@ -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__ */