diff --git a/ChangeLog b/ChangeLog index cafc9409c8..a982d0f621 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,24 @@ +2006-03-11 Edgard Lima + + * configure.ac: + * sys/Makefile.am: + * sys/v4l2/Makefile.am: + * sys/v4l2/gstv4l2.c: + * sys/v4l2/gstv4l2colorbalance.h: + * sys/v4l2/gstv4l2element.c: + * sys/v4l2/gstv4l2element.h: + * sys/v4l2/gstv4l2src.c: + * sys/v4l2/gstv4l2src.h: + * sys/v4l2/gstv4l2tuner.c: + * sys/v4l2/gstv4l2tuner.h: + * sys/v4l2/gstv4l2xoverlay.c: + * sys/v4l2/gstv4l2xoverlay.h: + * sys/v4l2/v4l2_calls.c: + * sys/v4l2/v4l2_calls.h: + * sys/v4l2/v4l2src_calls.c: + * sys/v4l2/v4l2src_calls.h: + V4L2 ported to 0.10. + 2006-03-11 Tim-Philipp Müller * configure.ac: diff --git a/configure.ac b/configure.ac index e17b1292db..6d6f1a386f 100644 --- a/configure.ac +++ b/configure.ac @@ -641,6 +641,97 @@ GST_CHECK_FEATURE(TAGLIB, [taglib ID3v2 tag writer], taglib, [ fi ]) +dnl *** XVideo *** +dnl Look for the PIC library first, Debian requires it. +dnl Check debian-devel archives for gory details. +dnl 20020110: +dnl At the moment XFree86 doesn't distribute shared libXv due +dnl to unstable API. On many platforms you CAN NOT link a shared +dnl lib to a static non-PIC lib. This is what the xvideo GStreamer +dnl plug-in wants to do. So Debian distributes a PIC compiled +dnl version of the static lib for plug-ins to link to when it is +dnl inappropriate to link the main application to libXv directly. +dnl FIXME: add check if this platform can support linking to a +dnl non-PIC libXv, if not then don not use Xv. +dnl FIXME: perhaps warn user if they have a shared libXv since +dnl this is an error until XFree86 starts shipping one + +dnl Check for Xv extension +translit(dnm, m, l) AM_CONDITIONAL(USE_XVIDEO, true) +GST_CHECK_FEATURE(XVIDEO, [X11 XVideo extensions], + [xvimagesink], [ + if test x$HAVE_X = xyes; then + AC_CHECK_LIB(Xv_pic, XvQueryExtension, + HAVE_XVIDEO="yes", HAVE_XVIDEO="no", + $X_LIBS -lXext) + + if test x$HAVE_XVIDEO = xyes; then + XVIDEO_LIBS="-lXv_pic -lXext" + AC_SUBST(XVIDEO_LIBS) + else + dnl try again using something else if we didn't find it first + if test x$HAVE_XVIDEO = xno; then + AC_CHECK_LIB(Xv, XvQueryExtension, + HAVE_XVIDEO="yes", HAVE_XVIDEO="no", + $X_LIBS -lXext) + + if test x$HAVE_XVIDEO = xyes; then + XVIDEO_LIBS="-lXv -lXext" + AC_SUBST(XVIDEO_LIBS) + fi + fi + fi + fi +]) + +dnl *** Video 4 Linux 2 *** +dnl for information about the header/define, see sys/v4l2/gstv4l2element.h +dnl renamed to GST_V4L2 because of some conflict with kernel headers +translit(dnm, m, l) AM_CONDITIONAL(USE_GST_V4L2, true) +GST_CHECK_FEATURE(GST_V4L2, [Video 4 Linux 2], v4l2src, [ + HAVE_GST_V4L2="no" + if test "$HAVE_X" = "yes" + then + AC_MSG_CHECKING([Checking for uptodate v4l2 installation]) + AC_TRY_COMPILE([ +#include +#include +#define _LINUX_TIME_H +#define __user +#include +#if defined(V4L2_MAJOR_VERSION) || defined(V4L2_MINOR_VERSION) +#error too early v4l2 version or no v4l2 at all +#endif + ], [ +return 0; + ], [ HAVE_GST_V4L2="yes" && AC_MSG_RESULT(yes)], + [ HAVE_GST_V4L2="no" && AC_MSG_RESULT(no) && + AC_CHECK_HEADER(linux/videodev2.h, + [ AC_MSG_WARN([video4linux2 headers were found, but they're old. Please update v4l2 to compile the v4l2 plugins])], + [ AC_MSG_WARN([video4linux2 was not found])]) + ]) + fi + dnl check for missing v4l2_buffer declaration (see #135919) + if [ test x$HAVE_GST_V4L2 = xyes ]; then + MISSING_DECL=0 + AC_MSG_CHECKING(struct v4l2_buffer declaration) + AC_TRY_COMPILE([ +#include +#include +#define _LINUX_TIME_H +#define __user +#include + ],[ +struct v4l2_buffer buf; +buf.index = 0; +return 0; + ], [ AC_MSG_RESULT(yes) ], [ MISSING_DECL=1 && AC_MSG_RESULT(no) ]) + if [ test x$MISSING_DECL = x1 ]; then + AC_DEFINE(GST_V4L2_MISSING_BUFDECL, 1, [struct v4l2_buffer missing]) + fi + fi +]) + dnl also add builddir include for enumtypes and marshal GST_CFLAGS="-I\$(top_srcdir)/gst-libs -I\$(top_builddir)/gst-libs $GST_CFLAGS $GST_ERROR" @@ -716,6 +807,7 @@ gst/xingheader/Makefile sys/Makefile sys/glsink/Makefile sys/ximagesrc/Makefile +sys/v4l2/Makefile examples/Makefile examples/directfb/Makefile ext/Makefile diff --git a/sys/Makefile.am b/sys/Makefile.am index ad8614a78d..4a26496575 100644 --- a/sys/Makefile.am +++ b/sys/Makefile.am @@ -10,11 +10,11 @@ # QCAM_DIR= # endif -# if USE_GST_V4L2 -# V4L2_DIR=v4l2 -# else -# V4L2_DIR= -# endif +if USE_GST_V4L2 +V4L2_DIR=v4l2 +else +V4L2_DIR= +endif # if USE_VCD # VCD_DIR=vcd @@ -40,6 +40,6 @@ else XIMAGESRC_DIR= endif -SUBDIRS = $(GL_DIR) $(XIMAGESRC_DIR) +SUBDIRS = $(GL_DIR) $(XIMAGESRC_DIR) $(V4L2_DIR) -DIST_SUBDIRS = glsink ximagesrc +DIST_SUBDIRS = glsink ximagesrc v4l2 diff --git a/sys/v4l2/Makefile.am b/sys/v4l2/Makefile.am index 89e6b853c1..223e807bf3 100644 --- a/sys/v4l2/Makefile.am +++ b/sys/v4l2/Makefile.am @@ -1,5 +1,4 @@ -plugin_LTLIBRARIES = \ - libgstvideo4linux2.la +plugin_LTLIBRARIES = libgstvideo4linux2.la if USE_XVIDEO xv_source = gstv4l2xoverlay.c @@ -10,18 +9,19 @@ xv_libs = endif libgstvideo4linux2_la_SOURCES = \ - gstv4l2element.c v4l2_calls.c \ - gstv4l2src.c v4l2src_calls.c \ gstv4l2.c \ + gstv4l2colorbalance.c \ + gstv4l2element.c \ + gstv4l2src.c \ gstv4l2tuner.c \ - $(xv_source) \ - gstv4l2colorbalance.c -libgstvideo4linux2_la_CFLAGS = $(GST_CFLAGS) $(X_CFLAGS) + v4l2_calls.c \ + v4l2src_calls.c $(xv_source) + +libgstvideo4linux2_la_CFLAGS = $(GST_CFLAGS) $(X_CFLAGS) $(GST_BASE_LIBS_CFLAGS) $(GST_PLUGINS_BASE_CFLAGS) +libgstvideo4linux2_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) libgstvideo4linux2_la_LIBADD = \ - $(top_builddir)/gst-libs/gst/libgstinterfaces-@GST_MAJORMINOR@.la -libgstvideo4linux2_la_LDFLAGS = \ - $(GST_PLUGIN_LDFLAGS) \ - $(xv_libs) + -lgstinterfaces-$(GST_MAJORMINOR) \ + $(GST_BASE_LIBS) $(GST_LIBS) $(xv_libs) noinst_HEADERS = gstv4l2element.h v4l2_calls.h \ gstv4l2src.h v4l2src_calls.h \ diff --git a/sys/v4l2/gstv4l2.c b/sys/v4l2/gstv4l2.c index b81080b217..23da8da197 100644 --- a/sys/v4l2/gstv4l2.c +++ b/sys/v4l2/gstv4l2.c @@ -1,5 +1,8 @@ -/* G-Streamer Video4linux2 video-capture plugin - * Copyright (C) 2002 Ronald Bultje +/* GStreamer + * + * gstv4l2.c: plugin for v4l2 elements + * + * Copyright (C) 2001-2002 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -21,20 +24,31 @@ #include "config.h" #endif -#include - #include "gst/gst-i18n-plugin.h" +#include + #include "gstv4l2element.h" #include "gstv4l2src.h" +/* #include "gstv4l2jpegsrc.h" */ +/* #include "gstv4l2mjpegsrc.h" */ +/* #include "gstv4l2mjpegsink.h" */ + +GST_DEBUG_CATEGORY (v4l2_debug); /* used in v4l2_calls.c and v4l2src_calls.c */ static gboolean plugin_init (GstPlugin * plugin) { - if (!gst_element_register (plugin, "v4l2element", - GST_RANK_NONE, GST_TYPE_V4L2ELEMENT) || - !gst_element_register (plugin, "v4l2src", - GST_RANK_NONE, GST_TYPE_V4L2SRC)) + GST_DEBUG_CATEGORY_INIT (v4l2_debug, "v4l2", 0, "V4L2 API calls"); + + if (!gst_element_register (plugin, "v4l2src", GST_RANK_NONE, + GST_TYPE_V4L2SRC)) + /* !gst_element_register (plugin, "v4l2jpegsrc", */ + /* GST_RANK_NONE, GST_TYPE_V4L2JPEGSRC) || */ + /* !gst_element_register (plugin, "v4l2mjpegsrc", */ + /* GST_RANK_NONE, GST_TYPE_V4L2MJPEGSRC) || */ + /* !gst_element_register (plugin, "v4l2mjpegsink", */ + /* GST_RANK_NONE, GST_TYPE_V4L2MJPEGSINK)) */ return FALSE; #ifdef ENABLE_NLS @@ -48,5 +62,5 @@ plugin_init (GstPlugin * plugin) GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR, "video4linux2", - "elements for Video 4 Linux 2", + "elements for Video 4 Linux", plugin_init, VERSION, GST_LICENSE, GST_PACKAGE, GST_ORIGIN) diff --git a/sys/v4l2/gstv4l2colorbalance.h b/sys/v4l2/gstv4l2colorbalance.h index b573cb2a38..487b348236 100644 --- a/sys/v4l2/gstv4l2colorbalance.h +++ b/sys/v4l2/gstv4l2colorbalance.h @@ -23,7 +23,7 @@ #define __GST_V4L2_COLOR_BALANCE_H__ #include -#include +#include #include "v4l2_calls.h" G_BEGIN_DECLS @@ -32,10 +32,10 @@ G_BEGIN_DECLS (gst_v4l2_color_balance_channel_get_type ()) #define GST_V4L2_COLOR_BALANCE_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_COLOR_BALANCE_CHANNEL, \ - GstV4l2ColorBalanceChannel)) + GstV4l2ColorBalanceChannel)) #define GST_V4L2_COLOR_BALANCE_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_V4L2_COLOR_BALANCE_CHANNEL, \ - GstV4l2ColorBalanceChannelClass)) + GstV4l2ColorBalanceChannelClass)) #define GST_IS_V4L2_COLOR_BALANCE_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_COLOR_BALANCE_CHANNEL)) #define GST_IS_V4L2_COLOR_BALANCE_CHANNEL_CLASS(klass) \ @@ -51,8 +51,8 @@ typedef struct _GstV4l2ColorBalanceChannelClass { GstColorBalanceChannelClass parent; } GstV4l2ColorBalanceChannelClass; -GType gst_v4l2_color_balance_channel_get_type (void); +GType gst_v4l2_color_balance_channel_get_type (void); -void gst_v4l2_color_balance_interface_init (GstColorBalanceClass *klass); +void gst_v4l2_color_balance_interface_init (GstColorBalanceClass *klass); #endif /* __GST_V4L2_COLOR_BALANCE_H__ */ diff --git a/sys/v4l2/gstv4l2element.c b/sys/v4l2/gstv4l2element.c index 139378b31b..e5acbf8795 100644 --- a/sys/v4l2/gstv4l2element.c +++ b/sys/v4l2/gstv4l2element.c @@ -1,5 +1,8 @@ -/* G-Streamer generic V4L2 element - * Copyright (C) 2002 Ronald Bultje +/* GStreamer + * + * gstv4l2element.c: base class for V4L2 elements + * + * Copyright (C) 2001-2002 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -25,6 +28,9 @@ #include #include #include +#include + +#include #include "v4l2_calls.h" #include "gstv4l2tuner.h" @@ -33,55 +39,37 @@ #endif #include "gstv4l2colorbalance.h" -#include - -/* elementfactory details */ -static GstElementDetails gst_v4l2element_details = { - "Generic video4linux2 Element", - "Generic/Video", - "Generic plugin for handling common video4linux2 calls", - "Ronald Bultje " -}; - -/* V4l2Element signals and args */ -enum -{ - /* FILL ME */ - SIGNAL_OPEN, - SIGNAL_CLOSE, - LAST_SIGNAL -}; enum { - ARG_0, - ARG_DEVICE, - ARG_DEVICE_NAME, - ARG_NORM, - ARG_CHANNEL, - ARG_FREQUENCY, - ARG_FLAGS + PROP_0, + PROP_DEVICE, + PROP_DEVICE_NAME, + PROP_FLAGS, + PROP_STD, + PROP_INPUT, + PROP_FREQUENCY }; -static void gst_v4l2element_class_init (GstV4l2ElementClass * klass); -static void gst_v4l2element_base_init (GstV4l2ElementClass * klass); -static void gst_v4l2element_init (GstV4l2Element * v4lelement); -static void gst_v4l2element_dispose (GObject * object); -static void gst_v4l2element_set_property (GObject * object, +static void gst_v4l2element_init_interfaces (GType type); + +GST_BOILERPLATE_FULL (GstV4l2Element, gst_v4l2element, GstPushSrc, + GST_TYPE_PUSH_SRC, gst_v4l2element_init_interfaces) + + + static void gst_v4l2element_dispose (GObject * object); + static void gst_v4l2element_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_v4l2element_get_property (GObject * object, + static void gst_v4l2element_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -static GstStateChangeReturn -gst_v4l2element_change_state (GstElement * element, GstStateChange transition); + static gboolean gst_v4l2element_start (GstBaseSrc * src); + static gboolean gst_v4l2element_stop (GstBaseSrc * src); -static GstElementClass *parent_class = NULL; -static guint gst_v4l2element_signals[LAST_SIGNAL] = { 0 }; - - -static gboolean -gst_v4l2_iface_supported (GstImplementsInterface * iface, GType iface_type) + static gboolean + gst_v4l2_iface_supported (GstImplementsInterface * iface, + GType iface_type) { GstV4l2Element *v4l2element = GST_V4L2ELEMENT (iface); @@ -104,7 +92,6 @@ gst_v4l2_iface_supported (GstImplementsInterface * iface, GType iface_type) return TRUE; } - static void gst_v4l2_interface_init (GstImplementsInterfaceClass * klass) { @@ -112,7 +99,6 @@ gst_v4l2_interface_init (GstImplementsInterfaceClass * klass) klass->supported = gst_v4l2_iface_supported; } - static const GList * gst_v4l2_probe_get_properties (GstPropertyProbe * probe) { @@ -133,7 +119,7 @@ gst_v4l2_class_probe_devices (GstV4l2ElementClass * klass, gboolean check) static GList *devices = NULL; if (!init && !check) { - gchar *dev_base[] = { "/dev/video", "/dev/v4l/video", NULL }; + gchar *dev_base[] = { "/dev/video", "/dev/v4l2/video", NULL }; gint base, n, fd; while (devices) { @@ -148,8 +134,7 @@ gst_v4l2_class_probe_devices (GstV4l2ElementClass * klass, gboolean check) for (n = 0; n < 64; n++) { for (base = 0; dev_base[base] != NULL; base++) { struct stat s; - gchar *device = g_strdup_printf ("%s%d", - dev_base[base], n); + gchar *device = g_strdup_printf ("%s%d", dev_base[base], n); /* does the /dev/ entry exist at all? */ if (stat (device, &s) == 0) { @@ -181,7 +166,7 @@ gst_v4l2_probe_probe_property (GstPropertyProbe * probe, GstV4l2ElementClass *klass = GST_V4L2ELEMENT_GET_CLASS (probe); switch (prop_id) { - case ARG_DEVICE: + case PROP_DEVICE: gst_v4l2_class_probe_devices (klass, FALSE); break; default: @@ -198,7 +183,7 @@ gst_v4l2_probe_needs_probe (GstPropertyProbe * probe, gboolean ret = FALSE; switch (prop_id) { - case ARG_DEVICE: + case PROP_DEVICE: ret = !gst_v4l2_class_probe_devices (klass, TRUE); break; default: @@ -243,7 +228,7 @@ gst_v4l2_probe_get_values (GstPropertyProbe * probe, GValueArray *array = NULL; switch (prop_id) { - case ARG_DEVICE: + case PROP_DEVICE: array = gst_v4l2_class_list_devices (klass); break; default: @@ -254,7 +239,6 @@ gst_v4l2_probe_get_values (GstPropertyProbe * probe, return array; } - static void gst_v4l2_property_probe_interface_init (GstPropertyProbeInterface * iface) { @@ -264,75 +248,6 @@ gst_v4l2_property_probe_interface_init (GstPropertyProbeInterface * iface) iface->get_values = gst_v4l2_probe_get_values; } - -GType -gst_v4l2element_get_type (void) -{ - static GType v4l2element_type = 0; - - if (!v4l2element_type) { - static const GTypeInfo v4l2element_info = { - sizeof (GstV4l2ElementClass), - (GBaseInitFunc) gst_v4l2element_base_init, - NULL, - (GClassInitFunc) gst_v4l2element_class_init, - NULL, - NULL, - sizeof (GstV4l2Element), - 0, - (GInstanceInitFunc) gst_v4l2element_init, - NULL - }; - static const GInterfaceInfo v4l2iface_info = { - (GInterfaceInitFunc) gst_v4l2_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_tuner_info = { - (GInterfaceInitFunc) gst_v4l2_tuner_interface_init, - NULL, - NULL, - }; -#ifdef HAVE_XVIDEO - static const GInterfaceInfo v4l2_xoverlay_info = { - (GInterfaceInitFunc) gst_v4l2_xoverlay_interface_init, - NULL, - NULL, - }; -#endif - static const GInterfaceInfo v4l2_colorbalance_info = { - (GInterfaceInitFunc) gst_v4l2_color_balance_interface_init, - NULL, - NULL, - }; - static const GInterfaceInfo v4l2_propertyprobe_info = { - (GInterfaceInitFunc) gst_v4l2_property_probe_interface_init, - NULL, - NULL, - }; - - v4l2element_type = - g_type_register_static (GST_TYPE_ELEMENT, - "GstV4l2Element", &v4l2element_info, 0); - - g_type_add_interface_static (v4l2element_type, - GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info); - g_type_add_interface_static (v4l2element_type, - GST_TYPE_TUNER, &v4l2_tuner_info); -#ifdef HAVE_XVIDEO - g_type_add_interface_static (v4l2element_type, - GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info); -#endif - g_type_add_interface_static (v4l2element_type, - GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info); - g_type_add_interface_static (v4l2element_type, - GST_TYPE_PROPERTY_PROBE, &v4l2_propertyprobe_info); - } - - return v4l2element_type; -} - - #define GST_TYPE_V4L2_DEVICE_FLAGS (gst_v4l2_device_get_type ()) GType gst_v4l2_device_get_type (void) @@ -341,16 +256,13 @@ gst_v4l2_device_get_type (void) if (v4l2_device_type == 0) { static const GFlagsValue values[] = { - {V4L2_CAP_VIDEO_CAPTURE, "CAPTURE", - "Device can capture"}, - {V4L2_CAP_VIDEO_OUTPUT, "PLAYBACK", - "Device can playback"}, - {V4L2_CAP_VIDEO_OVERLAY, "OVERLAY", - "Device can do overlay"}, - {V4L2_CAP_TUNER, "TUNER", - "Device has a tuner"}, - {V4L2_CAP_AUDIO, "AUDIO", - "Device handles audio"}, + {V4L2_CAP_VIDEO_CAPTURE, "CAPTURE", "Device can capture"}, + {V4L2_CAP_VIDEO_OUTPUT, "PLAYBACK", "Device can playback"}, + {V4L2_CAP_VIDEO_OVERLAY, "OVERLAY", "Device can do overlay"}, + + {V4L2_CAP_TUNER, "TUNER", "Device has a tuner"}, + {V4L2_CAP_AUDIO, "AUDIO", "Device handles audio"}, + {0, NULL, NULL} }; @@ -362,73 +274,108 @@ gst_v4l2_device_get_type (void) } static void -gst_v4l2element_base_init (GstV4l2ElementClass * klass) +gst_v4l2element_init_interfaces (GType type) { - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + static const GInterfaceInfo v4l2iface_info = { + (GInterfaceInitFunc) gst_v4l2_interface_init, + NULL, + NULL, + }; + static const GInterfaceInfo v4l2_tuner_info = { + (GInterfaceInitFunc) gst_v4l2_tuner_interface_init, + NULL, + NULL, + }; +#ifdef HAVE_XVIDEO + static const GInterfaceInfo v4l2_xoverlay_info = { + (GInterfaceInitFunc) gst_v4l2_xoverlay_interface_init, + NULL, + NULL, + }; +#endif + static const GInterfaceInfo v4l2_colorbalance_info = { + (GInterfaceInitFunc) gst_v4l2_color_balance_interface_init, + NULL, + NULL, + }; + static const GInterfaceInfo v4l2_propertyprobe_info = { + (GInterfaceInitFunc) gst_v4l2_property_probe_interface_init, + NULL, + NULL, + }; + + g_type_add_interface_static (type, + GST_TYPE_IMPLEMENTS_INTERFACE, &v4l2iface_info); + g_type_add_interface_static (type, GST_TYPE_TUNER, &v4l2_tuner_info); +#ifdef HAVE_XVIDEO + g_type_add_interface_static (type, GST_TYPE_X_OVERLAY, &v4l2_xoverlay_info); +#endif + g_type_add_interface_static (type, + GST_TYPE_COLOR_BALANCE, &v4l2_colorbalance_info); + g_type_add_interface_static (type, + GST_TYPE_PROPERTY_PROBE, &v4l2_propertyprobe_info); +} + + +static void +gst_v4l2element_base_init (gpointer g_class) +{ + GstV4l2ElementClass *klass = GST_V4L2ELEMENT_CLASS (g_class); klass->devices = NULL; - - gst_element_class_set_details (gstelement_class, &gst_v4l2element_details); } static void gst_v4l2element_class_init (GstV4l2ElementClass * klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS (klass); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + GObjectClass *gobject_class; + GstBaseSrcClass *basesrc_class; - parent_class = g_type_class_peek_parent (klass); - - g_object_class_install_property (gobject_class, ARG_DEVICE, - g_param_spec_string ("device", "Device", "Device location", - NULL, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_DEVICE_NAME, - g_param_spec_string ("device_name", "Device name", - "Name of the device", NULL, G_PARAM_READABLE)); - g_object_class_install_property (gobject_class, ARG_FLAGS, - g_param_spec_flags ("flags", "Flags", "Device type flags", - GST_TYPE_V4L2_DEVICE_FLAGS, 0, G_PARAM_READABLE)); - g_object_class_install_property (gobject_class, ARG_NORM, - g_param_spec_string ("norm", "norm", - "Norm to use", NULL, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_CHANNEL, - g_param_spec_string ("channel", "channel", - "input/output to switch to", NULL, G_PARAM_READWRITE)); - g_object_class_install_property (gobject_class, ARG_FREQUENCY, - g_param_spec_ulong ("frequency", "frequency", - "frequency to tune to (in Hz)", 0, G_MAXULONG, 0, G_PARAM_READWRITE)); - - /* signals */ - gst_v4l2element_signals[SIGNAL_OPEN] = - g_signal_new ("open", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstV4l2ElementClass, open), - NULL, NULL, g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, G_TYPE_STRING); - gst_v4l2element_signals[SIGNAL_CLOSE] = - g_signal_new ("close", G_TYPE_FROM_CLASS (klass), G_SIGNAL_RUN_LAST, - G_STRUCT_OFFSET (GstV4l2ElementClass, close), - NULL, NULL, g_cclosure_marshal_VOID__STRING, - G_TYPE_NONE, 1, G_TYPE_STRING); + gobject_class = (GObjectClass *) klass; + basesrc_class = (GstBaseSrcClass *) klass; gobject_class->set_property = gst_v4l2element_set_property; gobject_class->get_property = gst_v4l2element_get_property; - gobject_class->dispose = gst_v4l2element_dispose; - gstelement_class->change_state = gst_v4l2element_change_state; + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEVICE, + g_param_spec_string ("device", "Device", "Device location", + NULL, G_PARAM_READWRITE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DEVICE_NAME, + g_param_spec_string ("device_name", "Device name", "Name of the device", + NULL, G_PARAM_READABLE)); + g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FLAGS, + g_param_spec_flags ("flags", "Flags", "Device type flags", + GST_TYPE_V4L2_DEVICE_FLAGS, 0, G_PARAM_READABLE)); + g_object_class_install_property (gobject_class, PROP_STD, + g_param_spec_string ("std", "std", + "standard (norm) to use", NULL, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_INPUT, + g_param_spec_string ("input", "input", + "input/output (channel) to switch to", NULL, G_PARAM_READWRITE)); + g_object_class_install_property (gobject_class, PROP_FREQUENCY, + g_param_spec_ulong ("frequency", "frequency", + "frequency to tune to (in Hz)", 0, G_MAXULONG, 0, G_PARAM_READWRITE)); + + basesrc_class->start = gst_v4l2element_start; + basesrc_class->stop = gst_v4l2element_stop; + + gobject_class->dispose = gst_v4l2element_dispose; } static void -gst_v4l2element_init (GstV4l2Element * v4l2element) +gst_v4l2element_init (GstV4l2Element * v4l2element, GstV4l2ElementClass * klass) { /* some default values */ v4l2element->video_fd = -1; v4l2element->buffer = NULL; - v4l2element->device = g_strdup ("/dev/video0"); + v4l2element->videodev = g_strdup ("/dev/video0"); - v4l2element->channels = NULL; - v4l2element->norms = NULL; + v4l2element->stds = NULL; + v4l2element->inputs = NULL; v4l2element->colors = NULL; + + v4l2element->xwindow_id = 0; } @@ -437,35 +384,29 @@ gst_v4l2element_dispose (GObject * object) { GstV4l2Element *v4l2element = GST_V4L2ELEMENT (object); - g_free (v4l2element->device); - v4l2element->device = NULL; - g_free (v4l2element->norm); - v4l2element->norm = NULL; - g_free (v4l2element->channel); - v4l2element->channel = NULL; + if (v4l2element->videodev) { + g_free (v4l2element->videodev); + v4l2element->videodev = NULL; + } if (((GObjectClass *) parent_class)->dispose) ((GObjectClass *) parent_class)->dispose (object); } + static void gst_v4l2element_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec) { - GstV4l2Element *v4l2element; - - g_return_if_fail (GST_IS_V4L2ELEMENT (object)); - v4l2element = GST_V4L2ELEMENT (object); + GstV4l2Element *v4l2element = GST_V4L2ELEMENT (object); switch (prop_id) { - case ARG_DEVICE: - if (!GST_V4L2_IS_OPEN (v4l2element)) { - if (v4l2element->device) - g_free (v4l2element->device); - v4l2element->device = g_value_dup_string (value); - } + case PROP_DEVICE: + if (v4l2element->videodev) + g_free (v4l2element->videodev); + v4l2element->videodev = g_strdup (g_value_get_string (value)); break; - case ARG_NORM: + case PROP_STD: if (GST_V4L2_IS_OPEN (v4l2element)) { GstTuner *tuner = GST_TUNER (v4l2element); GstTunerNorm *norm = gst_tuner_find_norm_by_name (tuner, @@ -475,12 +416,12 @@ gst_v4l2element_set_property (GObject * object, gst_tuner_set_norm (tuner, norm); } } else { - g_free (v4l2element->norm); - v4l2element->norm = g_value_dup_string (value); - g_object_notify (object, "norm"); + g_free (v4l2element->std); + v4l2element->std = g_value_dup_string (value); + g_object_notify (object, "std"); } break; - case ARG_CHANNEL: + case PROP_INPUT: if (GST_V4L2_IS_OPEN (v4l2element)) { GstTuner *tuner = GST_TUNER (v4l2element); GstTunerChannel *channel = gst_tuner_find_channel_by_name (tuner, @@ -490,12 +431,12 @@ gst_v4l2element_set_property (GObject * object, gst_tuner_set_channel (tuner, channel); } } else { - g_free (v4l2element->channel); - v4l2element->channel = g_value_dup_string (value); - g_object_notify (object, "channel"); + g_free (v4l2element->input); + v4l2element->input = g_value_dup_string (value); + g_object_notify (object, "input"); } break; - case ARG_FREQUENCY: + case PROP_FREQUENCY: if (GST_V4L2_IS_OPEN (v4l2element)) { GstTuner *tuner = GST_TUNER (v4l2element); GstTunerChannel *channel = gst_tuner_get_channel (tuner); @@ -520,39 +461,40 @@ static void gst_v4l2element_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec) { - GstV4l2Element *v4l2element; - - g_return_if_fail (GST_IS_V4L2ELEMENT (object)); - v4l2element = GST_V4L2ELEMENT (object); + GstV4l2Element *v4l2element = GST_V4L2ELEMENT (object); switch (prop_id) { - case ARG_DEVICE: - g_value_set_string (value, v4l2element->device); + case PROP_DEVICE: + g_value_set_string (value, v4l2element->videodev); break; - case ARG_DEVICE_NAME:{ + case PROP_DEVICE_NAME:{ gchar *new = NULL; if (GST_V4L2_IS_OPEN (v4l2element)) - new = v4l2element->vcap.card; + new = (gchar *) v4l2element->vcap.card; g_value_set_string (value, new); break; } - case ARG_FLAGS:{ + case PROP_FLAGS:{ guint flags = 0; if (GST_V4L2_IS_OPEN (v4l2element)) { - flags |= v4l2element->vcap.capabilities & 0x30007; + flags |= v4l2element->vcap.capabilities & + (V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_VIDEO_OUTPUT | + V4L2_CAP_VIDEO_OVERLAY | V4L2_CAP_TUNER | V4L2_CAP_AUDIO); + if (v4l2element->vcap.capabilities & V4L2_CAP_AUDIO) + flags |= V4L2_FBUF_CAP_CHROMAKEY; } g_value_set_flags (value, flags); break; } - case ARG_NORM: - g_value_set_string (value, v4l2element->norm); + case PROP_STD: + g_value_set_string (value, v4l2element->std); break; - case ARG_CHANNEL: - g_value_set_string (value, v4l2element->channel); + case PROP_INPUT: + g_value_set_string (value, v4l2element->input); break; - case ARG_FREQUENCY: + case PROP_FREQUENCY: g_value_set_ulong (value, v4l2element->frequency); break; default: @@ -561,48 +503,32 @@ gst_v4l2element_get_property (GObject * object, } } - -static GstStateChangeReturn -gst_v4l2element_change_state (GstElement * element, GstStateChange transition) +static gboolean +gst_v4l2element_start (GstBaseSrc * src) { - GstV4l2Element *v4l2element; + GstV4l2Element *v4l2element = GST_V4L2ELEMENT (src); - g_return_val_if_fail (GST_IS_V4L2ELEMENT (element), GST_STATE_CHANGE_FAILURE); - - v4l2element = GST_V4L2ELEMENT (element); - - /* if going down into NULL state, close the device if it's open - * if going to READY, open the device (and set some options) - */ - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!gst_v4l2_open (v4l2element)) - return GST_STATE_CHANGE_FAILURE; + if (!gst_v4l2_open (v4l2element)) + return FALSE; #ifdef HAVE_XVIDEO - gst_v4l2_xoverlay_open (v4l2element); + gst_v4l2_xoverlay_start (v4l2element); #endif - /* emit a signal! whoopie! */ - g_signal_emit (G_OBJECT (v4l2element), - gst_v4l2element_signals[SIGNAL_OPEN], 0, v4l2element->device); - break; - case GST_STATE_CHANGE_READY_TO_NULL: -#ifdef HAVE_XVIDEO - gst_v4l2_xoverlay_close (v4l2element); -#endif - - if (!gst_v4l2_close (v4l2element)) - return GST_STATE_CHANGE_FAILURE; - - /* emit yet another signal! wheehee! */ - g_signal_emit (G_OBJECT (v4l2element), - gst_v4l2element_signals[SIGNAL_CLOSE], 0, v4l2element->device); - break; - } - - if (GST_ELEMENT_CLASS (parent_class)->change_state) - return GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - - return GST_STATE_CHANGE_SUCCESS; + return TRUE; +} + +static gboolean +gst_v4l2element_stop (GstBaseSrc * src) +{ + GstV4l2Element *v4l2element = GST_V4L2ELEMENT (src); + +#ifdef HAVE_XVIDEO + gst_v4l2_xoverlay_stop (v4l2element); +#endif + + if (!gst_v4l2_close (v4l2element)) + return FALSE; + + return TRUE; } diff --git a/sys/v4l2/gstv4l2element.h b/sys/v4l2/gstv4l2element.h index 62663a29a9..06e50bf796 100644 --- a/sys/v4l2/gstv4l2element.h +++ b/sys/v4l2/gstv4l2element.h @@ -1,5 +1,8 @@ -/* G-Streamer generic V4L2 element - * Copyright (C) 2002 Ronald Bultje +/* GStreamer + * + * gstv4l2element.h: base class for V4L2 elements + * + * Copyright (C) 2001-2002 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,9 +23,6 @@ #ifndef __GST_V4L2ELEMENT_H__ #define __GST_V4L2ELEMENT_H__ -#include -#include - /* Because of some really cool feature in video4linux1, also known as * 'not including sys/types.h and sys/time.h', we had to include it * ourselves. In all their intelligence, these people decided to fix @@ -36,104 +36,87 @@ * errors here, check your linux/time.h && sys/time.h header setup. */ #include -#include #define _LINUX_TIME_H -#define __user #include -/* - * See bug #135919, the Suse9 (and Mandrake10) videodev2 headers - * contain a bug where (for userspace applications) the v4l2_buffer - * struct is not declared, so applications have to declare it. - * Declaration straightly ripped out from . - */ -#ifdef GST_V4L2_MISSING_BUFDECL -struct v4l2_buffer -{ - __u32 index; - enum v4l2_buf_type type; - __u32 bytesused; - __u32 flags; - enum v4l2_field field; - struct timeval timestamp; - struct v4l2_timecode timecode; - __u32 sequence; - - /* memory location */ - enum v4l2_memory memory; - union { - __u32 offset; - unsigned long userptr; - } m; - __u32 length; - - __u32 reserved[2]; -}; -#endif /* GST_V4L2_MISSING_BUFDECL */ +#include +#include -#define GST_TYPE_V4L2ELEMENT \ - (gst_v4l2element_get_type()) -#define GST_V4L2ELEMENT(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_V4L2ELEMENT, GstV4l2Element)) -#define GST_V4L2ELEMENT_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_V4L2ELEMENT, GstV4l2ElementClass)) -#define GST_IS_V4L2ELEMENT(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_V4L2ELEMENT)) -#define GST_IS_V4L2ELEMENT_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_V4L2ELEMENT)) -#define GST_V4L2ELEMENT_GET_CLASS(obj) \ - (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2ELEMENT, GstV4l2ElementClass)) +G_BEGIN_DECLS +#define GST_TYPE_V4L2ELEMENT \ + (gst_v4l2element_get_type()) +#define GST_V4L2ELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2ELEMENT,GstV4l2Element)) +#define GST_V4L2ELEMENT_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2ELEMENT,GstV4l2ElementClass)) +#define GST_IS_V4L2ELEMENT(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2ELEMENT)) +#define GST_IS_V4L2ELEMENT_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2ELEMENT)) +#define GST_V4L2ELEMENT_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_V4L2ELEMENT, GstV4l2ElementClass)) -typedef struct _GstV4l2Element GstV4l2Element; -typedef struct _GstV4l2ElementClass GstV4l2ElementClass; -typedef struct _GstV4l2Xv GstV4l2Xv; +typedef struct _GstV4l2Element GstV4l2Element; +typedef struct _GstV4l2ElementClass GstV4l2ElementClass; +typedef struct _GstV4l2Xv GstV4l2Xv; struct _GstV4l2Element { - GstElement element; + GstPushSrc element; - /* the video device */ - char *device; + /* the video device */ + char *videodev; - /* the video-device's file descriptor */ - gint video_fd; + /* the video-device's file descriptor */ + gint video_fd; - /* the video buffer (mmap()'ed) */ - guint8 **buffer; + /* the video buffer (mmap()'ed) */ + guint8 **buffer; - /* the video-device's capabilities */ - struct v4l2_capability vcap; + /* the video device's capabilities */ + struct v4l2_capability vcap; - /* the toys available to us */ - GList *channels; - GList *norms; - GList *colors; + /* the video device's window properties */ + struct v4l2_window vwin; - /* X-overlay */ - GstV4l2Xv *xv; - XID xwindow_id; + /* some more info about the current input's capabilities */ + struct v4l2_input vinput; - /* properties */ - gchar *norm; - gchar *channel; - gulong frequency; + /* lists... */ + GList *colors; + GList *stds; + GList *inputs; + + /* properties */ + gchar *std; + gchar *input; + gulong frequency; + + + /* X-overlay */ + GstV4l2Xv *xv; + gulong xwindow_id; }; struct _GstV4l2ElementClass { - GstElementClass parent_class; + GstPushSrcClass parent_class; - /* probed devices */ - GList *devices; + /* probed devices */ + GList *devices; - /* signals */ - void (*open) (GstElement *element, - const gchar *device); - void (*close) (GstElement *element, - const gchar *device); + /* actions */ + gboolean (*get_attribute) (GstElement *element, + const gchar *attr_name, + int *value); + gboolean (*set_attribute) (GstElement *element, + const gchar *attr_name, + const int value); }; +GType gst_v4l2element_get_type(void); -GType gst_v4l2element_get_type (void); + +G_END_DECLS #endif /* __GST_V4L2ELEMENT_H__ */ diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c index 02fc41b49e..180ab94ff7 100644 --- a/sys/v4l2/gstv4l2src.c +++ b/sys/v4l2/gstv4l2src.c @@ -1,5 +1,8 @@ -/* G-Streamer Video4linux2 video-capture plugin - * Copyright (C) 2002 Ronald Bultje +/* GStreamer + * + * gstv4l2src.c: BT8x8/V4L2 source element + * + * Copyright (C) 2001-2002 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -18,365 +21,266 @@ */ #ifdef HAVE_CONFIG_H -#include "config.h" +#include #endif #include #include #include "v4l2src_calls.h" -#include "gstv4l2tuner.h" +#include +#include + + +static GstElementDetails gst_v4l2src_details = +GST_ELEMENT_DETAILS ("Video (video4linux2/raw) Source", + "Source/Video", + "Reads raw frames from a video4linux2 (BT8x8) device", + "Ronald Bultje ," + " Edgard Lima "); + GST_DEBUG_CATEGORY (v4l2src_debug); #define GST_CAT_DEFAULT v4l2src_debug -/* elementfactory details */ -static GstElementDetails gst_v4l2src_details = { - "Video (video4linux2) Source", - "Source/Video", - "Reads frames (compressed or uncompressed) from a video4linux2 device", - "Ronald Bultje " -}; -/* V4l2Src signals and args */ enum { - SIGNAL_FRAME_CAPTURE, - SIGNAL_FRAME_DROP, - SIGNAL_FRAME_INSERT, - SIGNAL_FRAME_LOST, - LAST_SIGNAL + PROP_0, + PROP_USE_FIXED_FPS }; -/* arguments */ -enum -{ - ARG_0, - ARG_NUMBUFS, - ARG_BUFSIZE, - ARG_USE_FIXED_FPS + +static guint32 gst_v4l2_formats[] = { + /* from Linux 2.6.15 videodev2.h */ + V4L2_PIX_FMT_RGB332, + V4L2_PIX_FMT_RGB555, + V4L2_PIX_FMT_RGB565, + V4L2_PIX_FMT_RGB555X, + V4L2_PIX_FMT_RGB565X, + V4L2_PIX_FMT_BGR24, + V4L2_PIX_FMT_RGB24, + V4L2_PIX_FMT_BGR32, + V4L2_PIX_FMT_RGB32, + V4L2_PIX_FMT_GREY, + V4L2_PIX_FMT_YVU410, + V4L2_PIX_FMT_YVU420, + V4L2_PIX_FMT_YUYV, + V4L2_PIX_FMT_UYVY, + V4L2_PIX_FMT_YUV422P, + V4L2_PIX_FMT_YUV411P, + V4L2_PIX_FMT_Y41P, + + /* two planes -- one Y, one Cr + Cb interleaved */ + V4L2_PIX_FMT_NV12, + V4L2_PIX_FMT_NV21, + + /* The following formats are not defined in the V4L2 specification */ + V4L2_PIX_FMT_YUV410, + V4L2_PIX_FMT_YUV420, + V4L2_PIX_FMT_YYUV, + V4L2_PIX_FMT_HI240, + + /* see http://www.siliconimaging.com/RGB%20Bayer.htm */ + V4L2_PIX_FMT_SBGGR8, + + /* compressed formats */ + V4L2_PIX_FMT_MJPEG, + V4L2_PIX_FMT_JPEG, + V4L2_PIX_FMT_DV, + V4L2_PIX_FMT_MPEG, + + /* Vendor-specific formats */ + V4L2_PIX_FMT_WNVA, + V4L2_PIX_FMT_SN9C10X + /* V4L2_PIX_FMT_PWC1 FIX */ + /* V4L2_PIX_FMT_PWC2 FIX */ }; -guint32 gst_v4l2_formats[] = { - /* from Linux 2.6.0 videodev2.h */ - V4L2_PIX_FMT_RGB332, /* 8 RGB-3-3-2 */ - V4L2_PIX_FMT_RGB555, /* 16 RGB-5-5-5 */ - V4L2_PIX_FMT_RGB565, /* 16 RGB-5-6-5 */ - V4L2_PIX_FMT_RGB555X, /* 16 RGB-5-5-5 BE */ - V4L2_PIX_FMT_RGB565X, /* 16 RGB-5-6-5 BE */ - V4L2_PIX_FMT_BGR24, /* 24 BGR-8-8-8 */ - V4L2_PIX_FMT_RGB24, /* 24 RGB-8-8-8 */ - V4L2_PIX_FMT_BGR32, /* 32 BGR-8-8-8-8 */ - V4L2_PIX_FMT_RGB32, /* 32 RGB-8-8-8-8 */ - V4L2_PIX_FMT_GREY, /* 8 Greyscale */ - V4L2_PIX_FMT_YVU410, /* 9 YVU 4:1:0 */ - V4L2_PIX_FMT_YVU420, /* 12 YVU 4:2:0 */ - V4L2_PIX_FMT_YUYV, /* 16 YUV 4:2:2 */ - V4L2_PIX_FMT_UYVY, /* 16 YUV 4:2:2 */ - V4L2_PIX_FMT_YUV422P, /* 16 YVU422 planar */ - V4L2_PIX_FMT_YUV411P, /* 16 YVU411 planar */ - V4L2_PIX_FMT_Y41P, /* 12 YUV 4:1:1 */ - V4L2_PIX_FMT_NV12, /* 12 Y/CbCr 4:2:0 */ - V4L2_PIX_FMT_NV21, /* 12 Y/CrCb 4:2:0 */ - V4L2_PIX_FMT_YUV410, /* 9 YUV 4:1:0 */ - V4L2_PIX_FMT_YUV420, /* 12 YUV 4:2:0 */ - V4L2_PIX_FMT_YYUV, /* 16 YUV 4:2:2 */ - V4L2_PIX_FMT_HI240, /* 8 8-bit color */ - V4L2_PIX_FMT_MJPEG, /* Motion-JPEG */ - V4L2_PIX_FMT_JPEG, /* JFIF JPEG */ - V4L2_PIX_FMT_DV, /* 1394 */ - V4L2_PIX_FMT_MPEG, /* MPEG */ - V4L2_PIX_FMT_WNVA /* Winnov hw compres */ -}; #define GST_V4L2_FORMAT_COUNT (G_N_ELEMENTS (gst_v4l2_formats)) -GST_FORMATS_FUNCTION (GstPad *, gst_v4l2src_get_formats, - GST_FORMAT_TIME, GST_FORMAT_DEFAULT); -GST_QUERY_TYPE_FUNCTION (GstPad *, gst_v4l2src_get_query_types, - GST_QUERY_POSITION); -/* init functions */ -static void gst_v4l2src_class_init (gpointer g_class, gpointer class_data); -static void gst_v4l2src_base_init (gpointer g_class); -static void gst_v4l2src_init (GTypeInstance * instance, gpointer g_class); +GST_BOILERPLATE (GstV4l2Src, gst_v4l2src, GstV4l2Element, GST_TYPE_V4L2ELEMENT); -/* signal functions */ -static void gst_v4l2src_open (GstElement * element, const gchar * device); -static void gst_v4l2src_close (GstElement * element, const gchar * device); -/* pad/buffer functions */ -static const GstCaps *gst_v4l2src_get_all_caps (void); -static GstPadLinkReturn gst_v4l2src_link (GstPad * pad, const GstCaps * caps); -static GstCaps *gst_v4l2src_getcaps (GstPad * pad); -static GstCaps *gst_v4l2src_fixate (GstPad * pad, const GstCaps * caps); -static GstData *gst_v4l2src_get (GstPad * pad); -static gboolean gst_v4l2src_src_convert (GstPad * pad, - GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value); -static gboolean gst_v4l2src_src_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value); +/* basesrc methods */ +static gboolean gst_v4l2src_start (GstBaseSrc * src); +static gboolean gst_v4l2src_stop (GstBaseSrc * src); +static gboolean gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps); +static GstCaps *gst_v4l2src_get_caps (GstBaseSrc * src); +static GstFlowReturn gst_v4l2src_create (GstPushSrc * src, GstBuffer ** out); -/* get/set params */ -static void gst_v4l2src_set_property (GObject * object, +static void gst_v4l2src_fixate (GstPad * pad, GstCaps * caps); + +static void +gst_v4l2src_set_property (GObject * object, guint prop_id, const GValue * value, GParamSpec * pspec); -static void gst_v4l2src_get_property (GObject * object, +static void +gst_v4l2src_get_property (GObject * object, guint prop_id, GValue * value, GParamSpec * pspec); -/* state handling */ -static GstStateChangeReturn gst_v4l2src_change_state (GstElement * element, - GstStateChange transition); - -/* set_clock function for A/V sync */ -static void gst_v4l2src_set_clock (GstElement * element, GstClock * clock); - -static GstElementClass *parent_class = NULL; -static guint gst_v4l2src_signals[LAST_SIGNAL] = { 0 }; - - -GType -gst_v4l2src_get_type (void) -{ - static GType v4l2src_type = 0; - - if (!v4l2src_type) { - static const GTypeInfo v4l2src_info = { - sizeof (GstV4l2SrcClass), - gst_v4l2src_base_init, - NULL, - gst_v4l2src_class_init, - NULL, - NULL, - sizeof (GstV4l2Src), - 0, - gst_v4l2src_init, - NULL - }; - - v4l2src_type = g_type_register_static (GST_TYPE_V4L2ELEMENT, - "GstV4l2Src", &v4l2src_info, 0); - GST_DEBUG_CATEGORY_INIT (v4l2src_debug, "v4l2src", 0, "v4l2src element"); - } - return v4l2src_type; -} +static GstCaps *gst_v4l2src_get_all_caps (void); static void gst_v4l2src_base_init (gpointer g_class) { - GstPadTemplate *template; GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); gst_element_class_set_details (gstelement_class, &gst_v4l2src_details); - template = gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, - gst_caps_copy (gst_v4l2src_get_all_caps ())); - - gst_element_class_add_pad_template (gstelement_class, template); + gst_element_class_add_pad_template + (gstelement_class, + gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS, + gst_v4l2src_get_all_caps ())); } static void -gst_v4l2src_class_init (gpointer g_class, gpointer class_data) +gst_v4l2src_class_init (GstV4l2SrcClass * klass) { - GObjectClass *gobject_class = G_OBJECT_CLASS (g_class); - GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class); - GstV4l2ElementClass *v4l2_class = GST_V4L2ELEMENT_CLASS (g_class); + GObjectClass *gobject_class; + GstBaseSrcClass *basesrc_class; + GstPushSrcClass *pushsrc_class; - parent_class = g_type_class_peek_parent (g_class); + gobject_class = G_OBJECT_CLASS (klass); + basesrc_class = GST_BASE_SRC_CLASS (klass); + pushsrc_class = GST_PUSH_SRC_CLASS (klass); gobject_class->set_property = gst_v4l2src_set_property; gobject_class->get_property = gst_v4l2src_get_property; - g_object_class_install_property (gobject_class, 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 (gobject_class, ARG_BUFSIZE, - g_param_spec_int ("buffer_size", "buffer_size", "buffer_size", - G_MININT, G_MAXINT, 0, G_PARAM_READABLE)); - - g_object_class_install_property (gobject_class, ARG_USE_FIXED_FPS, + g_object_class_install_property + (gobject_class, PROP_USE_FIXED_FPS, g_param_spec_boolean ("use_fixed_fps", "Use Fixed FPS", "Drop/Insert frames to reach a certain FPS (TRUE) " "or adapt FPS to suit the number of frabbed frames", TRUE, G_PARAM_READWRITE)); - /* signals */ - gst_v4l2src_signals[SIGNAL_FRAME_CAPTURE] = - g_signal_new ("frame-capture", G_TYPE_FROM_CLASS (g_class), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstV4l2SrcClass, frame_capture), NULL, - NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - gst_v4l2src_signals[SIGNAL_FRAME_DROP] = - g_signal_new ("frame-drop", G_TYPE_FROM_CLASS (g_class), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstV4l2SrcClass, frame_drop), NULL, - NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - gst_v4l2src_signals[SIGNAL_FRAME_INSERT] = - g_signal_new ("frame_insert", G_TYPE_FROM_CLASS (g_class), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstV4l2SrcClass, frame_insert), NULL, - NULL, g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); - gst_v4l2src_signals[SIGNAL_FRAME_LOST] = - g_signal_new ("frame-lost", G_TYPE_FROM_CLASS (g_class), - G_SIGNAL_RUN_LAST, G_STRUCT_OFFSET (GstV4l2SrcClass, frame_lost), NULL, - NULL, g_cclosure_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT); + GST_DEBUG_CATEGORY_INIT (v4l2src_debug, "v4l2src", 0, "V4L2 source element"); - gstelement_class->change_state = gst_v4l2src_change_state; + basesrc_class->get_caps = gst_v4l2src_get_caps; + basesrc_class->set_caps = gst_v4l2src_set_caps; + basesrc_class->start = gst_v4l2src_start; + basesrc_class->stop = gst_v4l2src_stop; - v4l2_class->open = gst_v4l2src_open; - v4l2_class->close = gst_v4l2src_close; - - gstelement_class->set_clock = gst_v4l2src_set_clock; + pushsrc_class->create = gst_v4l2src_create; } - static void -gst_v4l2src_init (GTypeInstance * instance, gpointer g_class) +gst_v4l2src_init (GstV4l2Src * v4l2src, GstV4l2SrcClass * klass) { - GstV4l2Src *v4l2src = GST_V4L2SRC (instance); - - GST_OBJECT_FLAG_SET (GST_ELEMENT (v4l2src), GST_ELEMENT_THREAD_SUGGESTED); - - v4l2src->srcpad = - gst_pad_new_from_template (gst_element_class_get_pad_template - (GST_ELEMENT_GET_CLASS (v4l2src), "src"), "src"); - gst_element_add_pad (GST_ELEMENT (v4l2src), v4l2src->srcpad); - - gst_pad_set_get_function (v4l2src->srcpad, gst_v4l2src_get); - gst_pad_set_link_function (v4l2src->srcpad, gst_v4l2src_link); - gst_pad_set_getcaps_function (v4l2src->srcpad, gst_v4l2src_getcaps); - gst_pad_set_fixate_function (v4l2src->srcpad, gst_v4l2src_fixate); - gst_pad_set_convert_function (v4l2src->srcpad, gst_v4l2src_src_convert); - gst_pad_set_formats_function (v4l2src->srcpad, gst_v4l2src_get_formats); - gst_pad_set_query_function (v4l2src->srcpad, gst_v4l2src_src_query); - gst_pad_set_query_type_function (v4l2src->srcpad, - gst_v4l2src_get_query_types); v4l2src->breq.count = 0; v4l2src->formats = NULL; - /* no clock */ - v4l2src->clock = NULL; - /* fps */ v4l2src->use_fixed_fps = TRUE; v4l2src->is_capturing = FALSE; -} + gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (v4l2src), + gst_v4l2src_fixate); + + gst_base_src_set_live (GST_BASE_SRC (v4l2src), TRUE); +} static void -gst_v4l2src_open (GstElement * element, const gchar * device) -{ - gst_v4l2src_fill_format_list (GST_V4L2SRC (element)); -} - - -static void -gst_v4l2src_close (GstElement * element, const gchar * device) -{ - gst_v4l2src_clear_format_list (GST_V4L2SRC (element)); -} - - -static gfloat -gst_v4l2src_get_fps (GstV4l2Src * v4l2src) -{ - v4l2_std_id norm; - const GList *item; - - if (!v4l2src->use_fixed_fps && v4l2src->clock != NULL && v4l2src->handled > 0) { - /* try to get time from clock master and calculate fps */ - GstClockTime time = gst_clock_get_time (v4l2src->clock) - - v4l2src->substract_time; - return v4l2src->handled * GST_SECOND / time; - } - - /* if that failed ... */ - - if (!GST_V4L2_IS_OPEN (GST_V4L2ELEMENT (v4l2src))) - return 0.; - - if (!gst_v4l2_get_norm (GST_V4L2ELEMENT (v4l2src), &norm)) - return 0.; - for (item = GST_V4L2ELEMENT (v4l2src)->norms; item != NULL; item = item->next) { - GstV4l2TunerNorm *v4l2norm = item->data; - - if (v4l2norm->index == norm) - return GST_TUNER_NORM (v4l2norm)->fps; - } - - return 0.; -} - -static gboolean -gst_v4l2src_src_convert (GstPad * pad, - GstFormat src_format, - gint64 src_value, GstFormat * dest_format, gint64 * dest_value) +gst_v4l2src_set_property (GObject * object, + guint prop_id, const GValue * value, GParamSpec * pspec) { GstV4l2Src *v4l2src; - gdouble fps; - v4l2src = GST_V4L2SRC (gst_pad_get_parent (pad)); + g_return_if_fail (GST_IS_V4L2SRC (object)); + v4l2src = GST_V4L2SRC (object); - if ((fps = gst_v4l2src_get_fps (v4l2src)) == 0) - return FALSE; - - switch (src_format) { - case GST_FORMAT_TIME: - switch (*dest_format) { - case GST_FORMAT_DEFAULT: - *dest_value = src_value * fps / GST_SECOND; - break; - default: - return FALSE; - } - break; - - case GST_FORMAT_DEFAULT: - switch (*dest_format) { - case GST_FORMAT_TIME: - *dest_value = src_value * GST_SECOND / fps; - break; - default: - return FALSE; + switch (prop_id) { + case PROP_USE_FIXED_FPS: + if (!GST_V4L2_IS_ACTIVE (GST_V4L2ELEMENT (v4l2src))) { + v4l2src->use_fixed_fps = g_value_get_boolean (value); } break; default: - return FALSE; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; } - - return TRUE; } -static gboolean -gst_v4l2src_src_query (GstPad * pad, - GstQueryType type, GstFormat * format, gint64 * value) + +static void +gst_v4l2src_get_property (GObject * object, + guint prop_id, GValue * value, GParamSpec * pspec) { - GstV4l2Src *v4l2src = GST_V4L2SRC (gst_pad_get_parent (pad)); - gboolean res = TRUE; - gdouble fps; + GstV4l2Src *v4l2src; - if ((fps = gst_v4l2src_get_fps (v4l2src)) == 0) - return FALSE; + g_return_if_fail (GST_IS_V4L2SRC (object)); + v4l2src = GST_V4L2SRC (object); - switch (type) { - case GST_QUERY_POSITION: - switch (*format) { - case GST_FORMAT_TIME: - *value = v4l2src->handled * GST_SECOND / fps; - break; - case GST_FORMAT_DEFAULT: - *value = v4l2src->handled; - break; - default: - res = FALSE; - break; - } + switch (prop_id) { + case PROP_USE_FIXED_FPS: + g_value_set_boolean (value, v4l2src->use_fixed_fps); break; + default: - res = FALSE; + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; } +} - return res; + +gboolean +get_fmt_width_height (GstV4l2Src * v4l2src, int *width, int *height) +{ + int ret; + + struct v4l2_format format; + + memset (&format, 0x00, sizeof (format)); + + format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + + ret = ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_G_FMT, &format); + + if (ret == 0) { + + *width = format.fmt.pix.width; + *height = format.fmt.pix.height; + + } + + return (ret == 0); + +} + +/* this function is a bit of a last resort */ +static void +gst_v4l2src_fixate (GstPad * pad, GstCaps * caps) +{ + GstStructure *structure; + gint i; + G_GNUC_UNUSED gchar *caps_str; + + caps_str = gst_caps_to_string (caps); + GST_DEBUG_OBJECT (GST_PAD_PARENT (pad), "fixating caps %s", caps_str); + g_free (caps_str); + + for (i = 0; i < gst_caps_get_size (caps); ++i) { + structure = gst_caps_get_structure (caps, i); + const GValue *v; + + gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT); + gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT); + + v = gst_structure_get_value (structure, "format"); + if (v && G_VALUE_TYPE (v) != GST_TYPE_FOURCC) { + guint32 fourcc; + + g_return_if_fail (G_VALUE_TYPE (v) == GST_TYPE_LIST); + + fourcc = gst_value_get_fourcc (gst_value_list_get_value (v, 0)); + gst_structure_set (structure, "format", GST_TYPE_FOURCC, fourcc, NULL); + } + } } static GstStructure * @@ -535,44 +439,14 @@ gst_v4l2src_v4l2fourcc_to_caps (guint32 fourcc) case V4L2_PIX_FMT_WNVA: /* Winnov hw compres */ break; default: - GST_DEBUG ("Unknown fourcc 0x%08x %" GST_FOURCC_FORMAT, + GST_DEBUG ("Unknown fourcc 0x%08x " GST_FOURCC_FORMAT, fourcc, GST_FOURCC_ARGS (fourcc)); break; } -#if 0 - gst_caps_set_simple (caps, - "width", G_TYPE_INT, width, - "height", G_TYPE_INT, height, "framerate", G_TYPE_DOUBLE, fps, NULL); -#endif + return structure; } -static struct v4l2_fmtdesc * -gst_v4l2src_get_format_from_fourcc (GstV4l2Src * v4l2src, guint32 fourcc) -{ - struct v4l2_fmtdesc *fmt; - GSList *walk; - - if (fourcc == 0) - return NULL; - - walk = v4l2src->formats; - while (walk) { - fmt = (struct v4l2_fmtdesc *) walk->data; - if (fmt->pixelformat == fourcc) - return fmt; - /* special case for jpeg */ - if ((fmt->pixelformat == V4L2_PIX_FMT_MJPEG && fourcc == V4L2_PIX_FMT_JPEG) - || (fmt->pixelformat == V4L2_PIX_FMT_JPEG - && fourcc == V4L2_PIX_FMT_MJPEG)) { - return fmt; - } - walk = g_slist_next (walk); - } - - return NULL; -} - static guint32 gst_v4l2_fourcc_from_structure (GstStructure * structure) { @@ -641,6 +515,32 @@ gst_v4l2_fourcc_from_structure (GstStructure * structure) return fourcc; } +static struct v4l2_fmtdesc * +gst_v4l2src_get_format_from_fourcc (GstV4l2Src * v4l2src, guint32 fourcc) +{ + struct v4l2_fmtdesc *fmt; + GSList *walk; + + if (fourcc == 0) + return NULL; + + walk = v4l2src->formats; + while (walk) { + fmt = (struct v4l2_fmtdesc *) walk->data; + if (fmt->pixelformat == fourcc) + return fmt; + /* special case for jpeg */ + if ((fmt->pixelformat == V4L2_PIX_FMT_MJPEG && fourcc == V4L2_PIX_FMT_JPEG) + || (fmt->pixelformat == V4L2_PIX_FMT_JPEG + && fourcc == V4L2_PIX_FMT_MJPEG)) { + return fmt; + } + walk = g_slist_next (walk); + } + + return NULL; +} + static struct v4l2_fmtdesc * gst_v4l2_caps_to_v4l2fourcc (GstV4l2Src * v4l2src, GstStructure * structure) { @@ -648,7 +548,7 @@ gst_v4l2_caps_to_v4l2fourcc (GstV4l2Src * v4l2src, GstStructure * structure) gst_v4l2_fourcc_from_structure (structure)); } -static const GstCaps * +static GstCaps * gst_v4l2src_get_all_caps (void) { static GstCaps *caps = NULL; @@ -664,8 +564,7 @@ gst_v4l2src_get_all_caps (void) gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, 1, 4096, "height", GST_TYPE_INT_RANGE, 1, 4096, - "framerate", GST_TYPE_DOUBLE_RANGE, (double) 0, G_MAXDOUBLE, NULL); - + "framerate", GST_TYPE_FRACTION_RANGE, 1, 1, 100, 1, NULL); gst_caps_append_structure (caps, structure); } } @@ -675,108 +574,30 @@ gst_v4l2src_get_all_caps (void) } static GstCaps * -gst_v4l2src_fixate (GstPad * pad, const GstCaps * const_caps) +gst_v4l2src_get_caps (GstBaseSrc * src) { - gint i; - GstStructure *structure; - G_GNUC_UNUSED gchar *caps_str; - gboolean changed = FALSE; - GstCaps *caps = gst_caps_copy (const_caps); - - caps_str = gst_caps_to_string (caps); - GST_DEBUG_OBJECT (gst_pad_get_parent (pad), "fixating caps %s", caps_str); - g_free (caps_str); - - for (i = 0; i < gst_caps_get_size (caps); i++) { - structure = gst_caps_get_structure (caps, i); - changed |= - gst_structure_fixate_field_nearest_int (structure, "width", G_MAXINT); - } - if (changed) - return caps; - - for (i = 0; i < gst_caps_get_size (caps); i++) { - structure = gst_caps_get_structure (caps, i); - changed |= - gst_structure_fixate_field_nearest_int (structure, "height", G_MAXINT); - } - if (changed) - return caps; - - gst_caps_free (caps); - return NULL; -} - -static GstPadLinkReturn -gst_v4l2src_link (GstPad * pad, const GstCaps * caps) -{ - GstV4l2Src *v4l2src; - GstV4l2Element *v4l2element; - struct v4l2_fmtdesc *format; - int w, h; - GstStructure *structure; - gboolean was_capturing; - - v4l2src = GST_V4L2SRC (gst_pad_get_parent (pad)); - v4l2element = GST_V4L2ELEMENT (v4l2src); - - structure = gst_caps_get_structure (caps, 0); - - /* clean up if we still haven't cleaned up our previous - * capture session */ - if ((was_capturing = v4l2src->is_capturing)) { - if (!gst_v4l2src_capture_stop (v4l2src)) - return GST_PAD_LINK_REFUSED; - } - if (GST_V4L2_IS_ACTIVE (v4l2element)) { - if (!gst_v4l2src_capture_deinit (v4l2src)) - return GST_PAD_LINK_REFUSED; - } else if (!GST_V4L2_IS_OPEN (v4l2element)) { - return GST_PAD_LINK_DELAYED; - } - - /* we want our own v4l2 type of fourcc codes */ - if (!(format = gst_v4l2_caps_to_v4l2fourcc (v4l2src, structure))) { - return GST_PAD_LINK_REFUSED; - } - - gst_structure_get_int (structure, "width", &w); - gst_structure_get_int (structure, "height", &h); - - /* we found the pixelformat! - try it out */ - if (gst_v4l2src_set_capture (v4l2src, format, w, h)) { - if (gst_v4l2src_capture_init (v4l2src)) { - if (was_capturing || GST_STATE (v4l2src) == GST_STATE_PLAYING) - if (!gst_v4l2src_capture_start (v4l2src)) - return GST_PAD_LINK_REFUSED; - - return GST_PAD_LINK_OK; - } - } - - return GST_PAD_LINK_REFUSED; -} - - -static GstCaps * -gst_v4l2src_getcaps (GstPad * pad) -{ - GstV4l2Src *v4l2src = GST_V4L2SRC (gst_pad_get_parent (pad)); + GstV4l2Src *v4l2src = GST_V4L2SRC (src); GstCaps *caps; struct v4l2_fmtdesc *format; int min_w, max_w, min_h, max_h; GSList *walk; GstStructure *structure; - gdouble fps; + gint fps_n, fps_d; + if (!GST_V4L2_IS_OPEN (GST_V4L2ELEMENT (v4l2src))) { - return gst_caps_copy (gst_pad_get_pad_template_caps (pad)); + return + gst_caps_copy (gst_pad_get_pad_template_caps (GST_BASE_SRC_PAD + (v4l2src))); } + if (!v4l2src->formats) + gst_v4l2src_fill_format_list (v4l2src); + /* build our own capslist */ caps = gst_caps_new_empty (); walk = v4l2src->formats; - fps = gst_v4l2src_get_fps (v4l2src); + gst_v4l2src_get_fps (v4l2src, &fps_n, &fps_d); while (walk) { format = (struct v4l2_fmtdesc *) walk->data; walk = g_slist_next (walk); @@ -787,14 +608,10 @@ gst_v4l2src_getcaps (GstPad * pad) continue; } /* template */ - if (min_w < 1) - min_w = 1; - if (min_h < 1) - min_h = 1; - if (max_w > 4096) - max_w = 4096; - if (max_h > 4096) - max_h = 4096; + min_w = CLAMP (min_w, 1, 4096); + min_h = CLAMP (min_h, 1, 4096); + max_w = CLAMP (max_w, min_w, 4096); + max_h = CLAMP (max_h, min_h, 4096); /* add to list */ structure = gst_v4l2src_v4l2fourcc_to_caps (format->pixelformat); @@ -803,7 +620,7 @@ gst_v4l2src_getcaps (GstPad * pad) gst_structure_set (structure, "width", GST_TYPE_INT_RANGE, min_w, max_w, "height", GST_TYPE_INT_RANGE, min_h, max_h, - "framerate", G_TYPE_DOUBLE, fps, NULL); + "framerate", GST_TYPE_FRACTION, fps_n, fps_d, NULL); gst_caps_append_structure (caps, structure); } @@ -812,269 +629,190 @@ gst_v4l2src_getcaps (GstPad * pad) return caps; } -static GstData * -gst_v4l2src_get (GstPad * pad) +static gboolean +gst_v4l2src_set_caps (GstBaseSrc * src, GstCaps * caps) { GstV4l2Src *v4l2src; - GstBuffer *buf; + gint w, h; + GstStructure *structure; + struct v4l2_fmtdesc *format; + + v4l2src = GST_V4L2SRC (src); + + /* if we're not open, punt -- we'll get setcaps'd later via negotiate */ + if (!GST_V4L2_IS_OPEN (v4l2src)) + return FALSE; + + /* make sure we stop capturing and dealloc buffers */ + if (GST_V4L2_IS_ACTIVE (v4l2src)) { + if (!gst_v4l2src_capture_stop (v4l2src)) + return FALSE; + if (!gst_v4l2src_capture_deinit (v4l2src)) + return FALSE; + } + + /* it's fixed, one struct */ + structure = gst_caps_get_structure (caps, 0); + + /* we want our own v4l2 type of fourcc codes */ + if (!(format = gst_v4l2_caps_to_v4l2fourcc (v4l2src, structure))) { + return FALSE; + } + + gst_structure_get_int (structure, "width", &w); + gst_structure_get_int (structure, "height", &h); + + GST_DEBUG_OBJECT (v4l2src, "trying to set_capture %dx%d, format %s", + w, h, format->description); + /* this only fills in v4l2src->mmap values */ + if (!gst_v4l2src_set_capture (v4l2src, format, w, h)) { + GST_WARNING_OBJECT (v4l2src, "could not set_capture %dx%d, format %s", + w, h, format->description); + return FALSE; + } + + if (!gst_v4l2src_capture_init (v4l2src)) + return FALSE; + + if (!gst_v4l2src_capture_start (v4l2src)) + return FALSE; + + return TRUE; +} + +/* start and stop are not symmetric -- start will open the device, but not start + capture. it's setcaps that will start capture, which is called via basesrc's + negotiate method. stop will both stop capture and close the device. +*/ +static gboolean +gst_v4l2src_start (GstBaseSrc * src) +{ + GstV4l2Src *v4l2src = GST_V4L2SRC (src); + + if (!GST_BASE_SRC_CLASS (parent_class)->start (src)) + return FALSE; + + v4l2src->offset = 0; + + return TRUE; +} + +static gboolean +gst_v4l2src_stop (GstBaseSrc * src) +{ + GstV4l2Src *v4l2src = GST_V4L2SRC (src); + + if (GST_V4L2_IS_ACTIVE (v4l2src) && !gst_v4l2src_capture_stop (v4l2src)) + return FALSE; + + if (GST_V4L2ELEMENT (v4l2src)->buffer != NULL) { + if (!gst_v4l2src_capture_deinit (v4l2src)) + return FALSE; + } + + if (!GST_BASE_SRC_CLASS (parent_class)->stop (src)) + return FALSE; + + return TRUE; +} + +static GstFlowReturn +gst_v4l2src_get_read (GstV4l2Src * v4l2src, GstBuffer ** buf) +{ + gint amount; + gint buffersize; + + buffersize = v4l2src->format.fmt.pix.sizeimage; + + do { + *buf = gst_v4l2src_buffer_new (v4l2src, buffersize, NULL, NULL); + GST_BUFFER_OFFSET (*buf) = GST_BUFFER_OFFSET_NONE; + gst_buffer_set_caps (*buf, GST_PAD_CAPS (v4l2src->srcpad)); + + amount = + read (GST_V4L2ELEMENT (v4l2src)->video_fd, GST_BUFFER_DATA (*buf), + buffersize); + if (amount == buffersize) { + break; + } else if (amount == -1) { + if (errno == EAGAIN || errno == EINTR) { + continue; + } else { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), + ("error read()ing a buffer on device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + gst_buffer_unref (*buf); + return GST_FLOW_ERROR; + } + } else { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), + ("error read()ing a buffer on device %s: got only %d bytes instead of expected %d", + GST_V4L2ELEMENT (v4l2src)->videodev, amount, buffersize)); + gst_buffer_unref (*buf); + return GST_FLOW_ERROR; + } + } while (TRUE); + + return GST_FLOW_OK; + +} + + +static GstFlowReturn +gst_v4l2src_get_mmap (GstV4l2Src * v4l2src, GstBuffer ** buf) +{ gint i, num = -1; - gdouble fps = 0; - v4l2src = GST_V4L2SRC (gst_pad_get_parent (pad)); - if (v4l2src->use_fixed_fps && (fps = gst_v4l2src_get_fps (v4l2src)) == 0) { + /* grab a frame from the device */ + num = gst_v4l2src_grab_frame (v4l2src); + if (num == -1) + return GST_FLOW_ERROR; + + i = v4l2src->format.fmt.pix.sizeimage; + + /* check if this is the last buffer in the queue. If so do a memcpy to put it back asap + to avoid framedrops and deadlocks because of stupid elements */ + if (g_atomic_int_get (&v4l2src->pool->refcount) == v4l2src->breq.count) { + GST_LOG_OBJECT (v4l2src, "using memcpy'd buffer"); + *buf = gst_v4l2src_buffer_new (v4l2src, i, NULL, NULL); + memcpy (GST_BUFFER_DATA (*buf), v4l2src->pool->buffers[num].start, i); + if (!gst_v4l2src_queue_frame (v4l2src, num)) { + gst_buffer_unref (*buf); + return GST_FLOW_ERROR; + } + } else { + GST_LOG_OBJECT (v4l2src, "using mmap'd buffer"); + *buf = + gst_v4l2src_buffer_new (v4l2src, i, v4l2src->pool->buffers[num].start, + &v4l2src->pool->buffers[num]); + /* no need to be careful here, both are > 0, because the element uses them */ + g_atomic_int_inc (&v4l2src->pool->buffers[num].refcount); + g_atomic_int_inc (&v4l2src->pool->refcount); + } + + return GST_FLOW_OK; +} + +static GstFlowReturn +gst_v4l2src_create (GstPushSrc * src, GstBuffer ** buf) +{ + GstV4l2Src *v4l2src = GST_V4L2SRC (src); + GstFlowReturn ret; + gint fps_n, fps_d; + + if (v4l2src->use_fixed_fps + && gst_v4l2src_get_fps (v4l2src, &fps_n, &fps_d) == 0) { GST_ELEMENT_ERROR (v4l2src, RESOURCE, SETTINGS, (NULL), ("could not get frame rate for element")); - return NULL; + return GST_FLOW_ERROR; } - if (v4l2src->need_writes > 0) { - /* use last frame */ - buf = v4l2src->cached_buffer; - v4l2src->need_writes--; + if (v4l2src->breq.memory == V4L2_MEMORY_MMAP) { + ret = gst_v4l2src_get_mmap (v4l2src, buf); } else { - GstClockTime time; - - /* grab a frame from the device */ - num = gst_v4l2src_grab_frame (v4l2src); - if (num == -1) - return NULL; - - /* to check if v4l2 sets the correct time */ - time = GST_TIMEVAL_TO_TIME (v4l2src->pool->buffers[num].buffer.timestamp); - if (v4l2src->clock && v4l2src->use_fixed_fps && time != 0) { - gboolean have_frame = FALSE; - - do { - /* FIXME: isn't this v4l2 timestamp its own clock?! */ - /* by default, we use the frame once */ - v4l2src->need_writes = 1; - - g_assert (time >= v4l2src->substract_time); - time -= v4l2src->substract_time; - - /* first check whether we lost any frames according to the device */ - if (v4l2src->last_seq != 0) { - if (v4l2src->pool->buffers[num].buffer.sequence - v4l2src->last_seq > - 1) { - v4l2src->need_writes = - v4l2src->pool->buffers[num].buffer.sequence - v4l2src->last_seq; - g_signal_emit (G_OBJECT (v4l2src), - gst_v4l2src_signals[SIGNAL_FRAME_LOST], 0, - v4l2src->need_writes - 1); - } - } - v4l2src->last_seq = v4l2src->pool->buffers[num].buffer.sequence; - - /* decide how often we're going to write the frame - set - * v4lmjpegsrc->need_writes to (that-1) and have_frame to TRUE - * if we're going to write it - else, just continue. - * - * time is generally the system or audio clock. Let's - * say that we've written one second of audio, then we want - * to have written one second of video too, within the same - * timeframe. This means that if time - begin_time = X sec, - * we want to have written X*fps frames. If we've written - * more - drop, if we've written less - dup... */ - if (v4l2src->handled * (GST_SECOND / fps) - time > - 1.5 * (GST_SECOND / fps)) { - /* yo dude, we've got too many frames here! Drop! DROP! */ - v4l2src->need_writes--; /* -= (v4l2src->handled - (time / fps)); */ - g_signal_emit (G_OBJECT (v4l2src), - gst_v4l2src_signals[SIGNAL_FRAME_DROP], 0); - } else if (v4l2src->handled * (GST_SECOND / fps) - time < - -1.5 * (GST_SECOND / fps)) { - /* this means we're lagging far behind */ - v4l2src->need_writes++; /* += ((time / fps) - v4l2src->handled); */ - g_signal_emit (G_OBJECT (v4l2src), - gst_v4l2src_signals[SIGNAL_FRAME_INSERT], 0); - } - - if (v4l2src->need_writes > 0) { - have_frame = TRUE; - v4l2src->need_writes--; - } else { - if (!gst_v4l2src_queue_frame (v4l2src, num)) - return NULL; - num = gst_v4l2src_grab_frame (v4l2src); - if (num == -1) - return NULL; - } - } while (!have_frame); - } - - g_assert (num != -1); - GST_LOG_OBJECT (v4l2src, "buffer %d needs %d writes", num, - v4l2src->need_writes + 1); - i = v4l2src->pool->buffers[num].buffer.bytesused > - 0 ? v4l2src->pool->buffers[num].buffer.bytesused : v4l2src->pool-> - buffers[num].length; - /* check if this is the last buffer in the queue. If so do a memcpy to put it back asap - to avoid framedrops and deadlocks because of stupid elements */ - if (gst_atomic_int_read (&v4l2src->pool->refcount) == v4l2src->breq.count) { - GST_LOG_OBJECT (v4l2src, "using memcpy'd buffer"); - buf = gst_buffer_new_and_alloc (i); - memcpy (GST_BUFFER_DATA (buf), v4l2src->pool->buffers[num].start, i); - if (!gst_v4l2src_queue_frame (v4l2src, num)) { - gst_data_unref (GST_DATA (buf)); - return NULL; - } - } else { - GST_LOG_OBJECT (v4l2src, "using mmap'd buffer"); - buf = gst_buffer_new (); - GST_BUFFER_DATA (buf) = v4l2src->pool->buffers[num].start; - GST_BUFFER_SIZE (buf) = i; - GST_BUFFER_FREE_DATA_FUNC (buf) = gst_v4l2src_free_buffer; - GST_BUFFER_PRIVATE (buf) = &v4l2src->pool->buffers[num]; - /* no need to be careful here, both are > 0, because the element uses them */ - gst_atomic_int_inc (&v4l2src->pool->buffers[num].refcount); - gst_atomic_int_inc (&v4l2src->pool->refcount); - } - GST_BUFFER_MAXSIZE (buf) = v4l2src->pool->buffers[num].length; - if (v4l2src->use_fixed_fps) { - GST_BUFFER_TIMESTAMP (buf) = v4l2src->handled * GST_SECOND / fps; - GST_BUFFER_DURATION (buf) = GST_SECOND / fps; - } else { - /* calculate time based on our own clock */ - GST_BUFFER_TIMESTAMP (buf) = - GST_TIMEVAL_TO_TIME (v4l2src->pool->buffers[num].buffer.timestamp) - - v4l2src->substract_time; - } - if (v4l2src->need_writes > 0) { - v4l2src->cached_buffer = buf; - for (i = 0; i < v4l2src->need_writes; i++) { - gst_data_ref (GST_DATA (buf)); - } - } + ret = gst_v4l2src_get_read (v4l2src, buf); } - v4l2src->handled++; - g_signal_emit (G_OBJECT (v4l2src), gst_v4l2src_signals[SIGNAL_FRAME_CAPTURE], - 0); - - return GST_DATA (buf); -} - -static void -gst_v4l2src_set_property (GObject * object, - guint prop_id, const GValue * value, GParamSpec * pspec) -{ - GstV4l2Src *v4l2src; - - g_return_if_fail (GST_IS_V4L2SRC (object)); - v4l2src = GST_V4L2SRC (object); - - switch (prop_id) { - case ARG_NUMBUFS: - if (!GST_V4L2_IS_ACTIVE (GST_V4L2ELEMENT (v4l2src))) { - v4l2src->breq.count = g_value_get_int (value); - } - break; - - case ARG_USE_FIXED_FPS: - if (!GST_V4L2_IS_ACTIVE (GST_V4L2ELEMENT (v4l2src))) { - v4l2src->use_fixed_fps = g_value_get_boolean (value); - } - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static void -gst_v4l2src_get_property (GObject * object, - guint prop_id, GValue * value, GParamSpec * pspec) -{ - GstV4l2Src *v4l2src; - - g_return_if_fail (GST_IS_V4L2SRC (object)); - v4l2src = GST_V4L2SRC (object); - - switch (prop_id) { - case ARG_NUMBUFS: - g_value_set_int (value, v4l2src->breq.count); - break; - - case ARG_BUFSIZE: - g_value_set_int (value, v4l2src->format.fmt.pix.sizeimage); - break; - - case ARG_USE_FIXED_FPS: - g_value_set_boolean (value, v4l2src->use_fixed_fps); - break; - - default: - G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); - break; - } -} - - -static GstStateChangeReturn -gst_v4l2src_change_state (GstElement * element, GstStateChange transition) -{ - GstV4l2Src *v4l2src; - GstStateChangeReturn parent_return; - GTimeVal time; - - g_return_val_if_fail (GST_IS_V4L2SRC (element), GST_STATE_CHANGE_FAILURE); - v4l2src = GST_V4L2SRC (element); - - if (GST_ELEMENT_CLASS (parent_class)->change_state) { - parent_return = - GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); - if (parent_return != GST_STATE_CHANGE_SUCCESS) - return parent_return; - } - - switch (transition) { - case GST_STATE_CHANGE_NULL_TO_READY: - if (!gst_v4l2src_get_capture (v4l2src)) - return GST_STATE_CHANGE_FAILURE; - break; - case GST_STATE_CHANGE_READY_TO_PAUSED: - v4l2src->handled = 0; - v4l2src->need_writes = 0; - v4l2src->substract_time = 0; - /* buffer setup moved to capsnego */ - break; - case GST_STATE_CHANGE_PAUSED_TO_PLAYING: - /* queue all buffer, start streaming capture */ - if (GST_V4L2ELEMENT (v4l2src)->buffer && - !gst_v4l2src_capture_start (v4l2src)) - return GST_STATE_CHANGE_FAILURE; - g_get_current_time (&time); - v4l2src->substract_time = GST_TIMEVAL_TO_TIME (time) - - v4l2src->substract_time; - v4l2src->last_seq = 0; - break; - case GST_STATE_CHANGE_PLAYING_TO_PAUSED: - g_get_current_time (&time); - v4l2src->substract_time = GST_TIMEVAL_TO_TIME (time) - - v4l2src->substract_time; - /* de-queue all queued buffers */ - if (v4l2src->is_capturing && !gst_v4l2src_capture_stop (v4l2src)) - return GST_STATE_CHANGE_FAILURE; - break; - case GST_STATE_CHANGE_PAUSED_TO_READY: - /* stop capturing, unmap all buffers */ - if (GST_V4L2ELEMENT (v4l2src)->buffer && - !gst_v4l2src_capture_deinit (v4l2src)) - return GST_STATE_CHANGE_FAILURE; - break; - case GST_STATE_CHANGE_READY_TO_NULL: - break; - } - - return GST_STATE_CHANGE_SUCCESS; -} - - -static void -gst_v4l2src_set_clock (GstElement * element, GstClock * clock) -{ - GST_V4L2SRC (element)->clock = clock; + return ret; } diff --git a/sys/v4l2/gstv4l2src.h b/sys/v4l2/gstv4l2src.h index c5aac389cf..12c3cd44ea 100644 --- a/sys/v4l2/gstv4l2src.h +++ b/sys/v4l2/gstv4l2src.h @@ -1,5 +1,8 @@ -/* G-Streamer Video4linux2 video-capture plugin - * Copyright (C) 2002 Ronald Bultje +/* GStreamer + * + * gstv4l2src.h: BT8x8/V4L2 video source element + * + * Copyright (C) 2001-2002 Ronald Bultje * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public @@ -20,6 +23,7 @@ #ifndef __GST_V4L2SRC_H__ #define __GST_V4L2SRC_H__ + #include GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug); @@ -27,83 +31,86 @@ GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug); #define GST_V4L2_MAX_BUFFERS 16 #define GST_V4L2_MIN_BUFFERS 2 -#define GST_TYPE_V4L2SRC \ - (gst_v4l2src_get_type()) -#define GST_V4L2SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2SRC,GstV4l2Src)) -#define GST_V4L2SRC_CLASS(klass) \ - (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2SRC,GstV4l2SrcClass)) -#define GST_IS_V4L2SRC(obj) \ - (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2SRC)) -#define GST_IS_V4L2SRC_CLASS(obj) \ - (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2SRC)) +G_BEGIN_DECLS + +#define GST_TYPE_V4L2SRC \ + (gst_v4l2src_get_type()) +#define GST_V4L2SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_V4L2SRC,GstV4l2Src)) +#define GST_V4L2SRC_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_V4L2SRC,GstV4l2SrcClass)) +#define GST_IS_V4L2SRC(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_V4L2SRC)) +#define GST_IS_V4L2SRC_CLASS(obj) \ + (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_V4L2SRC)) + + +typedef struct _GstV4l2BufferPool GstV4l2BufferPool; +typedef struct _GstV4l2Buffer GstV4l2Buffer; +typedef struct _GstV4l2Src GstV4l2Src; +typedef struct _GstV4l2SrcClass GstV4l2SrcClass; -typedef struct _GstV4l2BufferPool GstV4l2BufferPool; -typedef struct _GstV4l2Buffer GstV4l2Buffer; -typedef struct _GstV4l2Src GstV4l2Src; -typedef struct _GstV4l2SrcClass GstV4l2SrcClass; /* global info */ struct _GstV4l2BufferPool { - GstAtomicInt refcount; /* number of users: 1 for every buffer, 1 for element */ - gint video_fd; - guint buffer_count; - GstV4l2Buffer * buffers; + gint refcount; /* number of users: 1 for every buffer, 1 for element */ + gint video_fd; + guint buffer_count; + GstV4l2Buffer * buffers; }; struct _GstV4l2Buffer { - struct v4l2_buffer buffer; - guint8 * start; - guint length; - GstAtomicInt refcount; /* add 1 if in use by element, add 1 if in use by GstBuffer */ - GstV4l2BufferPool * pool; + struct v4l2_buffer buffer; + guint8 * start; + guint length; + gint refcount; /* add 1 if in use by element, add 1 if in use by GstBuffer */ + GstV4l2BufferPool * pool; }; -struct _GstV4l2Src { - GstV4l2Element v4l2element; +enum + { + QUEUE_STATE_ERROR = -1, + QUEUE_STATE_READY_FOR_QUEUE, /* the frame is ready to be queued for capture */ + QUEUE_STATE_QUEUED, /* the frame is queued for capture */ + QUEUE_STATE_SYNCED /* the frame is captured */ + }; - /* pads */ - GstPad *srcpad; - /* internal lists */ - GSList *formats; /* list of available capture formats */ +struct _GstV4l2Src +{ + GstV4l2Element v4l2element; - /* buffers */ - GstV4l2BufferPool *pool; + /* pads */ + GstPad *srcpad; - struct v4l2_requestbuffers breq; - struct v4l2_format format; + /* internal lists */ + GSList *formats; /* list of available capture formats */ - /* True if we want to stop */ - gboolean quit, is_capturing; + /* buffers */ + GstV4l2BufferPool *pool; - /* A/V sync... frame counter and internal cache */ - gulong handled; - gint need_writes; - GstBuffer *cached_buffer; - gulong last_seq; + struct v4l2_requestbuffers breq; + struct v4l2_format format; - /* clock */ - GstClock *clock; - - /* time to substract from clock time to get back to timestamp */ - GstClockTime substract_time; + /* True if we want to stop */ + gboolean quit, is_capturing; - /* how are we going to push buffers? */ - gboolean use_fixed_fps; + gint offset; + + /* how are we going to push buffers? */ + gboolean use_fixed_fps; }; -struct _GstV4l2SrcClass { - GstV4l2ElementClass parent_class; - - void (*frame_capture) (GObject *object); - void (*frame_drop) (GObject *object); - void (*frame_insert) (GObject *object); - void (*frame_lost) (GObject *object, - gint num_lost); +struct _GstV4l2SrcClass +{ + GstV4l2ElementClass parent_class; }; -GType gst_v4l2src_get_type(void); +GType gst_v4l2src_get_type (void); + + +G_END_DECLS + #endif /* __GST_V4L2SRC_H__ */ diff --git a/sys/v4l2/gstv4l2tuner.c b/sys/v4l2/gstv4l2tuner.c index 353cbb1c3b..9fb4e3cd70 100644 --- a/sys/v4l2/gstv4l2tuner.c +++ b/sys/v4l2/gstv4l2tuner.c @@ -24,7 +24,6 @@ #endif #include -#include #include "gstv4l2tuner.h" #include "gstv4l2element.h" @@ -156,13 +155,8 @@ gst_v4l2_tuner_interface_init (GstTunerClass * klass) static gboolean gst_v4l2_tuner_is_sink (GstV4l2Element * v4l2element) { - const GList *pads = gst_element_get_pad_list (GST_ELEMENT (v4l2element)); GstPadDirection dir = GST_PAD_UNKNOWN; - /* get direction */ - if (pads && g_list_length ((GList *) pads) == 1) - dir = GST_PAD_DIRECTION (GST_PAD (pads->data)); - return (dir == GST_PAD_SINK); } @@ -172,7 +166,7 @@ gst_v4l2_tuner_contains_channel (GstV4l2Element * v4l2element, { const GList *item; - for (item = v4l2element->channels; item != NULL; item = item->next) + for (item = v4l2element->inputs; item != NULL; item = item->next) if (item->data == v4l2channel) return TRUE; @@ -183,7 +177,7 @@ static const GList * gst_v4l2_tuner_list_channels (GstTuner * mixer) { /* ... or output, if we're a sink... */ - return GST_V4L2ELEMENT (mixer)->channels; + return GST_V4L2ELEMENT (mixer)->inputs; } static void @@ -201,7 +195,7 @@ gst_v4l2_tuner_set_channel (GstTuner * mixer, GstTunerChannel * channel) gst_v4l2_set_output (v4l2element, v4l2channel->index) : gst_v4l2_set_input (v4l2element, v4l2channel->index)) { gst_tuner_channel_changed (mixer, channel); - g_object_notify (G_OBJECT (v4l2element), "channel"); + g_object_notify (G_OBJECT (v4l2element), "input"); } } @@ -221,7 +215,7 @@ gst_v4l2_tuner_get_channel (GstTuner * mixer) else gst_v4l2_get_input (v4l2element, &channel); - for (item = v4l2element->channels; item != NULL; item = item->next) { + for (item = v4l2element->inputs; item != NULL; item = item->next) { if (channel == GST_V4L2_TUNER_CHANNEL (item->data)->index) return (GstTunerChannel *) item->data; } @@ -235,7 +229,7 @@ gst_v4l2_tuner_contains_norm (GstV4l2Element * v4l2element, { const GList *item; - for (item = v4l2element->norms; item != NULL; item = item->next) + for (item = v4l2element->stds; item != NULL; item = item->next) if (item->data == v4l2norm) return TRUE; @@ -245,7 +239,7 @@ gst_v4l2_tuner_contains_norm (GstV4l2Element * v4l2element, static const GList * gst_v4l2_tuner_list_norms (GstTuner * mixer) { - return GST_V4L2ELEMENT (mixer)->norms; + return GST_V4L2ELEMENT (mixer)->stds; } static void @@ -260,7 +254,7 @@ gst_v4l2_tuner_set_norm (GstTuner * mixer, GstTunerNorm * norm) if (gst_v4l2_set_norm (v4l2element, v4l2norm->index)) { gst_tuner_norm_changed (mixer, norm); - g_object_notify (G_OBJECT (v4l2element), "norm"); + g_object_notify (G_OBJECT (v4l2element), "std"); } } @@ -276,7 +270,7 @@ gst_v4l2_tuner_get_norm (GstTuner * mixer) gst_v4l2_get_norm (v4l2element, &norm); - for (item = v4l2element->norms; item != NULL; item = item->next) { + for (item = v4l2element->stds; item != NULL; item = item->next) { if (norm == GST_V4L2_TUNER_NORM (item->data)->index) return (GstTunerNorm *) item->data; } diff --git a/sys/v4l2/gstv4l2tuner.h b/sys/v4l2/gstv4l2tuner.h index c51bf1967f..a7a5f43510 100644 --- a/sys/v4l2/gstv4l2tuner.h +++ b/sys/v4l2/gstv4l2tuner.h @@ -23,7 +23,7 @@ #define __GST_V4L2_TUNER_H__ #include -#include +#include #include "gstv4l2element.h" @@ -33,10 +33,10 @@ G_BEGIN_DECLS (gst_v4l2_tuner_channel_get_type ()) #define GST_V4L2_TUNER_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_TUNER_CHANNEL, \ - GstV4l2TunerChannel)) + GstV4l2TunerChannel)) #define GST_V4L2_TUNER_CHANNEL_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_V4L2_TUNER_CHANNEL, \ - GstV4l2TunerChannelClass)) + GstV4l2TunerChannelClass)) #define GST_IS_V4L2_TUNER_CHANNEL(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_TUNER_CHANNEL)) #define GST_IS_V4L2_TUNER_CHANNEL_CLASS(klass) \ @@ -58,10 +58,10 @@ typedef struct _GstV4l2TunerChannelClass { (gst_v4l2_tuner_norm_get_type ()) #define GST_V4L2_TUNER_NORM(obj) \ (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2_TUNER_NORM, \ - GstV4l2TunerNorm)) + GstV4l2TunerNorm)) #define GST_V4L2_TUNER_NORM_CLASS(klass) \ (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_V4L2_TUNER_NORM, \ - GstV4l2TunerNormClass)) + GstV4l2TunerNormClass)) #define GST_IS_V4L2_TUNER_NORM(obj) \ (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2_TUNER_NORM)) #define GST_IS_V4L2_TUNER_NORM_CLASS(klass) \ @@ -77,9 +77,9 @@ typedef struct _GstV4l2TunerNormClass { GstTunerNormClass parent; } GstV4l2TunerNormClass; -GType gst_v4l2_tuner_channel_get_type (void); -GType gst_v4l2_tuner_norm_get_type (void); +GType gst_v4l2_tuner_channel_get_type (void); +GType gst_v4l2_tuner_norm_get_type (void); -void gst_v4l2_tuner_interface_init (GstTunerClass *klass); +void gst_v4l2_tuner_interface_init (GstTunerClass *klass); #endif /* __GST_V4L2_TUNER_H__ */ diff --git a/sys/v4l2/gstv4l2xoverlay.c b/sys/v4l2/gstv4l2xoverlay.c index 2e416b3eda..66ea505c3a 100644 --- a/sys/v4l2/gstv4l2xoverlay.c +++ b/sys/v4l2/gstv4l2xoverlay.c @@ -25,7 +25,6 @@ #include #include -#include #include #include #include @@ -59,13 +58,14 @@ gst_v4l2_xoverlay_interface_init (GstXOverlayClass * klass) "V4L2 XOverlay interface debugging"); } -void +static void gst_v4l2_xoverlay_open (GstV4l2Element * v4l2element) { struct stat s; GstV4l2Xv *v4l2xv; const gchar *name = g_getenv ("DISPLAY"); - int ver, rel, req, ev, err, anum, i, id = 0, first_id = 0, min; + unsigned int ver, rel, req, ev, err, anum; + int i, id = 0, first_id = 0, min; XvAdaptorInfo *ai; Display *dpy; @@ -75,6 +75,13 @@ gst_v4l2_xoverlay_open (GstV4l2Element * v4l2element) return; } + /* First let's check that XVideo extension is available */ + if (!XQueryExtension (dpy, "XVideo", &i, &i, &i)) { + GST_WARNING ("Xv extension not available - no overlay"); + XCloseDisplay (dpy); + return; + } + /* find port that belongs to this device */ if (XvQueryExtension (dpy, &ver, &rel, &req, &ev, &err) != Success) { GST_WARNING ("Xv extension not supported - no overlay"); @@ -93,7 +100,7 @@ gst_v4l2_xoverlay_open (GstV4l2Element * v4l2element) } min = s.st_rdev & 0xff; for (i = 0; i < anum; i++) { - if (!strcmp (ai[i].name, "video4linux")) { + if (!strcmp (ai[i].name, "video4linux2")) { if (first_id == 0) first_id = ai[i].base_id; @@ -123,7 +130,7 @@ gst_v4l2_xoverlay_open (GstV4l2Element * v4l2element) } } -void +static void gst_v4l2_xoverlay_close (GstV4l2Element * v4l2element) { GstV4l2Xv *v4l2xv = v4l2element->xv; @@ -143,6 +150,20 @@ gst_v4l2_xoverlay_close (GstV4l2Element * v4l2element) v4l2element->xv = NULL; } +void +gst_v4l2_xoverlay_start (GstV4l2Element * v4l2element) +{ + if (v4l2element->xwindow_id) { + gst_v4l2_xoverlay_open (v4l2element); + } +} + +void +gst_v4l2_xoverlay_stop (GstV4l2Element * v4l2element) +{ + gst_v4l2_xoverlay_close (v4l2element); +} + static gboolean idle_refresh (gpointer data) { @@ -170,12 +191,17 @@ static void gst_v4l2_xoverlay_set_xwindow_id (GstXOverlay * overlay, XID xwindow_id) { GstV4l2Element *v4l2element = GST_V4L2ELEMENT (overlay); - GstV4l2Xv *v4l2xv = v4l2element->xv; + GstV4l2Xv *v4l2xv; XWindowAttributes attr; gboolean change = (v4l2element->xwindow_id != xwindow_id); GST_LOG_OBJECT (v4l2element, "Setting XID to %lx", (gulong) xwindow_id); + if (!v4l2element->xv && GST_V4L2_IS_OPEN (v4l2element)) + gst_v4l2_xoverlay_open (v4l2element); + + v4l2xv = v4l2element->xv; + if (v4l2xv) g_mutex_lock (v4l2xv->mutex); diff --git a/sys/v4l2/gstv4l2xoverlay.h b/sys/v4l2/gstv4l2xoverlay.h index a1bf5d7ddf..a0c25abc90 100644 --- a/sys/v4l2/gstv4l2xoverlay.h +++ b/sys/v4l2/gstv4l2xoverlay.h @@ -23,15 +23,15 @@ #define __GST_V4L2_X_OVERLAY_H__ #include -#include +#include #include "gstv4l2element.h" G_BEGIN_DECLS -void gst_v4l2_xoverlay_interface_init (GstXOverlayClass *klass); +void gst_v4l2_xoverlay_interface_init (GstXOverlayClass *klass); -void gst_v4l2_xoverlay_open (GstV4l2Element *v4l2element); -void gst_v4l2_xoverlay_close (GstV4l2Element *v4l2element); +void gst_v4l2_xoverlay_start (GstV4l2Element *v4l2element); +void gst_v4l2_xoverlay_stop (GstV4l2Element *v4l2element); #endif /* __GST_V4L2_X_OVERLAY_H__ */ diff --git a/sys/v4l2/v4l2_calls.c b/sys/v4l2/v4l2_calls.c index b4b16d97f4..a89122f48a 100644 --- a/sys/v4l2/v4l2_calls.c +++ b/sys/v4l2/v4l2_calls.c @@ -36,11 +36,8 @@ #include "gstv4l2src.h" -#define DEBUG(format, args...) \ - GST_DEBUG_OBJECT (\ - GST_ELEMENT(v4l2element), \ - "V4L2: " format, ##args) - +GST_DEBUG_CATEGORY_EXTERN (v4l2_debug); +#define GST_CAT_DEFAULT v4l2_debug /****************************************************** * gst_v4l2_get_capabilities(): @@ -48,16 +45,16 @@ * return value: TRUE on success, FALSE on error ******************************************************/ -static gboolean +gboolean gst_v4l2_get_capabilities (GstV4l2Element * v4l2element) { - DEBUG ("getting capabilities"); - GST_V4L2_CHECK_OPEN (v4l2element); + GST_DEBUG ("getting capabilities"); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; if (ioctl (v4l2element->video_fd, VIDIOC_QUERYCAP, &(v4l2element->vcap)) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Error getting %s capabilities: %s", - v4l2element->device, g_strerror (errno))); + GST_ERROR_OBJECT (v4l2element, "Error getting %s capabilities: %s", + v4l2element->videodev, g_strerror (errno)); return FALSE; } @@ -75,16 +72,11 @@ static gboolean gst_v4l2_fill_lists (GstV4l2Element * v4l2element) { gint n; - const GList *pads = gst_element_get_pad_list (GST_ELEMENT (v4l2element)); GstPadDirection dir = GST_PAD_UNKNOWN; - DEBUG ("getting enumerations"); + GST_DEBUG ("getting enumerations"); GST_V4L2_CHECK_OPEN (v4l2element); - /* sinks have outputs, all others have inputs */ - if (pads && g_list_length ((GList *) pads) == 1) - dir = GST_PAD_DIRECTION (GST_PAD (pads->data)); - if (dir != GST_PAD_SINK) { /* and now, the inputs */ for (n = 0;; n++) { @@ -99,14 +91,14 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) else { GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), ("Failed to get %d in input enumeration for %s: %s", - n, v4l2element->device, g_strerror (errno))); + n, v4l2element->videodev, g_strerror (errno))); return FALSE; } } v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL); channel = GST_TUNER_CHANNEL (v4l2channel); - channel->label = g_strdup (input.name); + channel->label = g_strdup ((const gchar *) input.name); channel->flags = GST_TUNER_CHANNEL_INPUT; v4l2channel->index = n; if (input.type == V4L2_INPUT_TYPE_TUNER) { @@ -119,7 +111,7 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) if (ioctl (v4l2element->video_fd, VIDIOC_G_TUNER, &vtun) < 0) { GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), ("Failed to get tuner %d settings on %s: %s", - input.tuner, v4l2element->device, g_strerror (errno))); + input.tuner, v4l2element->videodev, g_strerror (errno))); g_object_unref (G_OBJECT (channel)); return FALSE; } @@ -138,8 +130,8 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) channel->flags |= GST_TUNER_CHANNEL_AUDIO; } - v4l2element->channels = - g_list_append (v4l2element->channels, (gpointer) channel); + v4l2element->inputs = + g_list_append (v4l2element->inputs, (gpointer) channel); } } else { /* outputs */ @@ -155,14 +147,14 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) else { GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), ("Failed to get %d in output enumeration for %s: %s", - n, v4l2element->device, g_strerror (errno))); + n, v4l2element->videodev, g_strerror (errno))); return FALSE; } } v4l2channel = g_object_new (GST_TYPE_V4L2_TUNER_CHANNEL, NULL); channel = GST_TUNER_CHANNEL (v4l2channel); - channel->label = g_strdup (output.name); + channel->label = g_strdup ((const gchar *) output.name); channel->flags = GST_TUNER_CHANNEL_OUTPUT; v4l2channel->index = n; if (output.audioset) { @@ -173,8 +165,8 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) channel->flags |= GST_TUNER_CHANNEL_AUDIO; } - v4l2element->channels = - g_list_append (v4l2element->channels, (gpointer) channel); + v4l2element->inputs = + g_list_append (v4l2element->inputs, (gpointer) channel); } } @@ -191,19 +183,19 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) else { GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), ("Failed to get %d in norm enumeration for %s: %s", - n, v4l2element->device, g_strerror (errno))); + n, v4l2element->videodev, g_strerror (errno))); return FALSE; } } v4l2norm = g_object_new (GST_TYPE_V4L2_TUNER_NORM, NULL); norm = GST_TUNER_NORM (v4l2norm); - norm->label = g_strdup (standard.name); - norm->fps = (gfloat) standard.frameperiod.denominator / - standard.frameperiod.numerator; + norm->label = g_strdup ((const gchar *) standard.name); + gst_value_set_fraction (&norm->framerate, + standard.frameperiod.denominator, standard.frameperiod.numerator); v4l2norm->index = standard.id; - v4l2element->norms = g_list_append (v4l2element->norms, (gpointer) norm); + v4l2element->stds = g_list_append (v4l2element->stds, (gpointer) norm); } /* and lastly, controls+menus (if appropriate) */ @@ -226,7 +218,7 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) } else { GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), ("Failed to get %d in control enumeration for %s: %s", - n, v4l2element->device, g_strerror (errno))); + n, v4l2element->videodev, g_strerror (errno))); return FALSE; } } @@ -250,7 +242,7 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) /* we only handle these for now */ break; default: - DEBUG ("ControlID %s (%d) unhandled, FIXME", control.name, n); + GST_DEBUG ("ControlID %s (%d) unhandled, FIXME", control.name, n); control.id++; break; } @@ -259,7 +251,7 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) v4l2channel = g_object_new (GST_TYPE_V4L2_COLOR_BALANCE_CHANNEL, NULL); channel = GST_COLOR_BALANCE_CHANNEL (v4l2channel); - channel->label = g_strdup (control.name); + channel->label = g_strdup ((const gchar *) control.name); v4l2channel->index = n; #if 0 @@ -276,7 +268,7 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) else { GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), ("Failed to get %d in menu enumeration for %s: %s", - n, v4l2element->device, g_strerror (errno))); + n, v4l2element->videodev, g_strerror (errno))); return FALSE; } } @@ -313,15 +305,15 @@ gst_v4l2_fill_lists (GstV4l2Element * v4l2element) static void gst_v4l2_empty_lists (GstV4l2Element * v4l2element) { - DEBUG ("deleting enumerations"); + GST_DEBUG ("deleting enumerations"); - g_list_foreach (v4l2element->channels, (GFunc) g_object_unref, NULL); - g_list_free (v4l2element->channels); - v4l2element->channels = NULL; + g_list_foreach (v4l2element->inputs, (GFunc) g_object_unref, NULL); + g_list_free (v4l2element->inputs); + v4l2element->inputs = NULL; - g_list_foreach (v4l2element->norms, (GFunc) g_object_unref, NULL); - g_list_free (v4l2element->norms); - v4l2element->norms = NULL; + g_list_foreach (v4l2element->stds, (GFunc) g_object_unref, NULL); + g_list_free (v4l2element->stds); + v4l2element->stds = NULL; g_list_foreach (v4l2element->colors, (GFunc) g_object_unref, NULL); g_list_free (v4l2element->colors); @@ -337,27 +329,27 @@ gst_v4l2_set_defaults (GstV4l2Element * v4l2element) GstTunerChannel *channel = NULL; GstTuner *tuner = GST_TUNER (v4l2element); - if (v4l2element->norm) - norm = gst_tuner_find_norm_by_name (tuner, v4l2element->norm); + if (v4l2element->std) + norm = gst_tuner_find_norm_by_name (tuner, v4l2element->std); if (norm) { gst_tuner_set_norm (tuner, norm); } else { norm = GST_TUNER_NORM (gst_tuner_get_norm (GST_TUNER (v4l2element))); - v4l2element->norm = g_strdup (norm->label); + v4l2element->std = g_strdup (norm->label); gst_tuner_norm_changed (tuner, norm); - g_object_notify (G_OBJECT (v4l2element), "norm"); + g_object_notify (G_OBJECT (v4l2element), "std"); } - if (v4l2element->channel) - channel = gst_tuner_find_channel_by_name (tuner, v4l2element->channel); + if (v4l2element->input) + channel = gst_tuner_find_channel_by_name (tuner, v4l2element->input); if (channel) { gst_tuner_set_channel (tuner, channel); } else { channel = GST_TUNER_CHANNEL (gst_tuner_get_channel (GST_TUNER (v4l2element))); - v4l2element->channel = g_strdup (channel->label); + v4l2element->input = g_strdup (channel->label); gst_tuner_channel_changed (tuner, channel); - g_object_notify (G_OBJECT (v4l2element), "channel"); + g_object_notify (G_OBJECT (v4l2element), "input"); } if (GST_TUNER_CHANNEL_HAS_FLAG (channel, GST_TUNER_CHANNEL_FREQUENCY)) { @@ -378,27 +370,27 @@ gst_v4l2_set_defaults (GstV4l2Element * v4l2element) /****************************************************** * gst_v4l2_open(): - * open the video device (v4l2element->device) + * open the video device (v4l2element->videodev) * return value: TRUE on success, FALSE on error ******************************************************/ gboolean gst_v4l2_open (GstV4l2Element * v4l2element) { - DEBUG ("Trying to open device %s", v4l2element->device); + GST_DEBUG ("Trying to open device %s", v4l2element->videodev); GST_V4L2_CHECK_NOT_OPEN (v4l2element); GST_V4L2_CHECK_NOT_ACTIVE (v4l2element); /* be sure we have a device */ - if (!v4l2element->device) - v4l2element->device = g_strdup ("/dev/video"); + if (!v4l2element->videodev) + v4l2element->videodev = g_strdup ("/dev/video"); /* open the device */ - v4l2element->video_fd = open (v4l2element->device, O_RDWR); + v4l2element->video_fd = open (v4l2element->videodev, O_RDWR); if (!GST_V4L2_IS_OPEN (v4l2element)) { GST_ELEMENT_ERROR (v4l2element, RESOURCE, OPEN_READ_WRITE, (_("Could not open device \"%s\" for reading and writing."), - v4l2element->device), GST_ERROR_SYSTEM); + v4l2element->videodev), GST_ERROR_SYSTEM); goto error; } @@ -411,7 +403,7 @@ gst_v4l2_open (GstV4l2Element * v4l2element) if (GST_IS_V4L2SRC (v4l2element) && !(v4l2element->vcap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) { GST_ELEMENT_ERROR (v4l2element, RESOURCE, NOT_FOUND, - (_("Device \"%s\" is not a capture device."), v4l2element->device), + (_("Device \"%s\" is not a capture device."), v4l2element->videodev), ("Capabilities: 0x%x", v4l2element->vcap.capabilities)); goto error; } @@ -424,7 +416,7 @@ gst_v4l2_open (GstV4l2Element * v4l2element) gst_v4l2_set_defaults (v4l2element); GST_INFO_OBJECT (v4l2element, "Opened device '%s' (%s) successfully\n", - v4l2element->vcap.card, v4l2element->device); + v4l2element->vcap.card, v4l2element->videodev); return TRUE; @@ -450,7 +442,7 @@ error: gboolean gst_v4l2_close (GstV4l2Element * v4l2element) { - DEBUG ("Trying to close %s", v4l2element->device); + GST_DEBUG ("Trying to close %s", v4l2element->videodev); GST_V4L2_CHECK_OPEN (v4l2element); GST_V4L2_CHECK_NOT_ACTIVE (v4l2element); @@ -474,13 +466,14 @@ gst_v4l2_close (GstV4l2Element * v4l2element) gboolean gst_v4l2_get_norm (GstV4l2Element * v4l2element, v4l2_std_id * norm) { - DEBUG ("getting norm"); - GST_V4L2_CHECK_OPEN (v4l2element); + GST_DEBUG ("getting norm"); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; if (ioctl (v4l2element->video_fd, VIDIOC_G_STD, norm) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to get the current norm for device %s: %s", - v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to get the current norm for device %s: %s", + v4l2element->videodev, g_strerror (errno)); return FALSE; } @@ -497,14 +490,16 @@ gst_v4l2_get_norm (GstV4l2Element * v4l2element, v4l2_std_id * norm) gboolean gst_v4l2_set_norm (GstV4l2Element * v4l2element, v4l2_std_id norm) { - DEBUG ("trying to set norm to %llx", norm); - GST_V4L2_CHECK_OPEN (v4l2element); - GST_V4L2_CHECK_NOT_ACTIVE (v4l2element); + GST_DEBUG ("trying to set norm to %llx", norm); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; + if (!GST_V4L2_IS_ACTIVE (v4l2element)) + return FALSE; if (ioctl (v4l2element->video_fd, VIDIOC_S_STD, &norm) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to set norm 0x%llx for device %s: %s", - norm, v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to set norm 0x%llx for device %s: %s", norm, + v4l2element->videodev, g_strerror (errno)); return FALSE; } @@ -523,13 +518,14 @@ gst_v4l2_get_input (GstV4l2Element * v4l2element, gint * input) { gint n; - DEBUG ("trying to get input"); - GST_V4L2_CHECK_OPEN (v4l2element); + GST_DEBUG ("trying to get input"); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; if (ioctl (v4l2element->video_fd, VIDIOC_G_INPUT, &n) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to get current input on device %s: %s", - v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to get current input on device %s: %s", v4l2element->videodev, + g_strerror (errno)); return FALSE; } @@ -548,14 +544,15 @@ gst_v4l2_get_input (GstV4l2Element * v4l2element, gint * input) gboolean gst_v4l2_set_input (GstV4l2Element * v4l2element, gint input) { - DEBUG ("trying to set input to %d", input); - GST_V4L2_CHECK_OPEN (v4l2element); - GST_V4L2_CHECK_NOT_ACTIVE (v4l2element); + GST_DEBUG ("trying to set input to %d", input); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; + if (!GST_V4L2_IS_ACTIVE (v4l2element)) + return FALSE; if (ioctl (v4l2element->video_fd, VIDIOC_S_INPUT, &input) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to set input %d on device %s: %s", - input, v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, "Failed to set input %d on device %s: %s", + input, v4l2element->videodev, g_strerror (errno)); return FALSE; } @@ -574,13 +571,14 @@ gst_v4l2_get_output (GstV4l2Element * v4l2element, gint * output) { gint n; - DEBUG ("trying to get output"); - GST_V4L2_CHECK_OPEN (v4l2element); + GST_DEBUG ("trying to get output"); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; if (ioctl (v4l2element->video_fd, VIDIOC_G_OUTPUT, &n) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to get current output on device %s: %s", - v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to get current output on device %s: %s", v4l2element->videodev, + g_strerror (errno)); return FALSE; } @@ -599,14 +597,16 @@ gst_v4l2_get_output (GstV4l2Element * v4l2element, gint * output) gboolean gst_v4l2_set_output (GstV4l2Element * v4l2element, gint output) { - DEBUG ("trying to set output to %d", output); - GST_V4L2_CHECK_OPEN (v4l2element); - GST_V4L2_CHECK_NOT_ACTIVE (v4l2element); + GST_DEBUG ("trying to set output to %d", output); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; + if (!GST_V4L2_IS_ACTIVE (v4l2element)) + return FALSE; if (ioctl (v4l2element->video_fd, VIDIOC_S_OUTPUT, &output) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to set output %d on device %s: %s", - output, v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to set current output on device %s to %d: %s", + v4l2element->videodev, output, g_strerror (errno)); return FALSE; } @@ -627,16 +627,17 @@ gst_v4l2_get_frequency (GstV4l2Element * v4l2element, struct v4l2_frequency freq; GstTunerChannel *channel; - DEBUG ("getting current tuner frequency"); - GST_V4L2_CHECK_OPEN (v4l2element); + GST_DEBUG ("getting current tuner frequency"); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; channel = gst_tuner_get_channel (GST_TUNER (v4l2element)); freq.tuner = tunernum; if (ioctl (v4l2element->video_fd, VIDIOC_G_FREQUENCY, &freq) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to get current tuner frequency for device %s: %s", - v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to get current tuner frequency for device %s: %s", + v4l2element->videodev, g_strerror (errno)); return FALSE; } @@ -659,9 +660,11 @@ gst_v4l2_set_frequency (GstV4l2Element * v4l2element, struct v4l2_frequency freq; GstTunerChannel *channel; - DEBUG ("setting current tuner frequency to %lu", frequency); - GST_V4L2_CHECK_OPEN (v4l2element); - GST_V4L2_CHECK_NOT_ACTIVE (v4l2element); + GST_DEBUG ("setting current tuner frequency to %lu", frequency); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; + if (!GST_V4L2_IS_ACTIVE (v4l2element)) + return FALSE; channel = gst_tuner_get_channel (GST_TUNER (v4l2element)); @@ -671,9 +674,9 @@ gst_v4l2_set_frequency (GstV4l2Element * v4l2element, freq.frequency = frequency / channel->freq_multiplicator; if (ioctl (v4l2element->video_fd, VIDIOC_S_FREQUENCY, &freq) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to set tuner frequency to %lu for device %s: %s", - frequency, v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to set current tuner frequency for device %s to %lu: %s", + v4l2element->videodev, frequency, g_strerror (errno)); return FALSE; } @@ -693,14 +696,15 @@ gst_v4l2_signal_strength (GstV4l2Element * v4l2element, { struct v4l2_tuner tuner; - DEBUG ("trying to get signal strength"); - GST_V4L2_CHECK_OPEN (v4l2element); + GST_DEBUG ("trying to get signal strength"); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; tuner.index = tunernum; if (ioctl (v4l2element->video_fd, VIDIOC_G_TUNER, &tuner) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to get signal strength for device %s: %s", - v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to get signal strength for device %s: %s", + v4l2element->videodev, g_strerror (errno)); return FALSE; } @@ -722,16 +726,17 @@ gst_v4l2_get_attribute (GstV4l2Element * v4l2element, { struct v4l2_control control; - GST_V4L2_CHECK_OPEN (v4l2element); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; - DEBUG ("getting value of attribute %d", attribute_num); + GST_DEBUG ("getting value of attribute %d", attribute_num); control.id = attribute_num; if (ioctl (v4l2element->video_fd, VIDIOC_G_CTRL, &control) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to get value for control %d on device %s: %s", - attribute_num, v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to get value for control %d on device %s: %s", attribute_num, + v4l2element->videodev, g_strerror (errno)); return FALSE; } @@ -753,17 +758,18 @@ gst_v4l2_set_attribute (GstV4l2Element * v4l2element, { struct v4l2_control control; - GST_V4L2_CHECK_OPEN (v4l2element); + if (!GST_V4L2_IS_OPEN (v4l2element)) + return FALSE; - DEBUG ("setting value of attribute %d to %d", attribute_num, value); + GST_DEBUG ("setting value of attribute %d to %d", attribute_num, value); control.id = attribute_num; control.value = value; if (ioctl (v4l2element->video_fd, VIDIOC_S_CTRL, &control) < 0) { - GST_ELEMENT_ERROR (v4l2element, RESOURCE, SETTINGS, (NULL), - ("Failed to set value %d for control %d on device %s: %s", - value, attribute_num, v4l2element->device, g_strerror (errno))); + GST_WARNING_OBJECT (v4l2element, + "Failed to set value %d for control %d on device %s: %s", value, + attribute_num, v4l2element->videodev, g_strerror (errno)); return FALSE; } diff --git a/sys/v4l2/v4l2_calls.h b/sys/v4l2/v4l2_calls.h index 5325eadf46..fcacf354ea 100644 --- a/sys/v4l2/v4l2_calls.h +++ b/sys/v4l2/v4l2_calls.h @@ -26,96 +26,98 @@ /* simple check whether the device is open */ #define GST_V4L2_IS_OPEN(element) \ - (element->video_fd > 0) + (GST_V4L2ELEMENT(element)->video_fd > 0) /* check whether the device is 'active' */ #define GST_V4L2_IS_ACTIVE(element) \ - (element->buffer != NULL) + (GST_V4L2ELEMENT(element)->buffer != NULL) #define GST_V4L2_IS_OVERLAY(element) \ - (element->vcap.capabilities & V4L2_CAP_VIDEO_OVERLAY) + (GST_V4L2ELEMENT(element)->vcap.capabilities & V4L2_CAP_VIDEO_OVERLAY) /* checks whether the current v4lelement has already been open()'ed or not */ -#define GST_V4L2_CHECK_OPEN(element) \ - if (!GST_V4L2_IS_OPEN(element)) \ - { \ - GST_ELEMENT_ERROR (element, RESOURCE, TOO_LAZY, \ +#define GST_V4L2_CHECK_OPEN(element) \ + if (!GST_V4L2_IS_OPEN(element)) \ + { \ + GST_ELEMENT_ERROR (element, RESOURCE, TOO_LAZY, \ (_("Device is not open.")), (NULL)); \ - return FALSE; \ + return FALSE; \ } /* checks whether the current v4lelement is close()'ed or whether it is still open */ -#define GST_V4L2_CHECK_NOT_OPEN(element) \ - if (GST_V4L2_IS_OPEN(element)) \ - { \ - GST_ELEMENT_ERROR (element, RESOURCE, TOO_LAZY, \ +#define GST_V4L2_CHECK_NOT_OPEN(element) \ + if (GST_V4L2_IS_OPEN(element)) \ + { \ + GST_ELEMENT_ERROR (element, RESOURCE, TOO_LAZY, \ (_("Device is open.")), (NULL)); \ - return FALSE; \ + return FALSE; \ } /* checks whether the current v4lelement does video overlay */ -#define GST_V4L2_CHECK_OVERLAY(element) \ - if (!GST_V4L2_IS_OVERLAY(element)) \ - { \ +#define GST_V4L2_CHECK_OVERLAY(element) \ + if (!GST_V4L2_IS_OVERLAY(element)) \ + { \ GST_ELEMENT_ERROR (element, RESOURCE, TOO_LAZY, \ (NULL), ("Device cannot handle overlay")); \ - return FALSE; \ + return FALSE; \ } /* checks whether we're in capture mode or not */ -#define GST_V4L2_CHECK_ACTIVE(element) \ - if (!GST_V4L2_IS_ACTIVE(element)) \ - { \ +#define GST_V4L2_CHECK_ACTIVE(element) \ + if (!GST_V4L2_IS_ACTIVE(element)) \ + { \ GST_ELEMENT_ERROR (element, RESOURCE, SETTINGS, \ (NULL), ("Device is not in streaming mode")); \ - return FALSE; \ + return FALSE; \ } /* checks whether we're out of capture mode or not */ -#define GST_V4L2_CHECK_NOT_ACTIVE(element) \ - if (GST_V4L2_IS_ACTIVE(element)) \ - { \ +#define GST_V4L2_CHECK_NOT_ACTIVE(element) \ + if (GST_V4L2_IS_ACTIVE(element)) \ + { \ GST_ELEMENT_ERROR (element, RESOURCE, SETTINGS, \ (NULL), ("Device is in streaming mode")); \ - return FALSE; \ + return FALSE; \ } /* open/close the device */ -gboolean gst_v4l2_open (GstV4l2Element *v4l2element); -gboolean gst_v4l2_close (GstV4l2Element *v4l2element); +gboolean gst_v4l2_open (GstV4l2Element *v4l2element); +gboolean gst_v4l2_close (GstV4l2Element *v4l2element); /* norm/input/output */ -gboolean gst_v4l2_get_norm (GstV4l2Element *v4l2element, - v4l2_std_id *norm); -gboolean gst_v4l2_set_norm (GstV4l2Element *v4l2element, - v4l2_std_id norm); -gboolean gst_v4l2_get_input (GstV4l2Element *v4l2element, - gint *input); -gboolean gst_v4l2_set_input (GstV4l2Element *v4l2element, - gint input); -gboolean gst_v4l2_get_output (GstV4l2Element *v4l2element, - gint *output); -gboolean gst_v4l2_set_output (GstV4l2Element *v4l2element, - gint output); +gboolean gst_v4l2_get_norm (GstV4l2Element *v4l2element, + v4l2_std_id *norm); +gboolean gst_v4l2_set_norm (GstV4l2Element *v4l2element, + v4l2_std_id norm); +gboolean gst_v4l2_get_input (GstV4l2Element *v4l2element, + gint *input); +gboolean gst_v4l2_set_input (GstV4l2Element *v4l2element, + gint input); +gboolean gst_v4l2_get_output (GstV4l2Element *v4l2element, + gint *output); +gboolean gst_v4l2_set_output (GstV4l2Element *v4l2element, + gint output); /* frequency control */ -gboolean gst_v4l2_get_frequency (GstV4l2Element *v4l2element, - gint tunernum, - gulong *frequency); -gboolean gst_v4l2_set_frequency (GstV4l2Element *v4l2element, - gint tunernum, - gulong frequency); -gboolean gst_v4l2_signal_strength (GstV4l2Element *v4l2element, - gint tunernum, - gulong *signal); +gboolean gst_v4l2_get_frequency (GstV4l2Element *v4l2element, + gint tunernum, + gulong *frequency); +gboolean gst_v4l2_set_frequency (GstV4l2Element *v4l2element, + gint tunernum, + gulong frequency); +gboolean gst_v4l2_signal_strength (GstV4l2Element *v4l2element, + gint tunernum, + gulong *signal); /* attribute control */ -gboolean gst_v4l2_get_attribute (GstV4l2Element *v4l2element, - int attribute, - int *value); -gboolean gst_v4l2_set_attribute (GstV4l2Element *v4l2element, - int attribute, - const int value); +gboolean gst_v4l2_get_attribute (GstV4l2Element *v4l2element, + int attribute, + int *value); +gboolean gst_v4l2_set_attribute (GstV4l2Element *v4l2element, + int attribute, + const int value); + +gboolean gst_v4l2_get_capabilities (GstV4l2Element * v4l2element); #endif /* __V4L2_CALLS_H__ */ diff --git a/sys/v4l2/v4l2src_calls.c b/sys/v4l2/v4l2src_calls.c index 6971733068..ae27de1b6e 100644 --- a/sys/v4l2/v4l2src_calls.c +++ b/sys/v4l2/v4l2src_calls.c @@ -33,6 +33,9 @@ #include #include +#include "gstv4l2tuner.h" + +GST_DEBUG_CATEGORY_EXTERN (v4l2src_debug); #define GST_CAT_DEFAULT v4l2src_debug /* lalala... */ @@ -40,9 +43,9 @@ #define GST_V4L2_SET_INACTIVE(element) (element)->buffer = NULL #define DEBUG(format, args...) \ - GST_CAT_DEBUG_OBJECT (\ - v4l2src_debug, v4l2src, \ - "V4L2SRC: " format, ##args) + GST_CAT_DEBUG_OBJECT (\ + v4l2src_debug, v4l2src, \ + "V4L2SRC: " format, ##args) /* On some systems MAP_FAILED seems to be missing */ #ifndef MAP_FAILED @@ -76,12 +79,12 @@ gst_v4l2src_fill_format_list (GstV4l2Src * v4l2src) } else { GST_ELEMENT_ERROR (v4l2src, RESOURCE, SETTINGS, (NULL), ("failed to get number %d in pixelformat enumeration for %s: %s", - n, GST_V4L2ELEMENT (v4l2src)->device, g_strerror (errno))); + n, GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); g_free (format); return FALSE; } } - GST_LOG_OBJECT (v4l2src, "got format %" GST_FOURCC_FORMAT, + GST_LOG_OBJECT (v4l2src, "got format" GST_FOURCC_FORMAT, GST_FOURCC_ARGS (format->pixelformat)); v4l2src->formats = g_slist_prepend (v4l2src->formats, format); } @@ -122,7 +125,7 @@ gst_v4l2src_queue_frame (GstV4l2Src * v4l2src, guint i) &v4l2src->pool->buffers[i].buffer) < 0) { GST_ELEMENT_ERROR (v4l2src, RESOURCE, WRITE, (_("Could not write to device \"%s\"."), - GST_V4L2ELEMENT (v4l2src)->device), + GST_V4L2ELEMENT (v4l2src)->videodev), ("Error queueing buffer %u on device %s", i, g_strerror (errno))); return FALSE; } @@ -145,13 +148,46 @@ gst_v4l2src_grab_frame (GstV4l2Src * v4l2src) buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; while (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_DQBUF, &buffer) < 0) { /* if the sync() got interrupted, we can retry */ - if (errno != EINTR) { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), - ("could not sync on a buffer on device %s: %s", - GST_V4L2ELEMENT (v4l2src)->device, g_strerror (errno))); - return -1; + switch (errno) { + case EAGAIN: + GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), + ("Non-blocking I/O has been selected using O_NONBLOCK and" + " no buffer was in the outgoing queue. device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + break; + case EINVAL: + GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), + ("The buffer type is not supported, or the index is out of bounds," + " or no buffers have been allocated yet, or the userptr" + " or length are invalid. device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + break; + case ENOMEM: + GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), + ("isufficient memory to enqueue a user pointer buffer. device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + break; + case EIO: + GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), + ("VIDIOC_DQBUF failed due to an internal error." + " Can also indicate temporary problems like signal loss." + " Note the driver might dequeue an (empty) buffer despite" + " returning an error, or even stop capturing." + " device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + break; + case EINTR: + GST_ELEMENT_ERROR (v4l2src, RESOURCE, SYNC, (NULL), + ("could not sync on a buffer on device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + break; + default: + GST_DEBUG_OBJECT (v4l2src, "grab got interrupted"); + break; } - GST_DEBUG_OBJECT (v4l2src, "grab got interrupted"); + + return -1; + } GST_LOG_OBJECT (v4l2src, "grabbed frame %d", buffer.index); @@ -178,7 +214,7 @@ gst_v4l2src_get_capture (GstV4l2Src * v4l2src) &v4l2src->format) < 0) { GST_ELEMENT_ERROR (v4l2src, RESOURCE, SETTINGS, (NULL), ("failed to get pixelformat for device %s: %s", - GST_V4L2ELEMENT (v4l2src)->device, g_strerror (errno))); + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); return FALSE; } @@ -213,8 +249,8 @@ gst_v4l2src_set_capture (GstV4l2Src * v4l2src, &v4l2src->format) < 0) { GST_ELEMENT_ERROR (v4l2src, RESOURCE, SETTINGS, (NULL), ("failed to set pixelformat to %s @ %dx%d for device %s: %s", - fmt->description, width, height, GST_V4L2ELEMENT (v4l2src)->device, - g_strerror (errno))); + fmt->description, width, height, + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); return FALSE; } @@ -249,72 +285,92 @@ gst_v4l2src_capture_init (GstV4l2Src * v4l2src) v4l2src->breq.count = GST_V4L2_MIN_BUFFERS; } v4l2src->breq.type = v4l2src->format.type; - v4l2src->breq.memory = V4L2_MEMORY_MMAP; - if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_REQBUFS, - &v4l2src->breq) < 0) { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, - (_("Could not get buffers from device \"%s\"."), - GST_V4L2ELEMENT (v4l2src)->device), - ("error requesting %d buffers: %s", v4l2src->breq.count, - g_strerror (errno))); - return FALSE; - } - - if (v4l2src->breq.count < GST_V4L2_MIN_BUFFERS) { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, - (_("Could not get enough buffers from device \"%s\"."), - GST_V4L2ELEMENT (v4l2src)->device), - ("we received %d, we want at least %d", v4l2src->breq.count, - GST_V4L2_MIN_BUFFERS)); - v4l2src->breq.count = buffers; - return FALSE; - } - if (v4l2src->breq.count != buffers) - g_object_notify (G_OBJECT (v4l2src), "num_buffers"); - - GST_INFO_OBJECT (v4l2src, - "Got %d buffers (%" GST_FOURCC_FORMAT ") of size %d KB\n", - v4l2src->breq.count, - GST_FOURCC_ARGS (v4l2src->format.fmt.pix.pixelformat), - v4l2src->format.fmt.pix.sizeimage / 1024); - - /* Map the buffers */ - v4l2src->pool = g_new (GstV4l2BufferPool, 1); - gst_atomic_int_init (&v4l2src->pool->refcount, 1); - v4l2src->pool->video_fd = GST_V4L2ELEMENT (v4l2src)->video_fd; - v4l2src->pool->buffer_count = v4l2src->breq.count; - v4l2src->pool->buffers = g_new0 (GstV4l2Buffer, v4l2src->breq.count); - - for (n = 0; n < v4l2src->breq.count; n++) { - GstV4l2Buffer *buffer = &v4l2src->pool->buffers[n]; - - gst_atomic_int_init (&buffer->refcount, 1); - buffer->pool = v4l2src->pool; - buffer->buffer.index = n; - buffer->buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_QUERYBUF, - &buffer->buffer) < 0) { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL), - ("Could not get buffer properties of buffer %d: %s", n, + if (GST_V4L2ELEMENT (v4l2src)->vcap.capabilities & V4L2_CAP_STREAMING) { + v4l2src->breq.memory = V4L2_MEMORY_MMAP; + if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_REQBUFS, + &v4l2src->breq) < 0) { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, + (_("Could not get buffers from device \"%s\"."), + GST_V4L2ELEMENT (v4l2src)->videodev), + ("error requesting %d buffers: %s", v4l2src->breq.count, g_strerror (errno))); - gst_v4l2src_capture_deinit (v4l2src); return FALSE; } - buffer->start = - mmap (0, buffer->buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, - GST_V4L2ELEMENT (v4l2src)->video_fd, buffer->buffer.m.offset); - if (buffer->start == MAP_FAILED) { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL), - ("Could not mmap video buffer %d: %s", n, g_strerror (errno))); - buffer->start = 0; - gst_v4l2src_capture_deinit (v4l2src); + GST_LOG_OBJECT (v4l2src, "using default mmap method"); + } else if (GST_V4L2ELEMENT (v4l2src)->vcap.capabilities & V4L2_CAP_READWRITE) { + v4l2src->breq.memory = 0; + GST_INFO_OBJECT (v4l2src, "using fallback read method"); + } else { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, + (_("the driver of device \"%s\" is broken."), + GST_V4L2ELEMENT (v4l2src)->videodev), + ("no supported read capability from %s", + GST_V4L2ELEMENT (v4l2src)->videodev)); + return FALSE; + } + + if (v4l2src->breq.memory > 0) { + if (v4l2src->breq.count < GST_V4L2_MIN_BUFFERS) { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, + (_("Could not get enough buffers from device \"%s\"."), + GST_V4L2ELEMENT (v4l2src)->videodev), + ("we received %d, we want at least %d", v4l2src->breq.count, + GST_V4L2_MIN_BUFFERS)); + v4l2src->breq.count = buffers; return FALSE; } - buffer->length = buffer->buffer.length; - if (!gst_v4l2src_queue_frame (v4l2src, n)) { - gst_v4l2src_capture_deinit (v4l2src); - return FALSE; + if (v4l2src->breq.count != buffers) + g_object_notify (G_OBJECT (v4l2src), "num_buffers"); + + GST_INFO_OBJECT (v4l2src, + "Got %d buffers (" GST_FOURCC_FORMAT ") of size %d KB\n", + v4l2src->breq.count, + GST_FOURCC_ARGS (v4l2src->format.fmt.pix.pixelformat), + v4l2src->format.fmt.pix.sizeimage / 1024); + + /* Map the buffers */ + GST_LOG_OBJECT (v4l2src, "initiating buffer pool"); + + v4l2src->pool = g_new (GstV4l2BufferPool, 1); + g_atomic_int_set (&v4l2src->pool->refcount, 1); + v4l2src->pool->video_fd = GST_V4L2ELEMENT (v4l2src)->video_fd; + v4l2src->pool->buffer_count = v4l2src->breq.count; + v4l2src->pool->buffers = g_new0 (GstV4l2Buffer, v4l2src->breq.count); + + for (n = 0; n < v4l2src->breq.count; n++) { + GstV4l2Buffer *buffer = &v4l2src->pool->buffers[n]; + + g_atomic_int_set (&buffer->refcount, 1); + buffer->pool = v4l2src->pool; + buffer->buffer.index = n; + buffer->buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_QUERYBUF, + &buffer->buffer) < 0) { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL), + ("Could not get buffer properties of buffer %d: %s", n, + g_strerror (errno))); + gst_v4l2src_capture_deinit (v4l2src); + return FALSE; + } + buffer->start = + mmap (0, buffer->buffer.length, PROT_READ | PROT_WRITE, MAP_SHARED, + GST_V4L2ELEMENT (v4l2src)->video_fd, buffer->buffer.m.offset); + if (buffer->start == MAP_FAILED) { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, READ, (NULL), + ("Could not mmap video buffer %d: %s", n, g_strerror (errno))); + buffer->start = 0; + gst_v4l2src_capture_deinit (v4l2src); + return FALSE; + } + buffer->length = buffer->buffer.length; + if (!gst_v4l2src_queue_frame (v4l2src, n)) { + gst_v4l2src_capture_deinit (v4l2src); + return FALSE; + } } + } else { + GST_LOG_OBJECT (v4l2src, "no buffer pool used"); + v4l2src->pool = NULL; } GST_V4L2_SET_ACTIVE (GST_V4L2ELEMENT (v4l2src)); @@ -338,17 +394,19 @@ gst_v4l2src_capture_start (GstV4l2Src * v4l2src) GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src)); if (!GST_V4L2_IS_ACTIVE (GST_V4L2ELEMENT (v4l2src))) { - gst_pad_renegotiate (v4l2src->srcpad); + /* gst_pad_renegotiate (v4l2src->srcpad); FIX: is it still required in 0.10 */ } GST_V4L2_CHECK_ACTIVE (GST_V4L2ELEMENT (v4l2src)); v4l2src->quit = FALSE; - if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_STREAMON, &type) < 0) { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, OPEN_READ, (NULL), - ("Error starting streaming capture from device %s: %s", - GST_V4L2ELEMENT (v4l2src)->device, g_strerror (errno))); - return FALSE; + if (v4l2src->breq.memory != 0) { + if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_STREAMON, &type) < 0) { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, OPEN_READ, (NULL), + ("Error starting streaming capture from device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + return FALSE; + } } v4l2src->is_capturing = TRUE; @@ -372,13 +430,16 @@ gst_v4l2src_capture_stop (GstV4l2Src * v4l2src) GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src)); GST_V4L2_CHECK_ACTIVE (GST_V4L2ELEMENT (v4l2src)); - /* we actually need to sync on all queued buffers but not - * on the non-queued ones */ - if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_STREAMOFF, &type) < 0) { - GST_ELEMENT_ERROR (v4l2src, RESOURCE, CLOSE, (NULL), - ("Error stopping streaming capture from device %s: %s", - GST_V4L2ELEMENT (v4l2src)->device, g_strerror (errno))); - return FALSE; + if (v4l2src->breq.memory != 0) { + /* we actually need to sync on all queued buffers but not + * on the non-queued ones */ + if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_STREAMOFF, + &type) < 0) { + GST_ELEMENT_ERROR (v4l2src, RESOURCE, CLOSE, (NULL), + ("Error stopping streaming capture from device %s: %s", + GST_V4L2ELEMENT (v4l2src)->videodev, g_strerror (errno))); + return FALSE; + } } /* make an optional pending wait stop */ @@ -394,16 +455,18 @@ gst_v4l2src_buffer_pool_free (GstV4l2BufferPool * pool, gboolean do_close) guint i; for (i = 0; i < pool->buffer_count; i++) { - gst_atomic_int_destroy (&pool->buffers[i].refcount); + g_atomic_int_set (&pool->buffers[i].refcount, 0); munmap (pool->buffers[i].start, pool->buffers[i].length); } g_free (pool->buffers); - gst_atomic_int_destroy (&pool->refcount); + g_atomic_int_set (&pool->refcount, 0); if (do_close) close (pool->video_fd); g_free (pool); } +#if 0 + void gst_v4l2src_free_buffer (GstBuffer * buffer) { @@ -411,18 +474,21 @@ gst_v4l2src_free_buffer (GstBuffer * buffer) GST_LOG ("freeing buffer %p (nr. %d)", buffer, buf->buffer.index); - if (!gst_atomic_int_dec_and_test (&buf->refcount)) { + if (!g_atomic_int_dec_and_test (&buf->refcount)) { /* we're still in use, add to queue again note: this might fail because the device is already stopped (race) */ if (ioctl (buf->pool->video_fd, VIDIOC_QBUF, &buf->buffer) < 0) GST_INFO ("readding to queue failed, assuming video device is stopped"); } - if (gst_atomic_int_dec_and_test (&buf->pool->refcount)) { + if (g_atomic_int_dec_and_test (&buf->pool->refcount)) { /* we're last thing that used all this */ gst_v4l2src_buffer_pool_free (buf->pool, TRUE); } } + +#endif + /****************************************************** * gst_v4l2src_capture_deinit(): * deinitialize the capture system @@ -432,31 +498,44 @@ gst_v4l2src_free_buffer (GstBuffer * buffer) gboolean gst_v4l2src_capture_deinit (GstV4l2Src * v4l2src) { - gint i, dequeue = 0; + gint i; + gboolean try_reinit = FALSE; GST_DEBUG_OBJECT (v4l2src, "deinitting capture system"); GST_V4L2_CHECK_OPEN (GST_V4L2ELEMENT (v4l2src)); GST_V4L2_CHECK_ACTIVE (GST_V4L2ELEMENT (v4l2src)); - /* free the buffers */ - for (i = 0; i < v4l2src->breq.count; i++) { - if (gst_atomic_int_dec_and_test (&v4l2src->pool->buffers[i].refcount)) - dequeue++; + if (v4l2src->pool) { + /* free the buffers */ + for (i = 0; i < v4l2src->breq.count; i++) { + if (g_atomic_int_dec_and_test (&v4l2src->pool->buffers[i].refcount)) { + if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_DQBUF, + &v4l2src->pool->buffers[i].buffer) < 0) + GST_WARNING_OBJECT (v4l2src, + "Could not dequeue buffer on uninitialization: %s - will try reinit instead", + g_strerror (errno)); + try_reinit = TRUE; + } + } + if (g_atomic_int_dec_and_test (&v4l2src->pool->refcount)) { + /* we're last thing that used all this */ + gst_v4l2src_buffer_pool_free (v4l2src->pool, FALSE); + } + v4l2src->pool = NULL; + /* This is our second try to get the buffers dequeued. + * Since buffers are normally dequeued automatically when capturing is + * stopped, but may be enqueued before capturing has started, you get + * a problem when you abort before capturing started but have enqueued + * the buffers. We avoid that by starting/stopping capturing once so + * they get auto-dequeued. + */ + if (try_reinit) { + if (!gst_v4l2src_capture_start (v4l2src) || + !gst_v4l2src_capture_stop (v4l2src)) + return FALSE; + } } - for (i = 0; i < dequeue; i++) { - struct v4l2_buffer buffer; - - buffer.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; - if (ioctl (GST_V4L2ELEMENT (v4l2src)->video_fd, VIDIOC_DQBUF, &buffer) < 0) - GST_WARNING_OBJECT (v4l2src, - "Could not dequeue buffer on uninitialization"); - } - if (gst_atomic_int_dec_and_test (&v4l2src->pool->refcount)) { - /* we're last thing that used all this */ - gst_v4l2src_buffer_pool_free (v4l2src->pool, FALSE); - } - v4l2src->pool = NULL; GST_V4L2_SET_INACTIVE (GST_V4L2ELEMENT (v4l2src)); return TRUE; @@ -474,8 +553,7 @@ gst_v4l2src_get_size_limits (GstV4l2Src * v4l2src, { struct v4l2_format fmt; - GST_LOG_OBJECT (v4l2src, - "getting size limits with format %" GST_FOURCC_FORMAT, + GST_LOG_OBJECT (v4l2src, "getting size limits with format " GST_FOURCC_FORMAT, GST_FOURCC_ARGS (format->pixelformat)); /* get size delimiters */ @@ -511,3 +589,212 @@ gst_v4l2src_get_size_limits (GstV4l2Src * v4l2src, return TRUE; } + +gboolean +gst_v4l2src_get_fps (GstV4l2Src * v4l2src, gint * fps_n, gint * fps_d) +{ + v4l2_std_id std; + const GList *item; + + if (!GST_V4L2_IS_OPEN (GST_V4L2ELEMENT (v4l2src))) + return FALSE; + + if (!gst_v4l2_get_norm (GST_V4L2ELEMENT (v4l2src), &std)) + return FALSE; + for (item = GST_V4L2ELEMENT (v4l2src)->stds; item != NULL; item = item->next) { + GstV4l2TunerNorm *v4l2norm = item->data; + + if (v4l2norm->index == std) { + *fps_n = + gst_value_get_fraction_numerator (&GST_TUNER_NORM (v4l2norm)-> + framerate); + *fps_d = + gst_value_get_fraction_denominator (&GST_TUNER_NORM (v4l2norm)-> + framerate); + return TRUE; + } + } + + return FALSE; + +} + +#if 0 + +/* get a list of possible framerates + * this is only done for webcams; + * other devices return NULL here. + * this function takes a LONG time to execute. + */ +GValue * +gst_v4l2src_get_fps_list (GstV4l2Src * v4l2src) +{ + gint fps_index; + struct video_window *vwin = &GST_V4L2ELEMENT (v4l2src)->vwin; + GstV4l2Element *v4l2element = GST_V4L2ELEMENT (v4l2src); + + /* check if we have vwin window properties giving a framerate, + * as is done for webcams + * See http://www.smcc.demon.nl/webcam/api.html + * which is used for the Philips and qce-ga drivers */ + fps_index = (vwin->flags >> 16) & 0x3F; /* 6 bit index for framerate */ + + /* webcams have a non-zero fps_index */ + if (fps_index == 0) { + GST_DEBUG_OBJECT (v4l2src, "fps_index is 0, no webcam"); + return NULL; + } + GST_DEBUG_OBJECT (v4l2src, "fps_index is %d, so webcam", fps_index); + + { + int i; + GValue *list = NULL; + GValue value = { 0 }; + + /* webcam detected, so try all framerates and return a list */ + + list = g_new0 (GValue, 1); + g_value_init (list, GST_TYPE_LIST); + + /* index of 16 corresponds to 15 fps */ + GST_DEBUG_OBJECT (v4l2src, "device reports fps of %d/%d (%.4f)", + fps_index * 15, 16, fps_index * 15.0 / 16); + for (i = 0; i < 63; ++i) { + /* set bits 16 to 21 to 0 */ + vwin->flags &= (0x3F00 - 1); + /* set bits 16 to 21 to the index */ + vwin->flags |= i << 16; + if (gst_v4l2_set_window_properties (v4l2element)) { + /* setting it succeeded. FIXME: get it and check. */ + g_value_init (&value, GST_TYPE_FRACTION); + gst_value_set_fraction (&value, i * 15, 16); + gst_value_list_append_value (list, &value); + g_value_unset (&value); + } + } + /* FIXME: set back the original fps_index */ + vwin->flags &= (0x3F00 - 1); + vwin->flags |= fps_index << 16; + gst_v4l2_set_window_properties (v4l2element); + return list; + } + return NULL; +} + +#endif + +#define GST_TYPE_V4L2SRC_BUFFER (gst_v4l2src_buffer_get_type()) +#define GST_IS_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_V4L2SRC_BUFFER)) +#define GST_V4L2SRC_BUFFER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_V4L2SRC_BUFFER, GstV4l2SrcBuffer)) + +typedef struct _GstV4l2SrcBuffer +{ + GstBuffer buffer; + + GstV4l2Buffer *buf; +} GstV4l2SrcBuffer; + +static void gst_v4l2src_buffer_class_init (gpointer g_class, + gpointer class_data); +static void gst_v4l2src_buffer_init (GTypeInstance * instance, + gpointer g_class); +static void gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer); + +GType +gst_v4l2src_buffer_get_type (void) +{ + static GType _gst_v4l2src_buffer_type; + + if (G_UNLIKELY (_gst_v4l2src_buffer_type == 0)) { + static const GTypeInfo v4l2src_buffer_info = { + sizeof (GstBufferClass), + NULL, + NULL, + gst_v4l2src_buffer_class_init, + NULL, + NULL, + sizeof (GstV4l2SrcBuffer), + 0, + gst_v4l2src_buffer_init, + NULL + }; + _gst_v4l2src_buffer_type = g_type_register_static (GST_TYPE_BUFFER, + "GstV4l2SrcBuffer", &v4l2src_buffer_info, 0); + } + return _gst_v4l2src_buffer_type; +} + +static void +gst_v4l2src_buffer_class_init (gpointer g_class, gpointer class_data) +{ + GstMiniObjectClass *mini_object_class = GST_MINI_OBJECT_CLASS (g_class); + + mini_object_class->finalize = (GstMiniObjectFinalizeFunction) + gst_v4l2src_buffer_finalize; +} + +static void +gst_v4l2src_buffer_init (GTypeInstance * instance, gpointer g_class) +{ + +} + +static void +gst_v4l2src_buffer_finalize (GstV4l2SrcBuffer * v4l2src_buffer) +{ + GstV4l2Buffer *buf = v4l2src_buffer->buf; + + if (buf) { + + GST_LOG ("freeing buffer %p (nr. %d)", buf, buf->buffer.index); + + if (!g_atomic_int_dec_and_test (&buf->refcount)) { + /* we're still in use, add to queue again + note: this might fail because the device is already stopped (race) */ + if (ioctl (buf->pool->video_fd, VIDIOC_QBUF, &buf->buffer) < 0) + GST_INFO ("readding to queue failed, assuming video device is stopped"); + } + if (g_atomic_int_dec_and_test (&buf->pool->refcount)) { + /* we're last thing that used all this */ + gst_v4l2src_buffer_pool_free (buf->pool, TRUE); + } + + } +} + +/* Create a V4l2Src buffer from our mmap'd data area */ +GstBuffer * +gst_v4l2src_buffer_new (GstV4l2Src * v4l2src, guint size, guint8 * data, + GstV4l2Buffer * srcbuf) +{ + GstBuffer *buf; + gint fps_n, fps_d; + + GST_DEBUG_OBJECT (v4l2src, "creating buffer %d"); + + g_return_val_if_fail (gst_v4l2src_get_fps (v4l2src, &fps_n, &fps_d), NULL); + + buf = (GstBuffer *) gst_mini_object_new (GST_TYPE_V4L2SRC_BUFFER); + + GST_V4L2SRC_BUFFER (buf)->buf = srcbuf; + + if (data == NULL) { + GST_BUFFER_DATA (buf) = g_malloc (size); + } else { + GST_BUFFER_DATA (buf) = data; + } + GST_BUFFER_SIZE (buf) = size; + + GST_BUFFER_TIMESTAMP (buf) = + gst_clock_get_time (GST_ELEMENT (v4l2src)->clock); + GST_BUFFER_TIMESTAMP (buf) -= GST_ELEMENT (v4l2src)->base_time; + + GST_BUFFER_FLAG_SET (buf, GST_BUFFER_FLAG_READONLY); + GST_BUFFER_DURATION (buf) = gst_util_uint64_scale_int (GST_SECOND, + fps_n, fps_d); + + /* the negotiate() method already set caps on the source pad */ + gst_buffer_set_caps (buf, GST_PAD_CAPS (GST_BASE_SRC_PAD (v4l2src))); + + return buf; +} diff --git a/sys/v4l2/v4l2src_calls.h b/sys/v4l2/v4l2src_calls.h index d4f271b0ff..876676116c 100644 --- a/sys/v4l2/v4l2src_calls.h +++ b/sys/v4l2/v4l2src_calls.h @@ -24,30 +24,38 @@ #include "v4l2_calls.h" -gboolean gst_v4l2src_get_capture (GstV4l2Src *v4l2src); -gboolean gst_v4l2src_set_capture (GstV4l2Src *v4l2src, - struct v4l2_fmtdesc *fmt, - gint width, - gint height); -gboolean gst_v4l2src_capture_init (GstV4l2Src *v4l2src); -gboolean gst_v4l2src_capture_start (GstV4l2Src *v4l2src); -gint gst_v4l2src_grab_frame (GstV4l2Src *v4l2src); -guint8 * gst_v4l2src_get_buffer (GstV4l2Src *v4l2src, - gint num); -gboolean gst_v4l2src_queue_frame (GstV4l2Src *v4l2src, - guint i); -gboolean gst_v4l2src_capture_stop (GstV4l2Src *v4l2src); -gboolean gst_v4l2src_capture_deinit (GstV4l2Src *v4l2src); +gboolean gst_v4l2src_get_capture (GstV4l2Src *v4l2src); +gboolean gst_v4l2src_set_capture (GstV4l2Src *v4l2src, + struct v4l2_fmtdesc *fmt, + gint width, + gint height); +gboolean gst_v4l2src_capture_init (GstV4l2Src *v4l2src); +gboolean gst_v4l2src_capture_start (GstV4l2Src *v4l2src); +gint gst_v4l2src_grab_frame (GstV4l2Src *v4l2src); -gboolean gst_v4l2src_fill_format_list (GstV4l2Src *v4l2src); -gboolean gst_v4l2src_clear_format_list (GstV4l2Src *v4l2src); +gboolean gst_v4l2src_queue_frame (GstV4l2Src *v4l2src, + guint i); +gboolean gst_v4l2src_capture_stop (GstV4l2Src *v4l2src); +gboolean gst_v4l2src_capture_deinit (GstV4l2Src *v4l2src); + +gboolean gst_v4l2src_fill_format_list (GstV4l2Src *v4l2src); +gboolean gst_v4l2src_clear_format_list (GstV4l2Src *v4l2src); /* hacky */ -gboolean gst_v4l2src_get_size_limits (GstV4l2Src *v4l2src, - struct v4l2_fmtdesc *fmt, - gint *min_w, gint *max_w, - gint *min_h, gint *max_h); +gboolean gst_v4l2src_get_size_limits (GstV4l2Src *v4l2src, + struct v4l2_fmtdesc *fmt, + gint *min_w, gint *max_w, + gint *min_h, gint *max_h); -void gst_v4l2src_free_buffer (GstBuffer *buffer); +void gst_v4l2src_free_buffer (GstBuffer *buffer); + + +gboolean gst_v4l2src_get_fps (GstV4l2Src * v4l2src, gint * fps_n, gint * fps_d); + +GValue * gst_v4l2src_get_fps_list (GstV4l2Src * v4l2src); + +GstBuffer * gst_v4l2src_buffer_new (GstV4l2Src * v4l2src, + guint size, guint8 * data, + GstV4l2Buffer * srcbuf); #endif /* __V4L2SRC_CALLS_H__ */