diff --git a/sys/v4l/Makefile.am b/sys/v4l/Makefile.am index b99e2d4940..914df99227 100644 --- a/sys/v4l/Makefile.am +++ b/sys/v4l/Makefile.am @@ -1,12 +1,40 @@ -plugindir = $(libdir)/gst +filterdir = $(libdir)/gst -plugin_LTLIBRARIES = libv4lsrc.la +filter_LTLIBRARIES = libv4lelement.la libv4lsrc.la \ + libv4lmjpegsrc.la +#libv4lmjpegsink.la -libv4lsrc_la_SOURCES = \ - gstv4lsrc.c \ - grab-v4l.c +libv4lelement_la_SOURCES = \ + gstv4lelement.c \ + v4l_calls.c +libv4lelement_la_CFLAGS = \ + $(GST_CFLAGS) -## FIXME : do we need -O2 ? libv4lsrc_la_CFLAGS = -O2 $(GST_CFLAGS) -libv4lsrc_la_CFLAGS = $(GST_CFLAGS) +libv4lsrc_la_SOURCES = \ + gstv4lsrc.c \ + v4lsrc_calls.c +libv4lsrc_la_LIBADD = \ + libv4lelement.la +libv4lsrc_la_CFLAGS = \ + $(GST_CFLAGS) -noinst_HEADERS = gstv4lsrc.h grab.h +libv4lmjpegsrc_la_SOURCES = \ + gstv4lmjpegsrc.c \ + v4lmjpegsrc_calls.c +libv4lmjpegsrc_la_LIBADD = \ + libv4lelement.la +libv4lmjpegsrc_la_CFLAGS = \ + $(GST_CFLAGS) + +#libv4lmjpegsink_la_SOURCED = \ +# gstv4lmjpegsink.c \ +# v4lmjpegsikn_calls.c +#libv4lmjpegsink_la_LIBADD = \ +# libv4lelement.la +#libv4lmjpegsink_la_CFLAGS = \ +# $(GST_CFLAGS) + +noinst_HEADERS = gstv4lelement.h v4l_calls.h \ + gstv4lsrc.h v4lsrc_calls.h \ + gstv4lmjpegsrc.h v4lmjpegsrc_calls.h +#gstv4lmjpegsink.h v4lmjpegsink_calls.h diff --git a/sys/v4l/README b/sys/v4l/README new file mode 100644 index 0000000000..2df87faa98 --- /dev/null +++ b/sys/v4l/README @@ -0,0 +1,22 @@ +General idea: + + / gstv4lsrc.[ch] + / v4lsrc_calls.[ch] + / +gstv4lelement.[ch] ------------- gstv4lmjpegsrc.[ch] +v4l_calls.[ch] ------------- v4lmjpegsrc_calls.[ch] + \ + \ gstv4lmjpegsink.[ch] + \ v4lmjpegsink_calls.[ch] + + +I.e., all the files on thei right are child classes of +the v4lelement 'parent' on the left.mjpegsink is still +todo. + +Generic idea: +* v4lelement handles generic v4l stuff (picture settings, + audio, norm/input setting, open()/close()) +* v4lsrc, v4lmjpegsrc handle the capture specific + functions. Maybe we'd need a v4lmpegsrc too +* v4lmjpegsink handles mjpeg hardware playback of video diff --git a/sys/v4l/TODO b/sys/v4l/TODO new file mode 100644 index 0000000000..efda739966 --- /dev/null +++ b/sys/v4l/TODO @@ -0,0 +1,5 @@ +* v4lmjpegsrc: integrate input/norm autodetection +* v4lmjpegsink: build +* v4lsrc: threaded sync() +* v4lsrc: threaded wait-for-sync()-until-queue() +* BSD-src: build (based on Bt848/Bt878/Meteor API) diff --git a/sys/v4l/gstv4lelement.c b/sys/v4l/gstv4lelement.c new file mode 100644 index 0000000000..9b05546f0c --- /dev/null +++ b/sys/v4l/gstv4lelement.c @@ -0,0 +1,479 @@ +/* G-Streamer generic V4L element - generic V4L calls handling + * Copyright (C) 2001 Ronald Bultje + * + * 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 "v4l_calls.h" + +#if 0 +static GstElementDetails gst_v4lelement_details = { + "Generic video4linux Element", + "None/Video", + "Generic plugin for handling common video4linux calls", + VERSION, + "Ronald Bultje ", + "(C) 2001", +}; +#endif + +/* V4lElement signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +enum { + ARG_0, + ARG_CHANNEL, + ARG_NORM, + ARG_HAS_TUNER, + ARG_FREQUENCY, + ARG_HAS_AUDIO, + ARG_MUTE, + ARG_MODE, + ARG_VOLUME, + ARG_HUE, + ARG_BRIGHTNESS, + ARG_CONTRAST, + ARG_SATURATION, + ARG_DEVICE +}; + + +static void gst_v4lelement_class_init (GstV4lElementClass *klass); +static void gst_v4lelement_init (GstV4lElement *v4lelement); +static void gst_v4lelement_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_v4lelement_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); +static GstElementStateReturn gst_v4lelement_change_state (GstElement *element); +static gboolean plugin_init (GModule *module, + GstPlugin *plugin); + + +static GstElementClass *parent_class = NULL; +//static guint gst_v4lelement_signals[LAST_SIGNAL] = { 0 }; + + +GType +gst_v4lelement_get_type (void) +{ + static GType v4lelement_type = 0; + + if (!v4lelement_type) { + static const GTypeInfo v4lelement_info = { + sizeof(GstV4lElementClass), + NULL, + NULL, + (GClassInitFunc)gst_v4lelement_class_init, + NULL, + NULL, + sizeof(GstV4lElement), + 0, + (GInstanceInitFunc)gst_v4lelement_init, + NULL + }; + v4lelement_type = g_type_register_static(GST_TYPE_ELEMENT, "GstV4lElement", &v4lelement_info, 0); + } + return v4lelement_type; +} + + + +static void +gst_v4lelement_class_init (GstV4lElementClass *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_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_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_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_MUTE, + g_param_spec_boolean("mute","mute","mute",0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_VOLUME, + g_param_spec_int("volume","volume","volume",G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MODE, + g_param_spec_int("mode","mode","mode",G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HUE, + g_param_spec_int("hue","hue","hue",G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BRIGHTNESS, + g_param_spec_int("brightness","brightness","brightness",G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CONTRAST, + g_param_spec_int("contrast","contrast","contrast",G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_SATURATION, + g_param_spec_int("saturation","saturation","saturation",G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE, + g_param_spec_string("device","device","device", NULL, G_PARAM_READWRITE)); + + gobject_class->set_property = gst_v4lelement_set_property; + gobject_class->get_property = gst_v4lelement_get_property; + + gstelement_class->change_state = gst_v4lelement_change_state; +} + + +static void +gst_v4lelement_init (GstV4lElement *v4lelement) +{ + /* some default values */ + v4lelement->video_fd = -1; + v4lelement->buffer = NULL; + v4lelement->videodev = NULL; + + v4lelement->norm = -1; + v4lelement->channel = 0; /* the first channel */ + + v4lelement->frequency = 0; + + v4lelement->mute = -1; + v4lelement->volume = -1; + v4lelement->mode = -1; + + v4lelement->brightness = -1; + v4lelement->hue = -1; + v4lelement->contrast = -1; + v4lelement->saturation = -1; +} + + +static void +gst_v4lelement_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GstV4lElement *v4lelement; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_V4LELEMENT(object)); + v4lelement = GST_V4LELEMENT(object); + + switch (prop_id) + { + case ARG_CHANNEL: + v4lelement->channel = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement) && !GST_V4L_IS_ACTIVE(v4lelement)) + { + if (v4lelement->norm >= 0) + if (!gst_v4l_set_chan_norm(v4lelement, v4lelement->channel, v4lelement->norm)) + return; + } + break; + case ARG_NORM: + v4lelement->norm = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement) && !GST_V4L_IS_ACTIVE(v4lelement)) + { + if (v4lelement->norm >= 0) + if (!gst_v4l_set_chan_norm(v4lelement, v4lelement->channel, v4lelement->norm)) + return; + } + break; + case ARG_FREQUENCY: + v4lelement->frequency = g_value_get_ulong(value); + if (GST_V4L_IS_ACTIVE(v4lelement) && !GST_V4L_IS_ACTIVE(v4lelement)) + { + if (!gst_v4l_set_frequency(v4lelement, v4lelement->frequency)) + return; + } + break; + case ARG_MUTE: + v4lelement->mute = g_value_get_boolean(value)?1:0; + if (GST_V4L_IS_OPEN(v4lelement)) + { + if (!gst_v4l_set_audio(v4lelement, V4L_AUDIO_MUTE, v4lelement->mute)) + return; + } + break; + case ARG_MODE: + v4lelement->mode = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement)) + { + if (!gst_v4l_set_audio(v4lelement, V4L_AUDIO_MODE, v4lelement->mute)) + return; + } + break; + case ARG_VOLUME: + v4lelement->volume = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement)) + { + if (!gst_v4l_set_audio(v4lelement, V4L_AUDIO_VOLUME, v4lelement->volume)) + return; + } + break; + case ARG_HUE: + v4lelement->hue = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement)) + { + if (!gst_v4l_set_picture(v4lelement, V4L_PICTURE_HUE, v4lelement->hue)) + return; + } + break; + case ARG_BRIGHTNESS: + v4lelement->brightness = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement)) + { + if (!gst_v4l_set_picture(v4lelement, V4L_PICTURE_BRIGHTNESS, v4lelement->brightness)) + return; + } + break; + case ARG_CONTRAST: + v4lelement->contrast = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement)) + { + if (!gst_v4l_set_picture(v4lelement, V4L_PICTURE_CONTRAST, v4lelement->contrast)) + return; + } + break; + case ARG_SATURATION: + v4lelement->saturation = g_value_get_int(value); + if (GST_V4L_IS_OPEN(v4lelement)) + { + if (!gst_v4l_set_picture(v4lelement, V4L_PICTURE_SATURATION, v4lelement->saturation)) + return; + } + break; + case ARG_DEVICE: + if (GST_V4L_IS_OPEN(v4lelement)) + break; /* only set when *not* open */ + if (v4lelement->videodev) + g_free(v4lelement->videodev); + v4lelement->videodev = g_strdup(g_value_get_string(value)); + break; + default: + //G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_v4lelement_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GstV4lElement *v4lelement; + gint temp_i = 0; + gulong temp_ul = 0; + + /* it's not null if we got it, but it might not be ours */ + g_return_if_fail(GST_IS_V4LELEMENT(object)); + v4lelement = GST_V4LELEMENT(object); + + switch (prop_id) + { + case ARG_CHANNEL: + if (GST_V4L_IS_OPEN(v4lelement)) + gst_v4l_get_chan_norm(v4lelement, &temp_i, NULL); + g_value_set_int(value, temp_i); + break; + case ARG_NORM: + if (GST_V4L_IS_OPEN(v4lelement)) + gst_v4l_get_chan_norm(v4lelement, NULL, &temp_i); + g_value_set_int(value, temp_i); + break; + case ARG_HAS_TUNER: + g_value_set_boolean(value, FALSE); + if (GST_V4L_IS_OPEN(v4lelement)) + if (gst_v4l_has_tuner(v4lelement)) + g_value_set_boolean(value, TRUE); + break; + case ARG_FREQUENCY: + if (GST_V4L_IS_OPEN(v4lelement)) + gst_v4l_get_frequency(v4lelement, &temp_ul); + g_value_set_ulong(value, temp_ul); + break; + case ARG_HAS_AUDIO: + g_value_set_boolean(value, FALSE); + if (GST_V4L_IS_OPEN(v4lelement)) + if (gst_v4l_has_audio(v4lelement)) + g_value_set_boolean(value, TRUE); + break; + case ARG_MUTE: + if (GST_V4L_IS_OPEN(v4lelement)) + if (gst_v4l_has_tuner(v4lelement)) + gst_v4l_get_audio(v4lelement, V4L_AUDIO_MUTE, &temp_i); + g_value_set_int(value, temp_i?TRUE:FALSE); + break; + case ARG_MODE: + if (GST_V4L_IS_OPEN(v4lelement)) + if (gst_v4l_has_tuner(v4lelement)) + gst_v4l_get_audio(v4lelement, V4L_AUDIO_MODE, &temp_i); + g_value_set_int(value, temp_i); + break; + case ARG_VOLUME: + if (GST_V4L_IS_OPEN(v4lelement)) + if (gst_v4l_has_tuner(v4lelement)) + gst_v4l_get_audio(v4lelement, V4L_AUDIO_VOLUME, &temp_i); + g_value_set_int(value, temp_i); + break; + case ARG_HUE: + if (GST_V4L_IS_OPEN(v4lelement)) + gst_v4l_get_picture(v4lelement, V4L_PICTURE_HUE, &temp_i); + g_value_set_int(value, temp_i); + break; + case ARG_BRIGHTNESS: + if (GST_V4L_IS_OPEN(v4lelement)) + gst_v4l_get_picture(v4lelement, V4L_PICTURE_BRIGHTNESS, &temp_i); + g_value_set_int(value, temp_i); + break; + case ARG_CONTRAST: + if (GST_V4L_IS_OPEN(v4lelement)) + gst_v4l_get_picture(v4lelement, V4L_PICTURE_CONTRAST, &temp_i); + g_value_set_int(value, temp_i); + break; + case ARG_SATURATION: + if (GST_V4L_IS_OPEN(v4lelement)) + gst_v4l_get_picture(v4lelement, V4L_PICTURE_SATURATION, &temp_i); + g_value_set_int(value, temp_i); + break; + case ARG_DEVICE: + g_value_set_string(value, v4lelement->videodev?v4lelement->videodev:"/dev/video"); + break; + default: + //G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static GstElementStateReturn +gst_v4lelement_change_state (GstElement *element) +{ + GstV4lElement *v4lelement; + + g_return_val_if_fail(GST_IS_V4LELEMENT(element), FALSE); + + v4lelement = GST_V4LELEMENT(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_PENDING(element)) + { + case GST_STATE_NULL: + if (GST_V4L_IS_OPEN(v4lelement)) + if (!gst_v4l_close(v4lelement)) + return GST_STATE_FAILURE; + break; + case GST_STATE_READY: + if (!GST_V4L_IS_OPEN(v4lelement)) + { + int n, temp; + + if (!gst_v4l_open(v4lelement)) + return GST_STATE_FAILURE; + + /* now, sync options */ + if (v4lelement->norm >= VIDEO_MODE_PAL && + v4lelement->norm < VIDEO_MODE_AUTO && + v4lelement->channel >= 0) + { + if (!gst_v4l_set_chan_norm(v4lelement, v4lelement->channel, v4lelement->norm)) + return GST_STATE_FAILURE; + } + if (v4lelement->frequency > 0 && gst_v4l_has_tuner(v4lelement)) + { + if (!gst_v4l_set_frequency(v4lelement, v4lelement->frequency)) + return GST_STATE_FAILURE; + } + for (n=V4L_AUDIO_VOLUME;n<=V4L_AUDIO_MODE;n++) + { + switch (n) + { + case V4L_AUDIO_MUTE: temp = v4lelement->mute; break; + case V4L_AUDIO_VOLUME: temp = v4lelement->volume; break; + case V4L_AUDIO_MODE: temp = v4lelement->mode; break; + } + if (temp >= 0 && gst_v4l_has_audio(v4lelement)) + { + if (!gst_v4l_set_audio(v4lelement, n, temp)) + return GST_STATE_FAILURE; + } + } + for (n=V4L_PICTURE_HUE;n<=V4L_PICTURE_SATURATION;n++) + { + switch (n) + { + case V4L_PICTURE_HUE: temp = v4lelement->hue; break; + case V4L_PICTURE_BRIGHTNESS: temp = v4lelement->brightness; break; + case V4L_PICTURE_SATURATION: temp = v4lelement->saturation; break; + case V4L_PICTURE_CONTRAST: temp = v4lelement->contrast; break; + } + if (temp >= 0) + { + if (!gst_v4l_set_picture(v4lelement, n, temp)) + return GST_STATE_FAILURE; + } + } + } + break; + default: + 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) +{ +#if 0 + GstElementFactory *factory; + + /* create an elementfactory for the v4lelement */ + factory = gst_elementfactory_new("v4lelement",GST_TYPE_V4LELEMENT, + &gst_v4lelement_details); + g_return_val_if_fail(factory != NULL, FALSE); + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE (factory)); +#endif + return TRUE; +} + + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "v4lelement", + plugin_init +}; diff --git a/sys/v4l/gstv4lelement.h b/sys/v4l/gstv4lelement.h new file mode 100644 index 0000000000..62c973b508 --- /dev/null +++ b/sys/v4l/gstv4lelement.h @@ -0,0 +1,84 @@ +/* G-Streamer generic V4L element - generic V4L calls handling + * Copyright (C) 2001 Ronald Bultje + * + * 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_V4LELEMENT_H__ +#define __GST_V4LELEMENT_H__ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GST_TYPE_V4LELEMENT \ + (gst_v4lelement_get_type()) +#define GST_V4LELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4LELEMENT,GstV4lElement)) +#define GST_V4LELEMENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4LELEMENT,GstV4lElementClass)) +#define GST_IS_V4LELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4LELEMENT)) +#define GST_IS_V4LELEMENT_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4LELEMENT)) + +typedef struct _GstV4lElement GstV4lElement; +typedef struct _GstV4lElementClass GstV4lElementClass; + +struct _GstV4lElement { + GstElement element; + + /* the video device */ + char *videodev; + + /* the video-device's file descriptor */ + gint video_fd; + + /* the video buffer (mmap()'ed) */ + guint8 *buffer; + + /* the video-device's capabilities */ + struct video_capability vcap; + + /* caching values */ + gint channel; + gint norm; + gulong frequency; + gint mute; + gint volume; + gint mode; + gint brightness; + gint hue; + gint contrast; + gint saturation; +}; + +struct _GstV4lElementClass { + GstElementClass parent_class; +}; + +GType gst_v4lelement_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GST_V4LELEMENT_H__ */ diff --git a/sys/v4l/gstv4lmjpegsrc.c b/sys/v4l/gstv4lmjpegsrc.c new file mode 100644 index 0000000000..eca922b7ff --- /dev/null +++ b/sys/v4l/gstv4lmjpegsrc.c @@ -0,0 +1,488 @@ +/* G-Streamer hardware MJPEG video source plugin + * Copyright (C) 2001 Ronald Bultje + * + * 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 +#include "v4lmjpegsrc_calls.h" + +static GstElementDetails gst_v4lmjpegsrc_details = { + "Video (video4linux/MJPEG) Source", + "Source/Video", + "Reads MJPEG-encoded frames from a zoran MJPEG/video4linux device", + VERSION, + "Ronald Bultje ", + "(C) 2001", +}; + +/* V4lMjpegSrc signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +/* arguments */ +enum { + ARG_0, + ARG_X_OFFSET, + ARG_Y_OFFSET, + ARG_F_WIDTH, + ARG_F_HEIGHT, + ARG_H_DECIMATION, + ARG_V_DECIMATION, + ARG_WIDTH, + ARG_HEIGHT, + ARG_QUALITY, + ARG_NUMBUFS, + ARG_BUFSIZE +}; + + +/* init functions */ +static void gst_v4lmjpegsrc_class_init (GstV4lMjpegSrcClass *klass); +static void gst_v4lmjpegsrc_init (GstV4lMjpegSrc *v4lmjpegsrc); + +/* pad/buffer functions */ +static GstPadNegotiateReturn gst_v4lmjpegsrc_negotiate (GstPad *pad, + GstCaps **caps, + gpointer *user_data); +static GstCaps* gst_v4lmjpegsrc_create_caps (GstV4lMjpegSrc *v4lmjpegsrc); +static GstBuffer* gst_v4lmjpegsrc_get (GstPad *pad); + +/* get/set params */ +static void gst_v4lmjpegsrc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_v4lmjpegsrc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +/* state handling */ +static GstElementStateReturn gst_v4lmjpegsrc_change_state (GstElement *element); + +/* bufferpool functions */ +static GstBuffer* gst_v4lmjpegsrc_buffer_new (GstBufferPool *pool, + gint64 location, + gint size, + gpointer user_data); +static GstBuffer* gst_v4lmjpegsrc_buffer_copy (GstBuffer *srcbuf); +static void gst_v4lmjpegsrc_buffer_free (GstBuffer *buf); + + +static GstElementClass *parent_class = NULL; +//static guint gst_v4lmjpegsrc_signals[LAST_SIGNAL] = { 0 }; + + +GType +gst_v4lmjpegsrc_get_type (void) +{ + static GType v4lmjpegsrc_type = 0; + + if (!v4lmjpegsrc_type) { + static const GTypeInfo v4lmjpegsrc_info = { + sizeof(GstV4lMjpegSrcClass), + NULL, + NULL, + (GClassInitFunc)gst_v4lmjpegsrc_class_init, + NULL, + NULL, + sizeof(GstV4lMjpegSrc), + 0, + (GInstanceInitFunc)gst_v4lmjpegsrc_init, + NULL + }; + v4lmjpegsrc_type = g_type_register_static(GST_TYPE_V4LELEMENT, "GstV4lMjpegSrc", &v4lmjpegsrc_info, 0); + } + return v4lmjpegsrc_type; +} + + +static void +gst_v4lmjpegsrc_class_init (GstV4lMjpegSrcClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_V4LELEMENT); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_X_OFFSET, + g_param_spec_int("x_offset","x_offset","x_offset",G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_Y_OFFSET, + g_param_spec_int("y_offset","y_offset","y_offset",G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_F_WIDTH, + g_param_spec_int("frame_width","frame_width","frame_width",G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_F_HEIGHT, + g_param_spec_int("frame_height","frame_height","frame_height",G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_H_DECIMATION, + g_param_spec_int("h_decimation","h_decimation","h_decimation",G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_V_DECIMATION, + g_param_spec_int("v_decimation","v_decimation","v_decimation",G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); + + 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_READABLE)); + 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_READABLE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_QUALITY, + g_param_spec_int("quality","quality","quality",G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); + + 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_READWRITE)); + + gobject_class->set_property = gst_v4lmjpegsrc_set_property; + gobject_class->get_property = gst_v4lmjpegsrc_get_property; + + gstelement_class->change_state = gst_v4lmjpegsrc_change_state; +} + + +static void +gst_v4lmjpegsrc_init (GstV4lMjpegSrc *v4lmjpegsrc) +{ + v4lmjpegsrc->srcpad = gst_pad_new("src", GST_PAD_SRC); + gst_element_add_pad(GST_ELEMENT(v4lmjpegsrc), v4lmjpegsrc->srcpad); + + gst_pad_set_get_function (v4lmjpegsrc->srcpad, gst_v4lmjpegsrc_get); + gst_pad_set_negotiate_function (v4lmjpegsrc->srcpad, gst_v4lmjpegsrc_negotiate); + + v4lmjpegsrc->bufferpool = gst_buffer_pool_new(); + gst_buffer_pool_set_buffer_new_function(v4lmjpegsrc->bufferpool, gst_v4lmjpegsrc_buffer_new); + gst_buffer_pool_set_buffer_copy_function(v4lmjpegsrc->bufferpool, gst_v4lmjpegsrc_buffer_copy); + gst_buffer_pool_set_buffer_free_function(v4lmjpegsrc->bufferpool, gst_v4lmjpegsrc_buffer_free); + gst_buffer_pool_set_user_data(v4lmjpegsrc->bufferpool, v4lmjpegsrc); + + v4lmjpegsrc->frame_width = 0; + v4lmjpegsrc->frame_height = 0; + v4lmjpegsrc->x_offset = -1; + v4lmjpegsrc->y_offset = -1; + + v4lmjpegsrc->horizontal_decimation = 4; + v4lmjpegsrc->vertical_decimation = 4; + + v4lmjpegsrc->end_width = 0; + v4lmjpegsrc->end_height = 0; + + v4lmjpegsrc->quality = 50; + + v4lmjpegsrc->numbufs = 64; + v4lmjpegsrc->bufsize = 256 * 1024; + + v4lmjpegsrc->init = TRUE; +} + + + +static GstPadNegotiateReturn +gst_v4lmjpegsrc_negotiate (GstPad *pad, + GstCaps **caps, + gpointer *user_data) +{ + GstV4lMjpegSrc *v4lmjpegsrc; + + v4lmjpegsrc = GST_V4LMJPEGSRC (gst_pad_get_parent (pad)); + + if (!*caps) { + return GST_PAD_NEGOTIATE_FAIL; + } + else { + return GST_PAD_NEGOTIATE_AGREE; + } + + return GST_PAD_NEGOTIATE_FAIL; +} + + +static GstCaps* +gst_v4lmjpegsrc_create_caps (GstV4lMjpegSrc *v4lmjpegsrc) +{ + GstCaps *caps; + + caps = GST_CAPS_NEW ( + "v4lmjpegsrc_caps", + "video/jpeg", + "width", GST_PROPS_INT(v4lmjpegsrc->end_width), + "height", GST_PROPS_INT(v4lmjpegsrc->end_height) + ); + + return caps; +} + + +static GstBuffer* +gst_v4lmjpegsrc_get (GstPad *pad) +{ + GstV4lMjpegSrc *v4lmjpegsrc; + GstBuffer *buf; + gint num; + + g_return_val_if_fail (pad != NULL, NULL); + + v4lmjpegsrc = GST_V4LMJPEGSRC (gst_pad_get_parent (pad)); + + if (v4lmjpegsrc->init) { + gst_pad_set_caps (v4lmjpegsrc->srcpad, gst_v4lmjpegsrc_create_caps (v4lmjpegsrc)); + v4lmjpegsrc->init = FALSE; + } + else { + if (!gst_pad_get_caps (v4lmjpegsrc->srcpad) && + !gst_pad_renegotiate (v4lmjpegsrc->srcpad)) { + return NULL; + } + } + + buf = gst_buffer_new_from_pool(v4lmjpegsrc->bufferpool, 0, 0); + if (!buf) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Failed to create a new GstBuffer"); + return NULL; + } + + /* grab a frame from the device */ + if (!gst_v4lmjpegsrc_grab_frame(v4lmjpegsrc, &num, &(GST_BUFFER_SIZE(buf)))) + return NULL; + GST_BUFFER_DATA(buf) = gst_v4lmjpegsrc_get_buffer(v4lmjpegsrc, num); + buf->timestamp = v4lmjpegsrc->bsync.timestamp.tv_sec * 1000000000 + + v4lmjpegsrc->bsync.timestamp.tv_usec * 1000; + + return buf; +} + + +static void +gst_v4lmjpegsrc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GstV4lMjpegSrc *v4lmjpegsrc; + + g_return_if_fail(GST_IS_V4LMJPEGSRC(object)); + v4lmjpegsrc = GST_V4LMJPEGSRC(object); + + switch (prop_id) { + case ARG_X_OFFSET: + v4lmjpegsrc->x_offset = g_value_get_int(value); + break; + case ARG_Y_OFFSET: + v4lmjpegsrc->y_offset = g_value_get_int(value); + break; + case ARG_F_WIDTH: + v4lmjpegsrc->frame_width = g_value_get_int(value); + break; + case ARG_F_HEIGHT: + v4lmjpegsrc->frame_height = g_value_get_int(value); + break; + case ARG_H_DECIMATION: + v4lmjpegsrc->horizontal_decimation = g_value_get_int(value); + break; + case ARG_V_DECIMATION: + v4lmjpegsrc->vertical_decimation = g_value_get_int(value); + break; + case ARG_QUALITY: + v4lmjpegsrc->quality = g_value_get_int(value); + break; + case ARG_NUMBUFS: + v4lmjpegsrc->numbufs = g_value_get_int(value); + break; + case ARG_BUFSIZE: + v4lmjpegsrc->bufsize = g_value_get_int(value); + break; + default: + parent_class->set_property(object, prop_id, value, pspec); + break; + } +} + + +static void +gst_v4lmjpegsrc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GstV4lMjpegSrc *v4lmjpegsrc; + + g_return_if_fail(GST_IS_V4LMJPEGSRC(object)); + v4lmjpegsrc = GST_V4LMJPEGSRC(object); + + switch (prop_id) { + case ARG_WIDTH: + g_value_set_int(value, v4lmjpegsrc->end_width); + break; + case ARG_HEIGHT: + g_value_set_int(value, v4lmjpegsrc->end_height); + break; + case ARG_NUMBUFS: + g_value_set_int(value, v4lmjpegsrc->breq.count); + break; + case ARG_BUFSIZE: + g_value_set_int(value, v4lmjpegsrc->breq.size); + break; + default: + parent_class->get_property(object, prop_id, value, pspec); + break; + } +} + + +static GstElementStateReturn +gst_v4lmjpegsrc_change_state (GstElement *element) +{ + GstV4lMjpegSrc *v4lmjpegsrc; + + g_return_val_if_fail(GST_IS_V4LMJPEGSRC(element), FALSE); + + v4lmjpegsrc = GST_V4LMJPEGSRC(element); + + switch (GST_STATE_PENDING(element)) { + case GST_STATE_READY: + if (GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc))) { + /* stop capturing, unmap all buffers */ + if (!gst_v4lmjpegsrc_capture_deinit(v4lmjpegsrc)) + return GST_STATE_FAILURE; + } + break; + case GST_STATE_PAUSED: + if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc))) { + /* set buffer info */ + if (!gst_v4lmjpegsrc_set_buffer(v4lmjpegsrc, v4lmjpegsrc->numbufs, v4lmjpegsrc->bufsize)) + return GST_STATE_FAILURE; + /* set capture parameters and mmap the buffers */ + if (!v4lmjpegsrc->frame_width && !v4lmjpegsrc->frame_height && + v4lmjpegsrc->x_offset < 0 && v4lmjpegsrc->y_offset < 0 && + v4lmjpegsrc->horizontal_decimation == v4lmjpegsrc->vertical_decimation) + { + if (!gst_v4lmjpegsrc_set_capture(v4lmjpegsrc, + v4lmjpegsrc->horizontal_decimation, v4lmjpegsrc->quality)) + return GST_STATE_FAILURE; + } + else + { + if (!gst_v4lmjpegsrc_set_capture_m(v4lmjpegsrc, + v4lmjpegsrc->x_offset, v4lmjpegsrc->y_offset, + v4lmjpegsrc->frame_width, v4lmjpegsrc->frame_height, + v4lmjpegsrc->horizontal_decimation, v4lmjpegsrc->vertical_decimation, + v4lmjpegsrc->quality)) + return GST_STATE_FAILURE; + } + v4lmjpegsrc->init = TRUE; + if (!gst_v4lmjpegsrc_capture_init(v4lmjpegsrc)) + return GST_STATE_FAILURE; + } + else { + /* de-queue all queued buffers */ + if (!gst_v4lmjpegsrc_capture_stop(v4lmjpegsrc)) + return GST_STATE_FAILURE; + } + break; + case GST_STATE_PLAYING: + /* queue all buffer, start streaming capture */ + if (!gst_v4lmjpegsrc_capture_start(v4lmjpegsrc)) + 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 GstBuffer* +gst_v4lmjpegsrc_buffer_new (GstBufferPool *pool, + gint64 location, + gint size, + gpointer user_data) +{ + GstBuffer *buffer; + + buffer = gst_buffer_new(); + if (!buffer) return NULL; + buffer->pool_private = user_data; + + /* TODO: add interlacing info to buffer as metadata */ + + return buffer; +} + + +static GstBuffer* +gst_v4lmjpegsrc_buffer_copy (GstBuffer *srcbuf) +{ + 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_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_v4lmjpegsrc_buffer_free (GstBuffer *buf) +{ + GstV4lMjpegSrc *v4lmjpegsrc = buf->pool_private; + int n; + + for (n=0;nbreq.count;n++) + if (GST_BUFFER_DATA(buf) == gst_v4lmjpegsrc_get_buffer(v4lmjpegsrc, n)) + { + gst_v4lmjpegsrc_requeue_frame(v4lmjpegsrc, n); + return; + } + + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Couldn't find the buffer"); +} + + +static gboolean +plugin_init (GModule *module, GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* create an elementfactory for the v4lmjpegsrcparse element */ + factory = gst_elementfactory_new("v4lmjpegsrc",GST_TYPE_V4LMJPEGSRC, + &gst_v4lmjpegsrc_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, + "v4lmjpegsrc", + plugin_init +}; diff --git a/sys/v4l/gstv4lmjpegsrc.h b/sys/v4l/gstv4lmjpegsrc.h new file mode 100644 index 0000000000..e05a58dc89 --- /dev/null +++ b/sys/v4l/gstv4lmjpegsrc.h @@ -0,0 +1,86 @@ +/* G-Streamer hardware MJPEG video source plugin + * Copyright (C) 2001 Ronald Bultje + * + * 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_V4LMJPEGSRC_H__ +#define __GST_V4LMJPEGSRC_H__ + +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#define GST_TYPE_V4LMJPEGSRC \ + (gst_v4lmjpegsrc_get_type()) +#define GST_V4LMJPEGSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4LMJPEGSRC,GstV4lMjpegSrc)) +#define GST_V4LMJPEGSRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4LMJPEGSRC,GstV4lMjpegSrcClass)) +#define GST_IS_V4LMJPEGSRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4LMJPEGSRC)) +#define GST_IS_V4LMJPEGSRC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4LMJPEGSRC)) + +typedef struct _GstV4lMjpegSrc GstV4lMjpegSrc; +typedef struct _GstV4lMjpegSrcClass GstV4lMjpegSrcClass; + +struct _GstV4lMjpegSrc { + GstV4lElement v4lelement; + + /* pads */ + GstPad *srcpad; + + /* the bufferpool */ + GstBufferPool *bufferpool; + + /* buffer/capture info */ + struct mjpeg_sync bsync; + struct mjpeg_requestbuffers breq; + + /* caching values */ + gint x_offset; + gint y_offset; + gint frame_width; + gint frame_height; + gint horizontal_decimation; + gint vertical_decimation; + + gint end_width; + gint end_height; + + gint quality; + gint numbufs; + gint bufsize; + + gboolean init; +}; + +struct _GstV4lMjpegSrcClass { + GstV4lElementClass parent_class; +}; + +GType gst_v4lmjpegsrc_get_type(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __GST_V4LMJPEGSRC_H__ */ diff --git a/sys/v4l/gstv4lsrc.c b/sys/v4l/gstv4lsrc.c index fc847c8721..d12afdcc21 100644 --- a/sys/v4l/gstv4lsrc.c +++ b/sys/v4l/gstv4lsrc.c @@ -1,5 +1,5 @@ -/* Gnome-Streamer - * Copyright (C) <1999> Erik Walthinsen +/* G-Streamer BT8x8/V4L frame grabber plugin + * Copyright (C) 2001 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,25 +17,18 @@ * Boston, MA 02111-1307, USA. */ -#include -#include -#include -#include -#include #include +#include +#include "v4lsrc_calls.h" -//#define DEBUG_ENABLED -#include - -#include static GstElementDetails gst_v4lsrc_details = { - "Video (v4l) Source", + "Video (video4linux/raw) Source", "Source/Video", - "Read from a Video for Linux capture device", + "Reads raw frames from a video4linux (BT8x8) device", VERSION, - "Wim Taymans ", - "(C) 2000", + "Ronald Bultje ", + "(C) 2001", }; /* V4lSrc signals and args */ @@ -44,43 +37,51 @@ enum { LAST_SIGNAL }; +/* arguments */ enum { ARG_0, ARG_WIDTH, ARG_HEIGHT, - ARG_FORMAT, - ARG_TUNE, - ARG_TUNED, - ARG_INPUT, - ARG_NORM, - ARG_VOLUME, - ARG_MUTE, - ARG_AUDIO_MODE, - ARG_COLOR, - ARG_BRIGHT, - ARG_HUE, - ARG_CONTRAST, - ARG_DEVICE, + ARG_PALETTE }; -static void gst_v4lsrc_class_init (GstV4lSrcClass *klass); -static void gst_v4lsrc_init (GstV4lSrc *v4lsrc); +/* init functions */ +static void gst_v4lsrc_class_init (GstV4lSrcClass *klass); +static void gst_v4lsrc_init (GstV4lSrc *v4lsrc); -static void gst_v4lsrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec); -static void gst_v4lsrc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec); +/* pad/buffer functions */ +static GstPadNegotiateReturn gst_v4lsrc_negotiate (GstPad *pad, + GstCaps **caps, + gpointer *user_data); +static GstCaps* gst_v4lsrc_create_caps (GstV4lSrc *v4lsrc); +static GstBuffer* gst_v4lsrc_get (GstPad *pad); -static GstElementStateReturn gst_v4lsrc_change_state (GstElement *element); -static void gst_v4lsrc_close_v4l (GstV4lSrc *src); -static gboolean gst_v4lsrc_open_v4l (GstV4lSrc *src); +/* get/set params */ +static void gst_v4lsrc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_v4lsrc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); -static GstBuffer* gst_v4lsrc_get (GstPad *pad); -static GstPadNegotiateReturn gst_v4lsrc_negotiate (GstPad *pad, GstCaps **caps, gpointer *user_data); +/* state handling */ +static GstElementStateReturn gst_v4lsrc_change_state (GstElement *element); -static gboolean gst_v4lsrc_sync_parms (GstV4lSrc *v4lsrc); +/* bufferpool functions */ +static GstBuffer* gst_v4lsrc_buffer_new (GstBufferPool *pool, + gint64 location, + gint size, + gpointer user_data); +static GstBuffer* gst_v4lsrc_buffer_copy (GstBuffer *srcbuf); +static void gst_v4lsrc_buffer_free (GstBuffer *buf); + + +static GstElementClass *parent_class = NULL;\ +//static guint gst_v4lsrc_signals[LAST_SIGNAL] = { 0 }; -static GstElementClass *parent_class = NULL; -////static guint gst_v4lsrc_signals[LAST_SIGNAL] = { 0 }; GType gst_v4lsrc_get_type (void) @@ -89,7 +90,8 @@ gst_v4lsrc_get_type (void) if (!v4lsrc_type) { static const GTypeInfo v4lsrc_info = { - sizeof(GstV4lSrcClass), NULL, + sizeof(GstV4lSrcClass), + NULL, NULL, (GClassInitFunc)gst_v4lsrc_class_init, NULL, @@ -99,11 +101,12 @@ gst_v4lsrc_get_type (void) (GInstanceInitFunc)gst_v4lsrc_init, NULL }; - v4lsrc_type = g_type_register_static(GST_TYPE_ELEMENT, "GstV4lSrc", &v4lsrc_info, 0); + v4lsrc_type = g_type_register_static(GST_TYPE_V4LELEMENT, "GstV4lSrc", &v4lsrc_info, 0); } return v4lsrc_type; } + static void gst_v4lsrc_class_init (GstV4lSrcClass *klass) { @@ -113,54 +116,14 @@ gst_v4lsrc_class_init (GstV4lSrcClass *klass) gobject_class = (GObjectClass*)klass; gstelement_class = (GstElementClass*)klass; - parent_class = g_type_class_ref(GST_TYPE_ELEMENT); + parent_class = g_type_class_ref(GST_TYPE_V4LELEMENT); 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)); // CHECKME + 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)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FORMAT, - g_param_spec_int("format","format","format", - G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME - - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TUNE, - g_param_spec_ulong("tune","tune","tune", - 0,G_MAXULONG,0,G_PARAM_WRITABLE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_TUNED, - g_param_spec_boolean("tuned","tuned","tuned", - TRUE,G_PARAM_READABLE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_INPUT, - g_param_spec_int("input","input","input", - G_MININT,G_MAXINT,0,G_PARAM_WRITABLE)); // CHECKME - 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_WRITABLE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_VOLUME, - g_param_spec_int("volume","volume","volume", - G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_MUTE, - g_param_spec_boolean("mute","mute","mute", - TRUE,G_PARAM_READWRITE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_AUDIO_MODE, - g_param_spec_int("mode","mode","mode", - G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_COLOR, - g_param_spec_int("color","color","color", - G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BRIGHT, - g_param_spec_int("bright","bright","bright", - G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HUE, - g_param_spec_int("hue","hue","hue", - G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_CONTRAST, - g_param_spec_int("contrast","contrast","contrast", - G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); // CHECKME - g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_DEVICE, - g_param_spec_string("device","device","device", - NULL, G_PARAM_READWRITE)); // CHECKME + 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)); gobject_class->set_property = gst_v4lsrc_set_property; gobject_class->get_property = gst_v4lsrc_get_property; @@ -168,33 +131,38 @@ gst_v4lsrc_class_init (GstV4lSrcClass *klass) gstelement_class->change_state = gst_v4lsrc_change_state; } + static void gst_v4lsrc_init (GstV4lSrc *v4lsrc) { - v4lsrc->srcpad = gst_pad_new("src",GST_PAD_SRC); - gst_element_add_pad(GST_ELEMENT(v4lsrc),v4lsrc->srcpad); + v4lsrc->srcpad = gst_pad_new("src", GST_PAD_SRC); + gst_element_add_pad(GST_ELEMENT(v4lsrc), v4lsrc->srcpad); - gst_pad_set_get_function (v4lsrc->srcpad,gst_v4lsrc_get); - gst_pad_set_negotiate_function (v4lsrc->srcpad,gst_v4lsrc_negotiate); + gst_pad_set_get_function (v4lsrc->srcpad, gst_v4lsrc_get); + gst_pad_set_negotiate_function (v4lsrc->srcpad, gst_v4lsrc_negotiate); + + v4lsrc->bufferpool = gst_buffer_pool_new(); + gst_buffer_pool_set_buffer_new_function(v4lsrc->bufferpool, gst_v4lsrc_buffer_new); + gst_buffer_pool_set_buffer_copy_function(v4lsrc->bufferpool, gst_v4lsrc_buffer_copy); + gst_buffer_pool_set_buffer_free_function(v4lsrc->bufferpool, gst_v4lsrc_buffer_free); + gst_buffer_pool_set_user_data(v4lsrc->bufferpool, v4lsrc); + + v4lsrc->palette = VIDEO_PALETTE_YUV420P; + v4lsrc->width = 160; + v4lsrc->height = 120; + v4lsrc->buffer_size = v4lsrc->width * v4lsrc->height * 1.5; - /* if the destination cannot say what it wants, we give this */ - v4lsrc->width = 100; - v4lsrc->height = 100; - v4lsrc->format = 0; - v4lsrc->buffer_size = v4lsrc->width * v4lsrc->height * 3; - // make a grbber - v4lsrc->grabber = grab_init(); - v4lsrc->device = NULL; v4lsrc->init = TRUE; } + static GstPadNegotiateReturn -gst_v4lsrc_negotiate (GstPad *pad, GstCaps **caps, gpointer *user_data) +gst_v4lsrc_negotiate (GstPad *pad, + GstCaps **caps, + gpointer *user_data) { GstV4lSrc *v4lsrc; - GST_DEBUG (0, "v4lsrc: negotiate %p\n", user_data); - v4lsrc = GST_V4LSRC (gst_pad_get_parent (pad)); if (!*caps) { @@ -204,175 +172,181 @@ gst_v4lsrc_negotiate (GstPad *pad, GstCaps **caps, gpointer *user_data) gint width, height; gulong format; - GST_DEBUG (0, "%08lx\n", gst_caps_get_fourcc_int (*caps, "format")); - width = gst_caps_get_int (*caps, "width"); height = gst_caps_get_int (*caps, "height"); - - format = gst_caps_get_fourcc_int (*caps, "format"); - - g_print ("v4lsrc: got format %08lx\n", format); + format = gst_caps_get_fourcc_int (*caps, "format"); switch (format) { case GST_MAKE_FOURCC ('R','G','B',' '): { - gint depth, endianness, bpp; + gint depth; depth = gst_caps_get_int (*caps, "depth"); - bpp = gst_caps_get_int (*caps, "bpp"); - endianness = gst_caps_get_int (*caps, "endianness"); - GST_DEBUG (0, "%d\n", depth); - g_print ("v4lsrc: got depth %d, bpp %d, endianness %d\n", depth, bpp, endianness); - switch (depth) { + switch (depth) { case 15: - v4lsrc->format = (endianness == G_LITTLE_ENDIAN ? - VIDEO_RGB15_LE: - VIDEO_RGB15_BE); + v4lsrc->palette = VIDEO_PALETTE_RGB555; v4lsrc->buffer_size = width * height * 2; break; case 16: - v4lsrc->format = (endianness == G_LITTLE_ENDIAN ? - VIDEO_RGB16_LE: - VIDEO_RGB16_BE); + v4lsrc->palette = VIDEO_PALETTE_RGB565; v4lsrc->buffer_size = width * height * 2; break; case 24: - v4lsrc->format = (endianness == G_LITTLE_ENDIAN ? - VIDEO_BGR24: - VIDEO_RGB24); + v4lsrc->palette = VIDEO_PALETTE_RGB24; v4lsrc->buffer_size = width * height * 3; break; case 32: - v4lsrc->format = (endianness == G_LITTLE_ENDIAN ? - VIDEO_BGR32: - VIDEO_RGB32); + v4lsrc->palette = VIDEO_PALETTE_RGB32; v4lsrc->buffer_size = width * height * 4; break; default: *caps = NULL; return GST_PAD_NEGOTIATE_TRY; - } - break; + } + + break; } + case GST_MAKE_FOURCC ('I','4','2','0'): - v4lsrc->format = VIDEO_YUV420P; - v4lsrc->buffer_size = width * height + - width * height / 2; - break; + v4lsrc->palette = VIDEO_PALETTE_YUV420P; + v4lsrc->buffer_size = width * height * 1.5; + break; + case GST_MAKE_FOURCC ('U','Y','V','Y'): - if (G_BYTE_ORDER == G_BIG_ENDIAN) { - v4lsrc->format = VIDEO_YUV422; - v4lsrc->buffer_size = width * height * 2; - break; - } - else { - *caps = NULL; - return GST_PAD_NEGOTIATE_TRY; - } + v4lsrc->palette = VIDEO_PALETTE_UYVY; //YUV422?; + v4lsrc->buffer_size = width * height * 2; + break; + case GST_MAKE_FOURCC ('Y','U','Y','2'): - if (G_BYTE_ORDER == G_LITTLE_ENDIAN) { - v4lsrc->format = VIDEO_YUV422; - v4lsrc->buffer_size = width * height * 2; - break; - } - else { - *caps = NULL; - return GST_PAD_NEGOTIATE_TRY; - } + v4lsrc->palette = VIDEO_PALETTE_YUYV; //YUV422?; + v4lsrc->buffer_size = width * height * 2; + break; + + /* TODO: add YUV4:2:2 planar and YUV4:2:0 packed, maybe also YUV4:1:1? */ + default: *caps = NULL; return GST_PAD_NEGOTIATE_TRY; + } + + /* if we get here, it's okay */ v4lsrc->width = width; v4lsrc->height = height; - if (gst_v4lsrc_sync_parms (v4lsrc)) { - return GST_PAD_NEGOTIATE_AGREE; - } - else { - *caps = NULL; - return GST_PAD_NEGOTIATE_TRY; - } + return GST_PAD_NEGOTIATE_AGREE; } return GST_PAD_NEGOTIATE_FAIL; } + static GstCaps* -gst_v4lsrc_create_caps (GstV4lSrc *src) +gst_v4lsrc_create_caps (GstV4lSrc *v4lsrc) { - GstCaps *caps; + GstCaps *caps = NULL; gulong fourcc = 0; - gint width, height; - width = src->width; - height = src->height; + switch (v4lsrc->palette) { + case VIDEO_PALETTE_RGB555: + case VIDEO_PALETTE_RGB565: + case VIDEO_PALETTE_RGB24: + case VIDEO_PALETTE_RGB32: + { + int depth=0, bpp=0; - switch (src->format) { - case VIDEO_RGB08: - case VIDEO_GRAY: - case VIDEO_LUT2: - case VIDEO_LUT4: - caps = NULL; - break; - case VIDEO_RGB15_LE: - case VIDEO_RGB16_LE: - case VIDEO_RGB15_BE: - case VIDEO_RGB16_BE: - case VIDEO_BGR24: - case VIDEO_BGR32: - case VIDEO_RGB24: - case VIDEO_RGB32: - caps = NULL; - break; - case VIDEO_YUV422: - case VIDEO_YUV422P: - case VIDEO_YUV420P: { + fourcc = GST_STR_FOURCC ("RGB "); - if (src->format == VIDEO_YUV422) { - fourcc = GST_STR_FOURCC ("YUY2"); - src->buffer_size = width * height * 2; + switch (v4lsrc->palette) { + case VIDEO_PALETTE_RGB555: + depth = 15; + bpp = 2; + break; + case VIDEO_PALETTE_RGB565: + depth = 16; + bpp = 2; + break; + case VIDEO_PALETTE_RGB24: + depth = 24; + bpp = 3; + break; + case VIDEO_PALETTE_RGB32: + depth = 32; + bpp = 4; + break; } - else if (src->format == VIDEO_YUV422P) { - fourcc = GST_STR_FOURCC ("YV12"); - src->buffer_size = width * height * 2; - } - else if (src->format == VIDEO_YUV420P) { - fourcc = GST_STR_FOURCC ("I420"); - src->buffer_size = width * height + - width * height / 2; - } - + caps = GST_CAPS_NEW ( - "v4lsrc_caps", - "video/raw", - "format", GST_PROPS_FOURCC (fourcc), - "width", GST_PROPS_INT (src->width), - "height", GST_PROPS_INT (src->height) - ); + "v4lsrc_caps", + "video/raw", + "format", GST_PROPS_FOURCC (fourcc), + "width", GST_PROPS_INT (v4lsrc->width), + "height", GST_PROPS_INT (v4lsrc->height), + "bpp", GST_PROPS_INT (bpp), + "depth", GST_PROPS_INT (depth) + ); + + v4lsrc->buffer_size = v4lsrc->width * v4lsrc->height * bpp; + break; } - default: - caps = NULL; + + case VIDEO_PALETTE_YUV422: + case VIDEO_PALETTE_YUV420P: + { + switch (v4lsrc->palette) { + case VIDEO_PALETTE_YUV422: + fourcc = (G_BYTE_ORDER == G_BIG_ENDIAN) ? + GST_STR_FOURCC("UYVY") : GST_STR_FOURCC("YUY2"); + v4lsrc->buffer_size = v4lsrc->width * v4lsrc->height * 2; + break; + case VIDEO_PALETTE_YUYV: + fourcc = GST_STR_FOURCC("YUY2"); + v4lsrc->buffer_size = v4lsrc->width * v4lsrc->height * 2; + break; + case VIDEO_PALETTE_UYVY: + fourcc = GST_STR_FOURCC("UYVY"); + v4lsrc->buffer_size = v4lsrc->width * v4lsrc->height * 2; + break; + case VIDEO_PALETTE_YUV420P: + fourcc = GST_STR_FOURCC("I420"); + v4lsrc->buffer_size = v4lsrc->width * v4lsrc->height * 1.5; + break; + } + + caps = GST_CAPS_NEW ( + "v4lsrc_caps", + "video/raw", + "format", GST_PROPS_FOURCC (fourcc), + "width", GST_PROPS_INT (v4lsrc->width), + "height", GST_PROPS_INT (v4lsrc->height) + ); + break; + } + + default: + return NULL; } return caps; } + static GstBuffer* gst_v4lsrc_get (GstPad *pad) { GstV4lSrc *v4lsrc; - GstBuffer *buf = NULL; - guint8 *grab_buf; + GstBuffer *buf; + gint num; + struct timeval timestamp; g_return_val_if_fail (pad != NULL, NULL); v4lsrc = GST_V4LSRC (gst_pad_get_parent (pad)); - if (v4lsrc->format && v4lsrc->init) { + if (v4lsrc->init) { gst_pad_set_caps (v4lsrc->srcpad, gst_v4lsrc_create_caps (v4lsrc)); v4lsrc->init = FALSE; } @@ -383,172 +357,125 @@ gst_v4lsrc_get (GstPad *pad) } } - buf = gst_buffer_new(); - GST_BUFFER_DATA(buf) = g_malloc(v4lsrc->buffer_size); + buf = gst_buffer_new_from_pool(v4lsrc->bufferpool, 0, 0); + if (!buf) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Failed to create a new GstBuffer"); + return NULL; + } + + /* grab a frame from the device */ + if (!gst_v4lsrc_grab_frame(v4lsrc, &num)) + return NULL; + gettimeofday(×tamp, 0); /* TODO: threaded sync() */ + GST_BUFFER_DATA(buf) = gst_v4lsrc_get_buffer(v4lsrc, num); GST_BUFFER_SIZE(buf) = v4lsrc->buffer_size; - GST_DEBUG (0,"v4lsrc: making new buffer %p\n", GST_BUFFER_DATA(buf)); - - GST_DEBUG (0,"v4lsrc: request buffer\n"); - // request a buffer from the grabber - grab_buf = v4lsrc->grabber->grab_capture(v4lsrc->grabber, 0); - //meta_pull->overlay_info->did_overlay = FALSE; - - g_assert(buf != NULL); - - GST_DEBUG (0,"v4lsrc: sending %d bytes in %p\n", GST_BUFFER_SIZE(buf), GST_BUFFER_DATA(buf)); - // copy the buffer - memcpy(GST_BUFFER_DATA(buf), grab_buf, GST_BUFFER_SIZE(buf)); - - GST_DEBUG (0,"v4lsrc: sent %d bytes in %p\n", GST_BUFFER_SIZE(buf), GST_BUFFER_DATA(buf)); + buf->timestamp = timestamp.tv_sec * 1000000000 + timestamp.tv_usec * 1000; return buf; } -static void -gst_v4lsrc_set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec) -{ - GstV4lSrc *src; - int ret = 0; - /* it's not null if we got it, but it might not be ours */ +static void +gst_v4lsrc_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GstV4lSrc *v4lsrc; + g_return_if_fail(GST_IS_V4LSRC(object)); - src = GST_V4LSRC(object); + v4lsrc = GST_V4LSRC(object); switch (prop_id) { case ARG_WIDTH: - src->width = g_value_get_int (value); - gst_v4lsrc_sync_parms(src); + v4lsrc->width = g_value_get_int(value); break; + case ARG_HEIGHT: - src->height = g_value_get_int (value); - gst_v4lsrc_sync_parms(src); + v4lsrc->height = g_value_get_int(value); break; - case ARG_FORMAT: - src->format = g_value_get_int (value); - break; - case ARG_TUNE: - src->tune = g_value_get_ulong (value); - ret = src->grabber->grab_tune(src->grabber, src->tune); - break; - case ARG_INPUT: - src->input = g_value_get_int (value); - ret = src->grabber->grab_input(src->grabber, src->input, -1); - break; - case ARG_NORM: - src->norm = g_value_get_int (value); - ret = src->grabber->grab_input(src->grabber, -1, src->norm); - break; - case ARG_VOLUME: - src->volume = g_value_get_int (value); - ret = src->grabber->grab_setattr(src->grabber, GRAB_ATTR_VOLUME, src->volume); - break; - case ARG_MUTE: - src->mute = g_value_get_boolean (value); - ret = src->grabber->grab_setattr(src->grabber, GRAB_ATTR_MUTE, src->mute); - break; - case ARG_AUDIO_MODE: - src->audio_mode = g_value_get_int (value); - ret = src->grabber->grab_setattr(src->grabber, GRAB_ATTR_MODE, src->audio_mode); - break; - case ARG_COLOR: - src->color = g_value_get_int (value); - ret = src->grabber->grab_setattr(src->grabber, GRAB_ATTR_COLOR, src->color); - break; - case ARG_BRIGHT: - src->bright = g_value_get_int (value); - ret = src->grabber->grab_setattr(src->grabber, GRAB_ATTR_BRIGHT, src->bright); - break; - case ARG_HUE: - src->hue = g_value_get_int (value); - ret = src->grabber->grab_setattr(src->grabber, GRAB_ATTR_HUE, src->hue); - break; - case ARG_CONTRAST: - src->contrast = g_value_get_int (value); - ret = src->grabber->grab_setattr(src->grabber, GRAB_ATTR_CONTRAST, src->contrast); - break; - case ARG_DEVICE: - if (src->device) - g_free (src->device); - src->device = g_strdup (g_value_get_string (value)); + + case ARG_PALETTE: + v4lsrc->palette = g_value_get_int(value); break; + default: - ret = -1; + parent_class->set_property(object, prop_id, value, pspec); break; } - if (ret == -1) { - fprintf(stderr, "v4lsrc: error setting property\n"); - } } + static void -gst_v4lsrc_get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec) +gst_v4lsrc_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) { - GstV4lSrc *src; + GstV4lSrc *v4lsrc; - /* it's not null if we got it, but it might not be ours */ g_return_if_fail(GST_IS_V4LSRC(object)); - src = GST_V4LSRC(object); - - g_print ("get arg\n"); + v4lsrc = GST_V4LSRC(object); switch (prop_id) { case ARG_WIDTH: - g_value_set_int (value, src->width); + g_value_set_int(value, v4lsrc->mmap.width); break; + case ARG_HEIGHT: - g_value_set_int (value, src->height); + g_value_set_int(value, v4lsrc->mmap.height); break; - case ARG_TUNED: - g_value_set_boolean (value, src->grabber->grab_tuned(src->grabber)); - break; - case ARG_VOLUME: - g_value_set_int (value, src->grabber->grab_getattr(src->grabber, GRAB_ATTR_VOLUME)); - break; - case ARG_MUTE: - g_value_set_int (value, src->grabber->grab_getattr(src->grabber, GRAB_ATTR_MUTE)); - break; - case ARG_AUDIO_MODE: - g_value_set_int (value, src->grabber->grab_getattr(src->grabber, GRAB_ATTR_MODE)); - break; - case ARG_COLOR: - g_value_set_int (value, src->grabber->grab_getattr(src->grabber, GRAB_ATTR_COLOR)); - break; - case ARG_BRIGHT: - g_value_set_int (value, src->grabber->grab_getattr(src->grabber, GRAB_ATTR_BRIGHT)); - break; - case ARG_HUE: - g_value_set_int (value, src->grabber->grab_getattr(src->grabber, GRAB_ATTR_HUE)); - break; - case ARG_CONTRAST: - g_value_set_int (value, src->grabber->grab_getattr(src->grabber, GRAB_ATTR_CONTRAST)); - break; - case ARG_DEVICE: - g_value_set_string (value, src->device); + + case ARG_PALETTE: + g_value_set_int(value, v4lsrc->mmap.format); break; + default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + parent_class->get_property(object, prop_id, value, pspec); break; } } + static GstElementStateReturn gst_v4lsrc_change_state (GstElement *element) { + GstV4lSrc *v4lsrc; + g_return_val_if_fail(GST_IS_V4LSRC(element), FALSE); + + v4lsrc = GST_V4LSRC(element); - /* if going down into NULL state, close the file if it's open */ - if (GST_STATE_PENDING(element) == GST_STATE_NULL) { - if (GST_FLAG_IS_SET(element,GST_V4LSRC_OPEN)) - gst_v4lsrc_close_v4l(GST_V4LSRC(element)); - /* otherwise (READY or higher) we need to open the sound card */ - } else { - gst_info ("v4lsrc: opening\n"); - if (!GST_FLAG_IS_SET(element,GST_V4LSRC_OPEN)) { - if (!gst_v4lsrc_open_v4l(GST_V4LSRC(element))) { - gst_info ("v4lsrc: open failed\n"); - return GST_STATE_FAILURE; + switch (GST_STATE_PENDING(element)) { + case GST_STATE_READY: + if (GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lsrc))) { + /* stop capturing, unmap all buffers */ + if (!gst_v4lsrc_capture_deinit(v4lsrc)) + return GST_STATE_FAILURE; } - } + break; + case GST_STATE_PAUSED: + if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lsrc))) { + /* set capture parameters and mmap the buffers */ + if (!gst_v4lsrc_set_capture(v4lsrc, v4lsrc->width, v4lsrc->height, v4lsrc->palette)) + return GST_STATE_FAILURE; + v4lsrc->init = TRUE; + if (!gst_v4lsrc_capture_init(v4lsrc)) + return GST_STATE_FAILURE; + } + else { + /* de-queue all queued buffers */ + if (!gst_v4lsrc_capture_stop(v4lsrc)) + return GST_STATE_FAILURE; + } + break; + case GST_STATE_PLAYING: + /* queue all buffer, start streaming capture */ + if (!gst_v4lsrc_capture_start(v4lsrc)) + return GST_STATE_FAILURE; + break; } if (GST_ELEMENT_CLASS(parent_class)->change_state) @@ -557,61 +484,67 @@ gst_v4lsrc_change_state (GstElement *element) return GST_STATE_SUCCESS; } -static gboolean -gst_v4lsrc_sync_parms (GstV4lSrc *src) + +static GstBuffer* +gst_v4lsrc_buffer_new (GstBufferPool *pool, + gint64 location, + gint size, + gpointer user_data) { - gint linelength; - gboolean success; + GstBuffer *buffer; - g_return_val_if_fail(src != NULL, FALSE); - g_return_val_if_fail(GST_IS_V4LSRC(src), FALSE); + buffer = gst_buffer_new(); + if (!buffer) return NULL; + buffer->pool_private = user_data; - GST_DEBUG (0,"v4lsrc: resync %d %d %d\n", src->width, src->height, src->format); + /* TODO: add interlacing info to buffer as metadata (height>288 or 240 = topfieldfirst, else noninterlaced) */ - if (!src->grabber->opened) - return FALSE; - - if (src->grabber->grab_setparams(src->grabber, src->format, &src->width, &src->height, &linelength) != 0) { - fprintf(stderr, "v4lsrc: error setting params\n"); - success = FALSE; - } - else { - GST_DEBUG (0,"v4lsrc: resynced to %d %d %d\n", src->width, src->height, src->buffer_size); - success = TRUE; - } - return success; + return buffer; } -static gboolean -gst_v4lsrc_open_v4l (GstV4lSrc *src) -{ - g_return_val_if_fail(src->grabber != NULL, FALSE); - g_return_val_if_fail(!src->grabber->opened, FALSE); - if (src->grabber->grab_open(src->grabber, src->device) != -1) { - gst_v4lsrc_sync_parms(src); - GST_FLAG_SET(src, GST_V4LSRC_OPEN); - return TRUE; - } - return FALSE; +static GstBuffer* +gst_v4lsrc_buffer_copy (GstBuffer *srcbuf) +{ + 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_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_v4lsrc_close_v4l (GstV4lSrc *src) +gst_v4lsrc_buffer_free (GstBuffer *buf) { - g_return_if_fail(src->grabber != NULL); - g_return_if_fail(src->grabber->opened); + GstV4lSrc *v4lsrc = buf->pool_private; + int n; - src->grabber->grab_close(src->grabber); - GST_FLAG_UNSET(src, GST_V4LSRC_OPEN); + for (n=0;nmbuf.frames;n++) + if (GST_BUFFER_DATA(buf) == gst_v4lsrc_get_buffer(v4lsrc, n)) + { + gst_v4lsrc_requeue_frame(v4lsrc, n); + return; + } + + gst_element_error(GST_ELEMENT(v4lsrc), + "Couldn't find the buffer"); } + static gboolean -plugin_init (GModule *module, GstPlugin *plugin) +plugin_init (GModule *module, + GstPlugin *plugin) { GstElementFactory *factory; - /* create an elementfactory for the v4lsrcparse element */ + /* create an elementfactory for the v4lsrc */ factory = gst_elementfactory_new("v4lsrc",GST_TYPE_V4LSRC, &gst_v4lsrc_details); g_return_val_if_fail(factory != NULL, FALSE); @@ -620,10 +553,10 @@ plugin_init (GModule *module, GstPlugin *plugin) return TRUE; } + GstPluginDesc plugin_desc = { GST_VERSION_MAJOR, GST_VERSION_MINOR, "v4lsrc", plugin_init }; - diff --git a/sys/v4l/gstv4lsrc.h b/sys/v4l/gstv4lsrc.h index 8411e9d375..ab68742d1c 100644 --- a/sys/v4l/gstv4lsrc.h +++ b/sys/v4l/gstv4lsrc.h @@ -1,5 +1,5 @@ -/* Gnome-Streamer - * Copyright (C) <1999> Erik Walthinsen +/* G-Streamer BT8x8/V4L frame grabber plugin + * Copyright (C) 2001 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -17,23 +17,15 @@ * Boston, MA 02111-1307, USA. */ - #ifndef __GST_V4LSRC_H__ #define __GST_V4LSRC_H__ - -#include -#include - -#include - -#include "grab.h" +#include #ifdef __cplusplus extern "C" { #endif /* __cplusplus */ - #define GST_TYPE_V4LSRC \ (gst_v4lsrc_get_type()) #define GST_V4LSRC(obj) \ @@ -45,46 +37,36 @@ extern "C" { #define GST_IS_V4LSRC_CLASS(obj) \ (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4LSRC)) -// NOTE: per-element flags start with 16 for now -typedef enum { - GST_V4LSRC_OPEN = GST_ELEMENT_FLAG_LAST, - - GST_V4LSRC_FLAG_LAST = GST_ELEMENT_FLAG_LAST+2, -} GstV4lSrcFlags; - typedef struct _GstV4lSrc GstV4lSrc; typedef struct _GstV4lSrcClass GstV4lSrcClass; struct _GstV4lSrc { - GstElement element; + GstV4lElement v4lelement; /* pads */ GstPad *srcpad; - /* video device */ - struct GRABBER *grabber; + /* bufferpool for the buffers we're gonna use */ + GstBufferPool *bufferpool; + + /* whether we need to reset the GstPad */ gboolean init; + /* capture/buffer info */ + struct video_mmap mmap; + struct video_mbuf mbuf; + gint sync_frame; + gboolean *frame_queued; + guint buffer_size; + + /* caching values */ gint width; gint height; - guint16 format; - guint32 buffer_size; - gulong tune; - gboolean tuned; - gint input; - gint norm; - gint volume; - gboolean mute; - gint audio_mode; - gint color; - gint bright; - gint hue; - gint contrast; - gchar *device; + gint palette; }; struct _GstV4lSrcClass { - GstElementClass parent_class; + GstV4lElementClass parent_class; }; GType gst_v4lsrc_get_type(void); @@ -93,5 +75,4 @@ GType gst_v4lsrc_get_type(void); } #endif /* __cplusplus */ - #endif /* __GST_V4LSRC_H__ */ diff --git a/sys/v4l/v4l_calls.c b/sys/v4l/v4l_calls.c new file mode 100644 index 0000000000..062cd8fd40 --- /dev/null +++ b/sys/v4l/v4l_calls.c @@ -0,0 +1,573 @@ +/* G-Streamer generic V4L element - generic V4L calls handling + * Copyright (C) 2001 Ronald Bultje + * + * 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. + */ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include "v4l_calls.h" + + +char *picture_name[] = { "Hue", "Brightness", "Contrast", "Saturation" }; + +char *audio_name[] = { "Volume", "Mute", "Mode" }; + +char *norm_name[] = { "PAL", "NTSC", "SECAM", "Autodetect" }; + +/****************************************************** + * gst_v4l_get_capabilities(): + * get the device's capturing capabilities + * return value: TRUE on success, FALSE on error + ******************************************************/ + +static gboolean +gst_v4l_get_capabilities (GstV4lElement *v4lelement) +{ +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_get_capabilities()\n"); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + if (ioctl(v4lelement->video_fd, VIDIOCGCAP, &(v4lelement->vcap)) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting \'%s\' capabilities: %s", + v4lelement->videodev, sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4l_open(): + * open the video device (v4lelement->videodev) + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_open (GstV4lElement *v4lelement) +{ +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_open()\n"); +#endif + + GST_V4L_CHECK_NOT_OPEN(v4lelement); + GST_V4L_CHECK_NOT_ACTIVE(v4lelement); + + /* be sure we have a device */ + if (!v4lelement->videodev) + v4lelement->videodev = g_strdup("/dev/video"); + + /* open the device */ + v4lelement->video_fd = open(v4lelement->videodev, O_RDONLY); + if (!GST_V4L_IS_OPEN(v4lelement)) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Failed to open device (\'%s\'): %s", + v4lelement->videodev, sys_errlist[errno]); + return FALSE; + } + + /* get capabilities */ + if (!gst_v4l_get_capabilities(v4lelement)) + { + close(v4lelement->video_fd); + v4lelement->video_fd = -1; + return FALSE; + } + + gst_element_info(GST_ELEMENT(v4lelement), + "Opened device \'%s\' (\'%s\') successfully", + v4lelement->vcap.name, v4lelement->videodev); + + return TRUE; +} + + +/****************************************************** + * gst_v4l_close(): + * close the video device (v4lelement->video_fd) + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_close (GstV4lElement *v4lelement) +{ +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_close()\n"); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + GST_V4L_CHECK_NOT_ACTIVE(v4lelement); + + close(v4lelement->video_fd); + v4lelement->video_fd = -1; + + return TRUE; +} + + +/****************************************************** + * gst_v4l_get_num_chans() + * return value: the numver of video input channels + ******************************************************/ + +gint +gst_v4l_get_num_chans (GstV4lElement *v4lelement) +{ +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_get_num_chans()\n"); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + return v4lelement->vcap.channels; +} + + +/****************************************************** + * gst_v4l_get_chan_norm(): + * get the currently active video-channel and it's + * norm (VIDEO_MODE_{PAL|NTSC|SECAM|AUTO}) + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_get_chan_norm (GstV4lElement *v4lelement, + gint *channel, + gint *norm) +{ + struct video_channel vchan; + +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_get_chan_norm()\n"); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + if (ioctl(v4lelement->video_fd, VIDIOCGCHAN, &vchan) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting the channel/norm settings: %s", + sys_errlist[errno]); + return FALSE; + } + + if (channel) + *channel = vchan.channel; + if (norm) + *norm = vchan.norm; + + return TRUE; +} + + +/****************************************************** + * gst_v4l_set_chan_norm(): + * set a new active channel and it's norm + * (VIDEO_MODE_{PAL|NTSC|SECAM|AUTO}) + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_set_chan_norm (GstV4lElement *v4lelement, + gint channel, + gint norm) +{ + struct video_channel vchan; + +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_set_chan_norm(), channel = %d, norm = %d (%s)\n", + channel, norm, norm_name[norm]); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + GST_V4L_CHECK_NOT_ACTIVE(v4lelement); + +// if (ioctl(v4lelement->video_fd, VIDIOCGCHAN, &vchan) < 0) +// { +// gst_error("V4lElement - Error getting the channel/norm settings: %s", +// sys_errlist[errno]); +// return FALSE; +// } + + vchan.channel = channel; + vchan.norm = norm; + + if (ioctl(v4lelement->video_fd, VIDIOCSCHAN, &vchan) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting the channel/norm settings: %s", + sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4l_has_tuner(): + * return value: TRUE if it has a tuner, else FALSE + ******************************************************/ + +gboolean +gst_v4l_has_tuner (GstV4lElement *v4lelement) +{ +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_has_tuner()\n"); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + return (v4lelement->vcap.type & VID_TYPE_TUNER); +} + + +/****************************************************** + * gst_v4l_get_frequency(): + * get the current frequency + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_get_frequency (GstV4lElement *v4lelement, + gulong *frequency) +{ +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_get_frequency()\n"); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + if (!gst_v4l_has_tuner(v4lelement)) + return FALSE; + + if (ioctl(v4lelement->video_fd, VIDIOCGFREQ, frequency) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting tuner frequency: %s", + sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4l_set_frequency(): + * set frequency + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_set_frequency (GstV4lElement *v4lelement, + gulong frequency) +{ +#ifdef DEBUG + fprintf(stderr, "gst_v4l_set_frequency(), frequency = %ul\n", + frequency); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + GST_V4L_CHECK_NOT_ACTIVE(v4lelement); + + if (!gst_v4l_has_tuner(v4lelement)) + return FALSE; + + if (ioctl(v4lelement->video_fd, VIDIOCSFREQ, &frequency) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting tuner frequency: %s", + sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4l_get_picture(): + * get a picture value + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_get_picture (GstV4lElement *v4lelement, + GstV4lPictureType type, + gint *value) +{ + struct video_picture vpic; + +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_get_picture(), type = %d (%s)\n", + type, picture_name[type]); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + if (ioctl(v4lelement->video_fd, VIDIOCGPICT, &vpic) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting picture parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + switch (type) + { + case V4L_PICTURE_HUE: + *value = vpic.hue; + break; + case V4L_PICTURE_BRIGHTNESS: + *value = vpic.brightness; + break; + case V4L_PICTURE_CONTRAST: + *value = vpic.contrast; + break; + case V4L_PICTURE_SATURATION: + *value = vpic.colour; + break; + default: + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting picture parameters: unknown type %d", + type); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4l_set_picture(): + * set a picture value + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_set_picture (GstV4lElement *v4lelement, + GstV4lPictureType type, + gint value) +{ + struct video_picture vpic; + +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_set_picture(), type = %d (%s), value = %d\n", + type, picture_name[type], value); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + if (ioctl(v4lelement->video_fd, VIDIOCGPICT, &vpic) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting picture parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + switch (type) + { + case V4L_PICTURE_HUE: + vpic.hue = value; + break; + case V4L_PICTURE_BRIGHTNESS: + vpic.brightness = value; + break; + case V4L_PICTURE_CONTRAST: + vpic.contrast = value; + break; + case V4L_PICTURE_SATURATION: + vpic.colour = value; + break; + default: + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting picture parameters: unknown type %d", + type); + return FALSE; + } + + if (ioctl(v4lelement->video_fd, VIDIOCSPICT, &vpic) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting picture parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4l_has_audio(): + * return value: TRUE if it can do audio, else FALSE + ******************************************************/ + +gboolean +gst_v4l_has_audio (GstV4lElement *v4lelement) +{ +#ifdef DEBUG + fprintf(stderr, "V4L: gst_v4l_has_audio()\n"); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + return (v4lelement->vcap.audios > 0); +} + + +/****************************************************** + * gst_v4l_get_audio(): + * get some audio value + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_get_audio (GstV4lElement *v4lelement, + GstV4lAudioType type, + gint *value) +{ + struct video_audio vau; + +#ifdef DEBUG + fprintf(stderr, "V4L: v4l_gst_get_audio(), type = %d (%s)\n", + type, audio_name[type]); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + if (!gst_v4l_has_audio(v4lelement)) + return FALSE; + + if (ioctl(v4lelement->video_fd, VIDIOCGAUDIO, &vau) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting audio parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + switch (type) + { + case V4L_AUDIO_MUTE: + *value = (vau.flags & VIDEO_AUDIO_MUTE); + break; + case V4L_AUDIO_VOLUME: + *value = vau.volume; + break; + case V4L_AUDIO_MODE: + *value = vau.mode; + break; + default: + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting audio parameters: unknown type %d", + type); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4l_set_audio(): + * set some audio value + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4l_set_audio (GstV4lElement *v4lelement, + GstV4lAudioType type, + gint value) +{ + struct video_audio vau; + +#ifdef DEBUG + fprintf(stderr, "V4L: v4l_gst_set_audio(), type = %d (%s), value = %d\n", + type, audio_name[type], value); +#endif + + GST_V4L_CHECK_OPEN(v4lelement); + + if (!gst_v4l_has_audio(v4lelement)) + return FALSE; + + if (ioctl(v4lelement->video_fd, VIDIOCGAUDIO, &vau) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error getting audio parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + switch (type) + { + case V4L_AUDIO_MUTE: + if (!(vau.flags & VIDEO_AUDIO_MUTABLE)) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting audio mute: (un)setting mute is not supported"); + return FALSE; + } + if (value) + vau.flags |= VIDEO_AUDIO_MUTE; + else + vau.flags &= ~VIDEO_AUDIO_MUTE; + break; + case V4L_AUDIO_VOLUME: + if (!(vau.flags & VIDEO_AUDIO_VOLUME)) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting audio volume: setting volume is not supported"); + return FALSE; + } + vau.volume = value; + break; + case V4L_AUDIO_MODE: + vau.mode = value; + break; + default: + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting audio parameters: unknown type %d", + type); + return FALSE; + } + + if (ioctl(v4lelement->video_fd, VIDIOCSAUDIO, &vau) < 0) + { + gst_element_error(GST_ELEMENT(v4lelement), + "Error setting audio parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} diff --git a/sys/v4l/v4l_calls.h b/sys/v4l/v4l_calls.h new file mode 100644 index 0000000000..01038d6da4 --- /dev/null +++ b/sys/v4l/v4l_calls.h @@ -0,0 +1,123 @@ +/* G-Streamer generic V4L element - generic V4L calls handling + * Copyright (C) 2001 Ronald Bultje + * + * 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 __V4L_CALLS_H__ +#define __V4L_CALLS_H__ + +#include "gstv4lelement.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* simple check whether the device is open */ +#define GST_V4L_IS_OPEN(v4lelement) \ + (v4lelement->video_fd > 0) + +/* check whether the device is 'active' */ +#define GST_V4L_IS_ACTIVE(v4lelement) \ + (v4lelement->buffer != NULL) + +/* checks whether the current v4lelement has already been open()'ed or not */ +#define GST_V4L_CHECK_OPEN(v4lelement) \ + if (v4lelement->video_fd <= 0) \ + { \ + gst_element_error(GST_ELEMENT(v4lelement), \ + "Device is not open"); \ + return FALSE; \ + } + +/* checks whether the current v4lelement is close()'ed or whether it is still open */ +#define GST_V4L_CHECK_NOT_OPEN(v4lelement) \ + if (v4lelement->video_fd != -1) \ + { \ + gst_element_error(GST_ELEMENT(v4lelement), \ + "Device is open"); \ + return FALSE; \ + } + +/* checks whether we're in capture mode or not */ +#define GST_V4L_CHECK_ACTIVE(v4lelement) \ + if (v4lelement->buffer == NULL) \ + { \ + gst_element_error(GST_ELEMENT(v4lelement), \ + "Device is not in streaming mode"); \ + return FALSE; \ + } + +/* checks whether we're out of capture mode or not */ +#define GST_V4L_CHECK_NOT_ACTIVE(v4lelement) \ + if (v4lelement->buffer != NULL) \ + { \ + gst_element_error(GST_ELEMENT(v4lelement), \ + "Device is in streaming mode"); \ + return FALSE; \ + } + + +typedef enum { + V4L_PICTURE_HUE, + V4L_PICTURE_BRIGHTNESS, + V4L_PICTURE_CONTRAST, + V4L_PICTURE_SATURATION, +} GstV4lPictureType; + +extern char *picture_name[]; + +typedef enum { + V4L_AUDIO_VOLUME, + V4L_AUDIO_MUTE, + V4L_AUDIO_MODE, /* stereo, mono, ... (see videodev.h) */ +} GstV4lAudioType; + +extern char *audio_name[]; + +extern char *norm_name[]; + + +/* open/close the device */ +gboolean gst_v4l_open (GstV4lElement *v4lelement); +gboolean gst_v4l_close (GstV4lElement *v4lelement); + +/* norm control (norm = VIDEO_MODE_{PAL|NTSC|SECAM|AUTO}) */ +gint gst_v4l_get_num_chans (GstV4lElement *v4lelement); +gboolean gst_v4l_get_chan_norm (GstV4lElement *v4lelement, gint *channel, gint *norm); +gboolean gst_v4l_set_chan_norm (GstV4lElement *v4lelement, gint channel, gint norm); + +/* frequency control */ +gboolean gst_v4l_has_tuner (GstV4lElement *v4lelement); +gboolean gst_v4l_get_frequency (GstV4lElement *v4lelement, gulong *frequency); +gboolean gst_v4l_set_frequency (GstV4lElement *v4lelement, gulong frequency); + +/* picture control */ +gboolean gst_v4l_get_picture (GstV4lElement *v4lelement, GstV4lPictureType type, gint *value); +gboolean gst_v4l_set_picture (GstV4lElement *v4lelement, GstV4lPictureType type, gint value); + +/* audio control */ +gboolean gst_v4l_has_audio (GstV4lElement *v4lelement); +gboolean gst_v4l_get_audio (GstV4lElement *v4lelement, GstV4lAudioType type, gint *value); +gboolean gst_v4l_set_audio (GstV4lElement *v4lelement, GstV4lAudioType type, gint value); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __V4L_CALLS_H__ */ diff --git a/sys/v4l/v4lmjpegsrc_calls.c b/sys/v4l/v4lmjpegsrc_calls.c new file mode 100644 index 0000000000..345ae8ee48 --- /dev/null +++ b/sys/v4l/v4lmjpegsrc_calls.c @@ -0,0 +1,595 @@ +/* G-Streamer hardware MJPEG video source plugin + * Copyright (C) 2001 Ronald Bultje + * + * 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. + */ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include "v4lmjpegsrc_calls.h" + +/* On some systems MAP_FAILED seems to be missing */ +#ifndef MAP_FAILED +#define MAP_FAILED ( (caddr_t) -1 ) +#endif + +char *input_name[] = { "Composite", "S-Video", "TV-Tuner", "Autodetect" }; + + +/****************************************************** + * gst_v4lmjpegsrc_queue_frame(): + * queue a frame for capturing + * return value: TRUE on success, FALSE on error + ******************************************************/ + +static gboolean +gst_v4lmjpegsrc_queue_frame (GstV4lMjpegSrc *v4lmjpegsrc, + gint num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_queue_frame(), num = %d\n", + num); +#endif + + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_QBUF_CAPT, &num) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error queueing a buffer (%d): %s", + num, sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_sync_next_frame(): + * sync on the next frame for capturing + * return value: TRUE on success, FALSE on error + ******************************************************/ + +static gboolean +gst_v4lmjpegsrc_sync_next_frame (GstV4lMjpegSrc *v4lmjpegsrc, + gint *num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_sync_frame(), num = %d\n", + num); +#endif + + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_SYNC, &(v4lmjpegsrc->bsync)) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error syncing on a buffer (%ld): %s", + v4lmjpegsrc->bsync.frame, sys_errlist[errno]); + return FALSE; + } + + *num = v4lmjpegsrc->bsync.frame; + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_set_input_norm(): + * set input/norm (includes autodetection), norm is + * VIDEO_MODE_{PAL|NTSC|SECAM|AUTO} + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_set_input_norm (GstV4lMjpegSrc *v4lmjpegsrc, + GstV4lMjpegInputType input, + gint norm) +{ + struct mjpeg_status bstat; + +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_set_input_norm(), input = %d (%s), norm = %d (%s)\n", + input, input_name[input], norm, norm_name[norm]); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + if (input == V4L_MJPEG_INPUT_AUTO) + { + int n; + + for (n=V4L_MJPEG_INPUT_COMPOSITE;nvideo_fd, MJPIOC_G_STATUS, &bstat) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error getting device status: %s", + sys_errlist[errno]); + return FALSE; + } + + if (bstat.signal) + { + input = bstat.input; + if (norm == VIDEO_MODE_AUTO) + norm = bstat.norm; + gst_element_info(GST_ELEMENT(v4lmjpegsrc), + "Signal found: on input %s, norm %s", + input_name[bstat.input], norm_name[bstat.norm]); + break; + } + } + } + else if (norm == VIDEO_MODE_AUTO && input) + { + bstat.input = input; + + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_G_STATUS, &bstat) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error getting device status: %s", + sys_errlist[errno]); + return FALSE; + } + + if (bstat.signal) + { + norm = bstat.norm; + gst_element_info(GST_ELEMENT(v4lmjpegsrc), + "Norm %s detected on input %s", + norm_name[bstat.norm], input_name[input]); + } + else + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "No signal found on input %s", + input_name[input]); + return FALSE; + } + } + + return gst_v4l_set_chan_norm(GST_V4LELEMENT(v4lmjpegsrc), input, norm); +} + + +/****************************************************** + * gst_v4lmjpegsrc_set_buffer(): + * set buffer parameters (size/count) + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_set_buffer (GstV4lMjpegSrc *v4lmjpegsrc, + gint numbufs, + gint bufsize) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_set_buffer(), numbufs = %d, bufsize = %d\n", + numbufs, bufsize); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + v4lmjpegsrc->breq.size = bufsize; + v4lmjpegsrc->breq.count = numbufs; + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_set_capture(): + * set capture parameters (simple) + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_set_capture (GstV4lMjpegSrc *v4lmjpegsrc, + gint decimation, + gint quality) +{ + int norm, input, mw; + struct mjpeg_params bparm; + +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_set_capture(), decimation = %d, quality = %d\n", + decimation, quality); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + gst_v4l_get_chan_norm(GST_V4LELEMENT(v4lmjpegsrc), &input, &norm); + + /* Query params for capture */ + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_G_PARAMS, &bparm) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error getting video parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + bparm.decimation = decimation; + bparm.quality = quality; + bparm.norm = norm; + bparm.input = input; + bparm.APP_len = 0; /* no JPEG markers - TODO: this is definately not right for decimation==1 */ + + mw = GST_V4LELEMENT(v4lmjpegsrc)->vcap.maxwidth; + if (mw != 768 && mw != 640) + { + if (decimation == 1) + mw = 720; + else + mw = 704; + } + v4lmjpegsrc->end_width = mw / decimation; + v4lmjpegsrc->end_height = (norm==VIDEO_MODE_NTSC?480:576) / decimation; + + /* TODO: interlacing */ + + /* Set params for capture */ + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_S_PARAMS, &bparm) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error setting video parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_set_capture_m(): + * set capture parameters (advanced) + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean gst_v4lmjpegsrc_set_capture_m (GstV4lMjpegSrc *v4lmjpegsrc, + gint x_offset, + gint y_offset, + gint width, + gint height, + gint h_decimation, + gint v_decimation, + gint quality) +{ + gint norm, input; + gint maxwidth; + struct mjpeg_params bparm; + +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_set_capture_m(), x_offset = %d, y_offset = %d, " + "width = %d, height = %d, h_decimation = %d, v_decimation = %d, quality = %d\n", + x_offset, y_offset, width, height, h_decimation, v_decimation, quality); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + gst_v4l_get_chan_norm(GST_V4LELEMENT(v4lmjpegsrc), &input, &norm); + + if (GST_V4LELEMENT(v4lmjpegsrc)->vcap.maxwidth != 768 && + GST_V4LELEMENT(v4lmjpegsrc)->vcap.maxwidth != 640) + maxwidth = 720; + else + maxwidth = GST_V4LELEMENT(v4lmjpegsrc)->vcap.maxwidth; + + /* Query params for capture */ + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_G_PARAMS, &bparm) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error getting video parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + bparm.decimation = 0; + bparm.quality = quality; + bparm.norm = norm; + bparm.input = input; + bparm.APP_len = 0; /* no JPEG markers - TODO: this is definately not right for decimation==1 */ + + if (width <= 0) + { + if (x_offset < 0) x_offset = 0; + width = (maxwidth==720&&h_decimation!=1)?704:maxwidth - 2*x_offset; + } + else + { + if (x_offset < 0) + x_offset = (maxwidth - width)/2; + } + + if (height <= 0) + { + if (y_offset < 0) y_offset = 0; + height = (norm==VIDEO_MODE_NTSC)?480:576 - 2*y_offset; + } + else + { + if (y_offset < 0) + y_offset = ((norm==VIDEO_MODE_NTSC)?480:576 - height)/2; + } + + if (width + x_offset > maxwidth) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Image width+offset (%d) bigger than maximum (%d)", + width + x_offset, maxwidth); + return FALSE; + } + if ((width%(bparm.HorDcm*16))!=0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Image width (%d) not multiple of %d (required for JPEG)", + width, bparm.HorDcm*16); + return FALSE; + } + if (height + y_offset > (norm==VIDEO_MODE_NTSC ? 480 : 576)) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Image height+offset (%d) bigger than maximum (%d)", + height + y_offset, (norm==VIDEO_MODE_NTSC ? 480 : 576)); + return FALSE; + } + /* RJ: Image height must only be a multiple of 8, but geom_height + * is double the field height + */ + if ((height%(bparm.VerDcm*16))!=0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Image height (%d) not multiple of %d (required for JPEG)", + height, bparm.VerDcm*16); + return FALSE; + } + + bparm.img_x = x_offset; + bparm.img_width = width; + bparm.img_y = y_offset; + bparm.img_height = height; + bparm.HorDcm = h_decimation; + bparm.VerDcm = (v_decimation==4) ? 2 : 1; + bparm.TmpDcm = (v_decimation==1) ? 1 : 2; + bparm.field_per_buff = (v_decimation==1) ? 2 : 1; + + v4lmjpegsrc->end_width = width / h_decimation; + v4lmjpegsrc->end_width = height / v_decimation; + + /* TODO: interlacing */ + + /* Set params for capture */ + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_S_PARAMS, &bparm) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error setting video parameters: %s", + sys_errlist[errno]); + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_capture_init(): + * initialize the capture system + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_capture_init (GstV4lMjpegSrc *v4lmjpegsrc) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_capture_init()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + /* Request buffers */ + if (ioctl(GST_V4LELEMENT(v4lmjpegsrc)->video_fd, MJPIOC_REQBUFS, &(v4lmjpegsrc->breq)) < 0) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error requesting video buffers: %s", + sys_errlist[errno]); + return FALSE; + } + + gst_element_info(GST_ELEMENT(v4lmjpegsrc), + "Got %ld buffers of size %ld KB", + v4lmjpegsrc->breq.count, v4lmjpegsrc->breq.size/1024); + + /* Map the buffers */ + GST_V4LELEMENT(v4lmjpegsrc)->buffer = mmap(0, v4lmjpegsrc->breq.count * v4lmjpegsrc->breq.size, + PROT_READ, MAP_SHARED, GST_V4LELEMENT(v4lmjpegsrc)->video_fd, 0); + if (GST_V4LELEMENT(v4lmjpegsrc)->buffer == MAP_FAILED) + { + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Error mapping video buffers: %s", + sys_errlist[errno]); + GST_V4LELEMENT(v4lmjpegsrc)->buffer = NULL; + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_capture_start(): + * start streaming capture + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_capture_start (GstV4lMjpegSrc *v4lmjpegsrc) +{ + int n; + +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_capture_start()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + /* queue'ing the buffers starts streaming capture */ + for (n=0;nbreq.count;n++) + if (!gst_v4lmjpegsrc_queue_frame(v4lmjpegsrc, n)) + return FALSE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_grab_frame(): + * grab one frame during streaming capture + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_grab_frame (GstV4lMjpegSrc *v4lmjpegsrc, + gint *num, + gint *size) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_grab_frame()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + /* syncing on the buffer grabs it */ + if (!gst_v4lmjpegsrc_sync_next_frame(v4lmjpegsrc, num)) + return FALSE; + + *size = v4lmjpegsrc->bsync.length; + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_get_buffer(): + * get the memory address of a single buffer + * return value: TRUE on success, FALSE on error + ******************************************************/ + +guint8 * +gst_v4lmjpegsrc_get_buffer (GstV4lMjpegSrc *v4lmjpegsrc, + gint num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_get_buffer(), num = %d\n", + num); +#endif + + if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc))) + return NULL; + + return GST_V4LELEMENT(v4lmjpegsrc)->buffer+(v4lmjpegsrc->breq.size*num); +} + + +/****************************************************** + * gst_v4lmjpegsrc_requeue_frame(): + * requeue a frame for capturing + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_requeue_frame (GstV4lMjpegSrc *v4lmjpegsrc, + gint num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_requeue_frame(), num = %d\n", + num); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + if (!gst_v4lmjpegsrc_queue_frame(v4lmjpegsrc, num)) + return FALSE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_capture_stop(): + * stop streaming capture + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_capture_stop (GstV4lMjpegSrc *v4lmjpegsrc) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_capture_stop()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + /* unqueue the buffers */ + if (!gst_v4lmjpegsrc_queue_frame(v4lmjpegsrc, -1)) + return FALSE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lmjpegsrc_capture_deinit(): + * deinitialize the capture system + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lmjpegsrc_capture_deinit (GstV4lMjpegSrc *v4lmjpegsrc) +{ +#ifdef DEBUG + fprintf(stderr, "V4LMJPEGSRC: gst_v4lmjpegsrc_capture_deinit()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lmjpegsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc)); + + /* unmap the buffer */ + munmap(GST_V4LELEMENT(v4lmjpegsrc)->buffer, v4lmjpegsrc->breq.size * v4lmjpegsrc->breq.count); + GST_V4LELEMENT(v4lmjpegsrc)->buffer = NULL; + + return TRUE; +} diff --git a/sys/v4l/v4lmjpegsrc_calls.h b/sys/v4l/v4lmjpegsrc_calls.h new file mode 100644 index 0000000000..f8572e5132 --- /dev/null +++ b/sys/v4l/v4lmjpegsrc_calls.h @@ -0,0 +1,63 @@ +/* G-Streamer hardware MJPEG video source plugin + * Copyright (C) 2001 Ronald Bultje + * + * 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 __V4L_MJPEG_SRC_CALLS_H__ +#define __V4L_MJPEG_SRC_CALLS_H__ + +#include "gstv4lmjpegsrc.h" +#include "v4l_calls.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +typedef enum { + V4L_MJPEG_INPUT_COMPOSITE = 0, + V4L_MJPEG_INPUT_SVIDEO = 1, + V4L_MJPEG_INPUT_TVTUNER = 2, + V4L_MJPEG_INPUT_AUTO = 3, +} GstV4lMjpegInputType; + +extern char *input_name[]; + + +/* set input/norm (includes autodetection, norm = VIDEO_MODE_{PAL|NTSC|SECAM|AUTO}) */ +gboolean gst_v4lmjpegsrc_set_input_norm (GstV4lMjpegSrc *v4lmjpegsrc, GstV4lMjpegInputType input, gint norm); + +/* frame grabbing/capture */ +gboolean gst_v4lmjpegsrc_set_buffer (GstV4lMjpegSrc *v4lmjpegsrc, gint numbufs, gint bufsize); +gboolean gst_v4lmjpegsrc_set_capture (GstV4lMjpegSrc *v4lmjpegsrc, gint decimation, gint quality); +gboolean gst_v4lmjpegsrc_set_capture_m (GstV4lMjpegSrc *v4lmjpegsrc, + gint x_offset, gint y_offset, gint width, gint height, + gint h_decimation, gint v_decimation, gint quality); +gboolean gst_v4lmjpegsrc_capture_init (GstV4lMjpegSrc *v4lmjpegsrc); +gboolean gst_v4lmjpegsrc_capture_start (GstV4lMjpegSrc *v4lmjpegsrc); +gboolean gst_v4lmjpegsrc_grab_frame (GstV4lMjpegSrc *v4lmjpegsrc, gint *num, gint *size); +guint8 * gst_v4lmjpegsrc_get_buffer (GstV4lMjpegSrc *v4lmjpegsrc, gint num); +gboolean gst_v4lmjpegsrc_requeue_frame (GstV4lMjpegSrc *v4lmjpegsrc, gint num); +gboolean gst_v4lmjpegsrc_capture_stop (GstV4lMjpegSrc *v4lmjpegsrc); +gboolean gst_v4lmjpegsrc_capture_deinit (GstV4lMjpegSrc *v4lmjpegsrc); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __V4L_MJPEG_SRC_CALLS_H__ */ diff --git a/sys/v4l/v4lsrc_calls.c b/sys/v4l/v4lsrc_calls.c new file mode 100644 index 0000000000..e0e53211c7 --- /dev/null +++ b/sys/v4l/v4lsrc_calls.c @@ -0,0 +1,338 @@ +/* G-Streamer BT8x8/V4L frame grabber plugin + * Copyright (C) 2001 Ronald Bultje + * + * 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. + */ + +//#define DEBUG + +#include +#include +#include +#include +#include +#include +#include +#include +#include "v4lsrc_calls.h" + +/* On some systems MAP_FAILED seems to be missing */ +#ifndef MAP_FAILED +#define MAP_FAILED ( (caddr_t) -1 ) +#endif + + +/****************************************************** + * gst_v4lsrc_queue_frame(): + * queue a frame for capturing + * return value: TRUE on success, FALSE on error + ******************************************************/ + +static gboolean +gst_v4lsrc_queue_frame (GstV4lSrc *v4lsrc, + gint num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_queue_frame(), num = %d\n", + num); +#endif + + v4lsrc->mmap.frame = num; + + if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCMCAPTURE, &(v4lsrc->mmap)) < 0) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error queueing a buffer (%d): %s", + num, sys_errlist[errno]); + return FALSE; + } + + v4lsrc->frame_queued[num] = TRUE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_sync_frame(): + * sync on a frame for capturing + * return value: TRUE on success, FALSE on error + ******************************************************/ + +static gboolean +gst_v4lsrc_sync_next_frame (GstV4lSrc *v4lsrc, + gint *num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_sync_frame(), num = %d\n", + num); +#endif + + *num = (v4lsrc->sync_frame + 1)%v4lsrc->mbuf.frames; + + if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCSYNC, num) < 0) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error syncing on a buffer (%d): %s", + *num, sys_errlist[errno]); + return FALSE; + } + + v4lsrc->frame_queued[*num] = FALSE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_set_capture(): + * set capture parameters, palette = VIDEO_PALETTE_* + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_set_capture (GstV4lSrc *v4lsrc, + gint width, + gint height, + gint palette) +{ +#ifdef DBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_set_capture(), width = %d, height = %d, palette = %d\n", + width, height, palette); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + v4lsrc->mmap.width = width; + v4lsrc->mmap.height = height; + v4lsrc->mmap.format = palette; + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_capture_init(): + * initialize the capture system + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_capture_init (GstV4lSrc *v4lsrc) +{ + int n; + +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_capture_init()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + /* request buffer info */ + if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCGMBUF, &(v4lsrc->mbuf)) < 0) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error getting buffer information: %s", + sys_errlist[errno]); + return FALSE; + } + + gst_element_info(GST_ELEMENT(v4lsrc), + "Got %d buffers of size %d KB", + v4lsrc->mbuf.frames, v4lsrc->mbuf.size/(v4lsrc->mbuf.frames*1024)); + + /* keep trakc of queued buffers */ + v4lsrc->frame_queued = (gint *) malloc(sizeof(gint) * v4lsrc->mbuf.frames); + if (!v4lsrc->frame_queued) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error creating buffer tracker: %s", + sys_errlist[errno]); + return FALSE; + } + for (n=0;nmbuf.frames;n++) + v4lsrc->frame_queued[n] = FALSE; + + /* Map the buffers */ + GST_V4LELEMENT(v4lsrc)->buffer = mmap(0, v4lsrc->mbuf.size, + PROT_READ, MAP_SHARED, GST_V4LELEMENT(v4lsrc)->video_fd, 0); + if (GST_V4LELEMENT(v4lsrc)->buffer == MAP_FAILED) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error mapping video buffers: %s", + sys_errlist[errno]); + GST_V4LELEMENT(v4lsrc)->buffer = NULL; + return FALSE; + } + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_capture_start(): + * start streaming capture + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_capture_start (GstV4lSrc *v4lsrc) +{ + int n; + +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_capture_start()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + /* queue all buffers, this starts streaming capture */ + for (n=0;nmbuf.frames;n++) + if (!gst_v4lsrc_queue_frame(v4lsrc, n)) + return FALSE; + + v4lsrc->sync_frame = -1; + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_grab_frame(): + * capture one frame during streaming capture + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_grab_frame (GstV4lSrc *v4lsrc, gint *num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_grab_frame()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + /* syncing on the buffer grabs it */ + if (!gst_v4lsrc_sync_next_frame(v4lsrc, num)) + return FALSE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_get_buffer(): + * get the address of the just-capture buffer + * return value: TRUE on success, FALSE on error + ******************************************************/ + +guint8 * +gst_v4lsrc_get_buffer (GstV4lSrc *v4lsrc, gint num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_get_buffer(), num = %d\n", + num); +#endif + + if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lsrc))) + return NULL; + + return GST_V4LELEMENT(v4lsrc)->buffer+v4lsrc->mbuf.offsets[num]; +} + + +/****************************************************** + * gst_v4lsrc_requeue_frame(): + * re-queue a frame after we're done with the buffer + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_requeue_frame (GstV4lSrc *v4lsrc, gint num) +{ +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_requeue_buffer(), num = %d\n", + num); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + /* and let's queue the buffer */ + if (!gst_v4lsrc_queue_frame(v4lsrc, num)) + return FALSE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_capture_stop(): + * stop streaming capture + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_capture_stop (GstV4lSrc *v4lsrc) +{ + int n, num; + +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_capture_stop()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + /* we actually need to sync on all queued buffers but not on the non-queued ones */ + for (n=0;nmbuf.frames;n++) + while (v4lsrc->frame_queued[n]) + if (!gst_v4lsrc_sync_next_frame(v4lsrc, &num)) + return FALSE; + + return TRUE; +} + + +/****************************************************** + * gst_v4lsrc_capture_deinit(): + * deinitialize the capture system + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_capture_deinit (GstV4lSrc *v4lsrc) +{ +#ifdef DEBUG + fprintf(stderr, "V4LSRC: gst_v4lsrc_capture_deinit()\n"); +#endif + + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + /* free buffer tracker */ + free(v4lsrc->frame_queued); + + /* unmap the buffer */ + munmap(GST_V4LELEMENT(v4lsrc)->buffer, v4lsrc->mbuf.size); + GST_V4LELEMENT(v4lsrc)->buffer = NULL; + + return TRUE; +} diff --git a/sys/v4l/v4lsrc_calls.h b/sys/v4l/v4lsrc_calls.h new file mode 100644 index 0000000000..1a2a11c261 --- /dev/null +++ b/sys/v4l/v4lsrc_calls.h @@ -0,0 +1,46 @@ +/* G-Streamer BT8x8/V4L frame grabber plugin + * Copyright (C) 2001 Ronald Bultje + * + * 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 __V4L_SRC_CALLS_H__ +#define __V4L_SRC_CALLS_H__ + +#include "gstv4lsrc.h" +#include "v4l_calls.h" + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + + +/* frame grabbing/capture (palette = VIDEO_PALETTE_* - see videodev.h) */ +gboolean gst_v4lsrc_set_capture (GstV4lSrc *v4lsrc, gint width, gint height, gint palette); +gboolean gst_v4lsrc_capture_init (GstV4lSrc *v4lsrc); +gboolean gst_v4lsrc_capture_start (GstV4lSrc *v4lsrc); +gboolean gst_v4lsrc_grab_frame (GstV4lSrc *v4lsrc, gint *num); +guint8 * gst_v4lsrc_get_buffer (GstV4lSrc *v4lsrc, gint num); +gboolean gst_v4lsrc_requeue_frame (GstV4lSrc *v4lsrc, gint num); +gboolean gst_v4lsrc_capture_stop (GstV4lSrc *v4lsrc); +gboolean gst_v4lsrc_capture_deinit (GstV4lSrc *v4lsrc); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* __V4L_SRC_CALLS_H__ */ diff --git a/sys/v4l/videodev_mjpeg.h b/sys/v4l/videodev_mjpeg.h new file mode 100644 index 0000000000..68fd79c576 --- /dev/null +++ b/sys/v4l/videodev_mjpeg.h @@ -0,0 +1,118 @@ +/* These are the MJPEG API extensions for the Video4Linux API, + first introduced by the Iomega Buz driver by Rainer Johanni + +*/ + +/* This is identical with the mgavideo internal params struct, + please tell me if you change this struct here ! top-field-first */ + + int APPn; /* Number of APP segment to be written, must be 0..15 */ + int APP_len; /* Length of data in JPEG APPn segment */ + char APP_data[60]; /* Data in the JPEG APPn segment. */ + + int COM_len; /* Length of data in JPEG COM segment */ + char COM_data[60]; /* Data in JPEG COM segment */ + + unsigned long jpeg_markers; /* Which markers should go into the JPEG output. + Unless you exactly know what you do, leave them untouched. + Inluding less markers will make the resulting code + smaller, but there will be fewer aplications + which can read it. + The presence of the APP and COM marker is + influenced by APP0_len and COM_len ONLY! */ +#define JPEG_MARKER_DHT (1<<3) /* Define Huffman Tables */ +#define JPEG_MARKER_DQT (1<<4) /* Define Quantization Tables */ +#define JPEG_MARKER_DRI (1<<5) /* Define Restart Interval */ +#define JPEG_MARKER_COM (1<<6) /* Comment segment */ +#define JPEG_MARKER_APP (1<<7) /* App segment, driver will allways use APP0 */ + + int VFIFO_FB; /* Flag for enabling Video Fifo Feedback. + If this flag is turned on and JPEG decompressing + is going to the screen, the decompress process + is stopped every time the Video Fifo is full. + This enables a smooth decompress to the screen + but the video output signal will get scrambled */ + + /* Misc */ + + char reserved[312]; /* Makes 512 bytes for this structure */ +}; + +struct mjpeg_requestbuffers +{ + unsigned long count; /* Number of buffers for MJPEG grabbing */ + unsigned long size; /* Size PER BUFFER in bytes */ +}; + +struct mjpeg_sync +{ + unsigned long frame; /* Frame (0 - n) for double buffer */ + unsigned long length; /* number of code bytes in buffer (capture only) */ + unsigned long seq; /* frame sequence number */ + struct timeval timestamp; /* timestamp */ +}; + +struct mjpeg_status +{ + int input; /* Input channel, has to be set prior to BUZIOC_G_STATUS */ + int signal; /* Returned: 1 if valid video signal detected */ + int norm; /* Returned: VIDEO_MODE_PAL or VIDEO_MODE_NTSC */ + int color; /* Returned: 1 if color signal detected */ +}; + +/* +Private IOCTL to set up for displaying MJPEG +*/ +#define MJPIOC_G_PARAMS _IOR ('v', BASE_VIDIOCPRIVATE+0, struct mjpeg_params) +#define MJPIOC_S_PARAMS _IOWR('v', BASE_VIDIOCPRIVATE+1, struct mjpeg_params) +#define MJPIOC_REQBUFS _IOWR('v', BASE_VIDIOCPRIVATE+2, struct mjpeg_requestbuffers) +#define MJPIOC_QBUF_CAPT _IOW ('v', BASE_VIDIOCPRIVATE+3, int) +#define MJPIOC_QBUF_PLAY _IOW ('v', BASE_VIDIOCPRIVATE+4, int) +#define MJPIOC_SYNC _IOR ('v', BASE_VIDIOCPRIVATE+5, struct mjpeg_sync) +#define MJPIOC_G_STATUS _IOWR('v', BASE_VIDIOCPRIVATE+6, struct mjpeg_status)