gstreamer/sys/v4l/gstv4lsrc.c
Andy Wingo f38bff62af sys/v4l/gstv4lsrc.c (gst_v4lsrc_fixate): Also fixate the framerate. Need to get a handle on when exactly this functio...
Original commit message from CVS:
2005-07-07  Andy Wingo  <wingo@pobox.com>

* sys/v4l/gstv4lsrc.c (gst_v4lsrc_fixate): Also fixate the
framerate. Need to get a handle on when exactly this function is
called, tho.
2005-07-07 14:01:07 +00:00

649 lines
19 KiB
C

/* GStreamer
*
* gstv4lsrc.c: BT8x8/V4L source element
*
* Copyright (C) 2001-2002 Ronald Bultje <rbultje@ronald.bitfreak.net>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 59 Temple Place - Suite 330,
* Boston, MA 02111-1307, USA.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <sys/time.h>
#include "v4lsrc_calls.h"
#include <sys/ioctl.h>
static GstElementDetails gst_v4lsrc_details =
GST_ELEMENT_DETAILS ("Video (video4linux/raw) Source",
"Source/Video",
"Reads raw frames from a video4linux (BT8x8) device",
"Ronald Bultje <rbultje@ronald.bitfreak.net>");
GST_DEBUG_CATEGORY (v4lsrc_debug);
#define GST_CAT_DEFAULT v4lsrc_debug
enum
{
PROP_0,
PROP_AUTOPROBE,
PROP_AUTOPROBE_FPS,
PROP_TIMESTAMP_OFFSET
};
GST_BOILERPLATE (GstV4lSrc, gst_v4lsrc, GstV4lElement, GST_TYPE_V4LELEMENT);
/* basesrc methods */
static gboolean gst_v4lsrc_start (GstBaseSrc * src);
static gboolean gst_v4lsrc_stop (GstBaseSrc * src);
static gboolean gst_v4lsrc_set_caps (GstBaseSrc * src, GstCaps * caps);
static GstCaps *gst_v4lsrc_get_caps (GstBaseSrc * src);
static GstFlowReturn gst_v4lsrc_create (GstPushSrc * src, GstBuffer ** out);
static void gst_v4lsrc_fixate (GstPad * pad, GstCaps * caps);
static void gst_v4lsrc_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec);
static void gst_v4lsrc_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec);
static void
gst_v4lsrc_base_init (gpointer g_class)
{
GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
gst_element_class_set_details (gstelement_class, &gst_v4lsrc_details);
gst_element_class_add_pad_template (gstelement_class,
gst_pad_template_new ("src", GST_PAD_SRC, GST_PAD_ALWAYS,
gst_caps_new_any ()));
}
static void
gst_v4lsrc_class_init (GstV4lSrcClass * klass)
{
GObjectClass *gobject_class;
GstBaseSrcClass *basesrc_class;
GstPushSrcClass *pushsrc_class;
gobject_class = (GObjectClass *) klass;
basesrc_class = (GstBaseSrcClass *) klass;
pushsrc_class = (GstPushSrcClass *) klass;
gobject_class->set_property = gst_v4lsrc_set_property;
gobject_class->get_property = gst_v4lsrc_get_property;
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOPROBE,
g_param_spec_boolean ("autoprobe", "Autoprobe",
"Whether the device should be probed for all possible features",
TRUE, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_AUTOPROBE_FPS,
g_param_spec_boolean ("autoprobe-fps", "Autoprobe FPS",
"Whether the device should be probed for framerates",
TRUE, G_PARAM_READWRITE));
g_object_class_install_property (G_OBJECT_CLASS (klass),
PROP_TIMESTAMP_OFFSET, g_param_spec_int64 ("timestamp-offset",
"Timestamp offset",
"A time offset subtracted from timestamps set on buffers (in ns)",
G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE));
GST_DEBUG_CATEGORY_INIT (v4lsrc_debug, "v4lsrc", 0, "V4L source element");
basesrc_class->get_caps = gst_v4lsrc_get_caps;
basesrc_class->set_caps = gst_v4lsrc_set_caps;
basesrc_class->start = gst_v4lsrc_start;
basesrc_class->stop = gst_v4lsrc_stop;
pushsrc_class->create = gst_v4lsrc_create;
}
static void
gst_v4lsrc_init (GstV4lSrc * v4lsrc)
{
v4lsrc->buffer_size = 0;
/* no colorspaces */
v4lsrc->colorspaces = NULL;
v4lsrc->is_capturing = FALSE;
v4lsrc->autoprobe = TRUE;
v4lsrc->autoprobe_fps = TRUE;
v4lsrc->timestamp_offset = 0;
v4lsrc->fps_list = NULL;
gst_pad_set_fixatecaps_function (GST_BASE_SRC_PAD (v4lsrc),
gst_v4lsrc_fixate);
gst_base_src_set_live (GST_BASE_SRC (v4lsrc), TRUE);
}
static void
gst_v4lsrc_set_property (GObject * object,
guint prop_id, const GValue * value, GParamSpec * pspec)
{
GstV4lSrc *v4lsrc = GST_V4LSRC (object);
switch (prop_id) {
case PROP_AUTOPROBE:
g_return_if_fail (!GST_V4L_IS_ACTIVE (GST_V4LELEMENT (v4lsrc)));
v4lsrc->autoprobe = g_value_get_boolean (value);
break;
case PROP_AUTOPROBE_FPS:
g_return_if_fail (!GST_V4L_IS_ACTIVE (GST_V4LELEMENT (v4lsrc)));
v4lsrc->autoprobe_fps = g_value_get_boolean (value);
break;
case PROP_TIMESTAMP_OFFSET:
v4lsrc->timestamp_offset = g_value_get_int (value);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
static void
gst_v4lsrc_get_property (GObject * object,
guint prop_id, GValue * value, GParamSpec * pspec)
{
GstV4lSrc *v4lsrc = GST_V4LSRC (object);
switch (prop_id) {
case PROP_AUTOPROBE:
g_value_set_boolean (value, v4lsrc->autoprobe);
break;
case PROP_AUTOPROBE_FPS:
g_value_set_boolean (value, v4lsrc->autoprobe_fps);
break;
case PROP_TIMESTAMP_OFFSET:
g_value_set_int (value, v4lsrc->timestamp_offset);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
}
/* this function is a bit of a last resort */
static void
gst_v4lsrc_fixate (GstPad * pad, GstCaps * caps)
{
GstStructure *structure;
int i;
int targetwidth, targetheight;
GstV4lSrc *v4lsrc = GST_V4LSRC (gst_pad_get_parent (pad));
struct video_capability *vcap = &GST_V4LELEMENT (v4lsrc)->vcap;
struct video_window *vwin = &GST_V4LELEMENT (v4lsrc)->vwin;
if (GST_V4L_IS_OPEN (GST_V4LELEMENT (v4lsrc))) {
GST_DEBUG_OBJECT (v4lsrc, "device reported w: %d-%d, h: %d-%d",
vcap->minwidth, vcap->maxwidth, vcap->minheight, vcap->maxheight);
targetwidth = vcap->minwidth;
targetheight = vcap->minheight;
/* if we can get the current vwin settings, we use those to fixate */
if (!gst_v4l_get_capabilities (GST_V4LELEMENT (v4lsrc)))
GST_DEBUG_OBJECT (v4lsrc, "failed getting capabilities");
else {
targetwidth = vwin->width;
targetheight = vwin->height;
}
} else {
GST_DEBUG_OBJECT (v4lsrc, "device closed, guessing");
targetwidth = 320;
targetheight = 200;
}
GST_DEBUG_OBJECT (v4lsrc, "targetting %dx%d", targetwidth, targetheight);
for (i = 0; i < gst_caps_get_size (caps); ++i) {
structure = gst_caps_get_structure (caps, i);
gst_caps_structure_fixate_field_nearest_int (structure, "width",
targetwidth);
gst_caps_structure_fixate_field_nearest_int (structure, "height",
targetheight);
gst_caps_structure_fixate_field_nearest_double (structure, "framerate",
7.5);
}
}
static gint all_palettes[] = {
VIDEO_PALETTE_YUV422,
VIDEO_PALETTE_YUV420P,
VIDEO_PALETTE_UYVY,
VIDEO_PALETTE_YUV411P,
VIDEO_PALETTE_YUV422P,
VIDEO_PALETTE_YUV410P,
VIDEO_PALETTE_YUV411,
VIDEO_PALETTE_RGB555,
VIDEO_PALETTE_RGB565,
VIDEO_PALETTE_RGB24,
VIDEO_PALETTE_RGB32,
-1
};
static GstCaps *
gst_v4lsrc_palette_to_caps (int palette)
{
guint32 fourcc;
GstCaps *caps;
switch (palette) {
case VIDEO_PALETTE_YUV422:
case VIDEO_PALETTE_YUYV:
fourcc = GST_MAKE_FOURCC ('Y', 'U', 'Y', '2');
break;
case VIDEO_PALETTE_YUV420P:
fourcc = GST_MAKE_FOURCC ('I', '4', '2', '0');
break;
case VIDEO_PALETTE_UYVY:
fourcc = GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y');
break;
case VIDEO_PALETTE_YUV411P:
fourcc = GST_MAKE_FOURCC ('Y', '4', '1', 'B');
break;
case VIDEO_PALETTE_YUV411:
fourcc = GST_MAKE_FOURCC ('Y', '4', '1', 'P');
break;
case VIDEO_PALETTE_YUV422P:
fourcc = GST_MAKE_FOURCC ('Y', '4', '2', 'B');
break;
case VIDEO_PALETTE_YUV410P:
fourcc = GST_MAKE_FOURCC ('Y', 'U', 'V', '9');
break;
case VIDEO_PALETTE_RGB555:
case VIDEO_PALETTE_RGB565:
case VIDEO_PALETTE_RGB24:
case VIDEO_PALETTE_RGB32:
fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', ' ');
break;
default:
return NULL;
}
if (fourcc == GST_MAKE_FOURCC ('R', 'G', 'B', ' ')) {
switch (palette) {
case VIDEO_PALETTE_RGB555:
caps = gst_caps_from_string ("video/x-raw-rgb, "
"bpp = (int) 16, "
"depth = (int) 15, "
"endianness = (int) BYTE_ORDER, "
"red_mask = 0x7c00, " "green_mask = 0x03e0, " "blue_mask = 0x001f");
break;
case VIDEO_PALETTE_RGB565:
caps = gst_caps_from_string ("video/x-raw-rgb, "
"bpp = (int) 16, "
"depth = (int) 16, "
"endianness = (int) BYTE_ORDER, "
"red_mask = 0xf800, " "green_mask = 0x07f0, " "blue_mask = 0x001f");
break;
case VIDEO_PALETTE_RGB24:
caps = gst_caps_from_string ("video/x-raw-rgb, "
"bpp = (int) 24, "
"depth = (int) 24, "
"endianness = (int) BIG_ENDIAN, "
"red_mask = 0x0000FF, "
"green_mask = 0x00FF00, " "blue_mask = 0xFF0000");
break;
case VIDEO_PALETTE_RGB32:
caps = gst_caps_from_string ("video/x-raw-rgb, "
"bpp = (int) 32, "
"depth = (int) 24, "
"endianness = (int) BIG_ENDIAN, "
"red_mask = 0xFF000000, "
"green_mask = 0x00FF0000, " "blue_mask = 0x0000FF00");
break;
default:
g_assert_not_reached ();
return NULL;
}
} else {
caps = gst_caps_new_simple ("video/x-raw-yuv",
"format", GST_TYPE_FOURCC, fourcc, NULL);
}
return caps;
}
static GstCaps *
gst_v4lsrc_get_any_caps ()
{
gint i;
GstCaps *caps = gst_caps_new_empty (), *one;
for (i = 0; all_palettes[i] != -1; i++) {
one = gst_v4lsrc_palette_to_caps (all_palettes[i]);
gst_caps_append (caps, one);
}
return caps;
}
static GstCaps *
gst_v4lsrc_get_caps (GstBaseSrc * src)
{
GstCaps *list;
GstV4lSrc *v4lsrc = GST_V4LSRC (src);
struct video_capability *vcap = &GST_V4LELEMENT (v4lsrc)->vcap;
gint width = GST_V4LELEMENT (src)->vcap.minwidth;
gint height = GST_V4LELEMENT (src)->vcap.minheight;
gint i;
gdouble fps;
GList *item;
if (!GST_V4L_IS_OPEN (GST_V4LELEMENT (v4lsrc))) {
return gst_v4lsrc_get_any_caps ();
}
if (!v4lsrc->autoprobe) {
/* FIXME: query current caps and return those, with _any appended */
return gst_v4lsrc_get_any_caps ();
}
if (!v4lsrc->colorspaces) {
GST_DEBUG_OBJECT (v4lsrc, "Checking supported palettes");
for (i = 0; all_palettes[i] != -1; i++) {
/* try palette out */
if (!gst_v4lsrc_try_capture (v4lsrc, width, height, all_palettes[i]))
continue;
GST_DEBUG_OBJECT (v4lsrc, "Added palette %d (%s) to supported list",
all_palettes[i], gst_v4lsrc_palette_name (all_palettes[i]));
v4lsrc->colorspaces = g_list_append (v4lsrc->colorspaces,
GINT_TO_POINTER (all_palettes[i]));
}
GST_DEBUG_OBJECT (v4lsrc, "%d palette(s) supported",
g_list_length (v4lsrc->colorspaces));
if (v4lsrc->autoprobe_fps) {
GST_DEBUG_OBJECT (v4lsrc, "autoprobing framerates");
v4lsrc->fps_list = gst_v4lsrc_get_fps_list (v4lsrc);
}
}
fps = gst_v4lsrc_get_fps (v4lsrc);
list = gst_caps_new_empty ();
for (item = v4lsrc->colorspaces; item != NULL; item = item->next) {
GstCaps *one;
one = gst_v4lsrc_palette_to_caps (GPOINTER_TO_INT (item->data));
if (!one) {
GST_WARNING_OBJECT (v4lsrc, "Palette %d gave no caps\n",
GPOINTER_TO_INT (item->data));
continue;
}
GST_DEBUG_OBJECT (v4lsrc,
"Device reports w: %d-%d, h: %d-%d, fps: %f for palette %d",
vcap->minwidth, vcap->maxwidth, vcap->minheight, vcap->maxheight, fps,
GPOINTER_TO_INT (item->data));
if (vcap->minwidth < vcap->maxwidth) {
gst_caps_set_simple (one, "width", GST_TYPE_INT_RANGE, vcap->minwidth,
vcap->maxwidth, NULL);
} else {
gst_caps_set_simple (one, "width", G_TYPE_INT, vcap->minwidth, NULL);
}
if (vcap->minheight < vcap->maxheight) {
gst_caps_set_simple (one, "height", GST_TYPE_INT_RANGE, vcap->minheight,
vcap->maxheight, NULL);
} else {
gst_caps_set_simple (one, "height", G_TYPE_INT, vcap->minheight, NULL);
}
if (v4lsrc->autoprobe_fps) {
if (v4lsrc->fps_list) {
GstStructure *structure = gst_caps_get_structure (one, 0);
gst_structure_set_value (structure, "framerate", v4lsrc->fps_list);
} else {
gst_caps_set_simple (one, "framerate", G_TYPE_DOUBLE, fps, NULL);
}
} else {
gst_caps_set_simple (one, "framerate", GST_TYPE_DOUBLE_RANGE,
(gdouble) 1.0, (gdouble) 100.0, NULL);
}
GST_DEBUG_OBJECT (v4lsrc, "caps: %" GST_PTR_FORMAT, one);
gst_caps_append (list, one);
}
return list;
}
static gboolean
gst_v4lsrc_set_caps (GstBaseSrc * src, GstCaps * caps)
{
GstV4lSrc *v4lsrc;
guint32 fourcc;
gint bpp, depth, w, h, palette = -1;
gdouble fps;
GstStructure *structure;
struct video_window *vwin;
v4lsrc = GST_V4LSRC (src);
vwin = &GST_V4LELEMENT (v4lsrc)->vwin;
/* if we're not open, punt -- we'll get setcaps'd later via negotiate */
if (!GST_V4L_IS_OPEN (v4lsrc))
return FALSE;
/* make sure we stop capturing and dealloc buffers */
if (GST_V4L_IS_ACTIVE (v4lsrc)) {
if (!gst_v4lsrc_capture_stop (v4lsrc))
return FALSE;
if (!gst_v4lsrc_capture_deinit (v4lsrc))
return FALSE;
}
/* it's fixed, one struct */
structure = gst_caps_get_structure (caps, 0);
if (strcmp (gst_structure_get_name (structure), "video/x-raw-yuv") == 0)
gst_structure_get_fourcc (structure, "format", &fourcc);
else
fourcc = GST_MAKE_FOURCC ('R', 'G', 'B', ' ');
gst_structure_get_int (structure, "width", &w);
gst_structure_get_int (structure, "height", &h);
gst_structure_get_double (structure, "framerate", &fps);
GST_DEBUG_OBJECT (v4lsrc, "linking with %dx%d at %f fps", w, h, fps);
/* set framerate if it's not already correct */
if (fps != gst_v4lsrc_get_fps (v4lsrc)) {
int fps_index = fps / 15.0 * 16;
GST_DEBUG_OBJECT (v4lsrc, "Trying to set fps index %d", fps_index);
/* set bits 16 to 21 to 0 */
vwin->flags &= (0x3F00 - 1);
/* set bits 16 to 21 to the index */
vwin->flags |= fps_index << 16;
if (!gst_v4l_set_window_properties (GST_V4LELEMENT (v4lsrc))) {
return FALSE;
}
}
switch (fourcc) {
case GST_MAKE_FOURCC ('I', '4', '2', '0'):
palette = VIDEO_PALETTE_YUV420P;
v4lsrc->buffer_size = ((w + 1) & ~1) * ((h + 1) & ~1) * 1.5;
break;
case GST_MAKE_FOURCC ('Y', 'U', 'Y', '2'):
palette = VIDEO_PALETTE_YUV422;
v4lsrc->buffer_size = ((w + 1) & ~1) * h * 2;
break;
case GST_MAKE_FOURCC ('U', 'Y', 'V', 'Y'):
palette = VIDEO_PALETTE_UYVY;
v4lsrc->buffer_size = ((w + 1) & ~1) * h * 2;
break;
case GST_MAKE_FOURCC ('Y', '4', '1', 'B'):
palette = VIDEO_PALETTE_YUV411P;
v4lsrc->buffer_size = ((w + 3) & ~3) * h * 1.5;
break;
case GST_MAKE_FOURCC ('Y', '4', '1', 'P'):
palette = VIDEO_PALETTE_YUV411;
v4lsrc->buffer_size = ((w + 3) & ~3) * h * 1.5;
break;
case GST_MAKE_FOURCC ('Y', 'U', 'V', '9'):
palette = VIDEO_PALETTE_YUV410P;
v4lsrc->buffer_size = ((w + 3) & ~3) * ((h + 3) & ~3) * 1.125;
break;
case GST_MAKE_FOURCC ('Y', '4', '2', 'B'):
palette = VIDEO_PALETTE_YUV422P;
v4lsrc->buffer_size = ((w + 1) & ~1) * h * 2;
break;
case GST_MAKE_FOURCC ('R', 'G', 'B', ' '):
gst_structure_get_int (structure, "depth", &depth);
switch (depth) {
case 15:
palette = VIDEO_PALETTE_RGB555;
v4lsrc->buffer_size = w * h * 2;
break;
case 16:
palette = VIDEO_PALETTE_RGB565;
v4lsrc->buffer_size = w * h * 2;
break;
case 24:
gst_structure_get_int (structure, "bpp", &bpp);
switch (bpp) {
case 24:
palette = VIDEO_PALETTE_RGB24;
v4lsrc->buffer_size = w * h * 3;
break;
case 32:
palette = VIDEO_PALETTE_RGB32;
v4lsrc->buffer_size = w * h * 4;
break;
default:
break;
}
break;
default:
break;
}
break;
default:
break;
}
if (palette == -1) {
GST_WARNING_OBJECT (v4lsrc, "palette for fourcc " GST_FOURCC_FORMAT
" is -1, refusing link", GST_FOURCC_ARGS (fourcc));
return FALSE;
}
GST_DEBUG_OBJECT (v4lsrc, "trying to set_capture %dx%d, palette %d",
w, h, palette);
/* this only fills in v4lsrc->mmap values */
if (!gst_v4lsrc_set_capture (v4lsrc, w, h, palette)) {
GST_WARNING_OBJECT (v4lsrc, "could not set_capture %dx%d, palette %d",
w, h, palette);
return FALSE;
}
/* first try the negotiated settings using try_capture */
if (!gst_v4lsrc_try_capture (v4lsrc, w, h, palette)) {
GST_DEBUG_OBJECT (v4lsrc, "failed trying palette %d for %dx%d", palette,
w, h);
return FALSE;
}
if (!gst_v4lsrc_capture_init (v4lsrc))
return FALSE;
if (!gst_v4lsrc_capture_start (v4lsrc))
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_v4lsrc_start (GstBaseSrc * src)
{
GstV4lSrc *v4lsrc = GST_V4LSRC (src);
if (!GST_BASE_SRC_CLASS (parent_class)->start (src))
return FALSE;
v4lsrc->offset = 0;
return TRUE;
}
static gboolean
gst_v4lsrc_stop (GstBaseSrc * src)
{
GstV4lSrc *v4lsrc = GST_V4LSRC (src);
if (!gst_v4lsrc_capture_stop (v4lsrc))
return FALSE;
if (GST_V4LELEMENT (v4lsrc)->buffer != NULL) {
if (!gst_v4lsrc_capture_deinit (v4lsrc))
return FALSE;
}
if (!GST_BASE_SRC_CLASS (parent_class)->stop (src))
return FALSE;
g_list_free (v4lsrc->colorspaces);
v4lsrc->colorspaces = NULL;
if (v4lsrc->fps_list) {
g_value_unset (v4lsrc->fps_list);
g_free (v4lsrc->fps_list);
v4lsrc->fps_list = NULL;
}
return TRUE;
}
static GstFlowReturn
gst_v4lsrc_create (GstPushSrc * src, GstBuffer ** buf)
{
GstV4lSrc *v4lsrc;
gint num;
v4lsrc = GST_V4LSRC (src);
/* grab a frame from the device */
if (!gst_v4lsrc_grab_frame (v4lsrc, &num))
return GST_FLOW_ERROR;
*buf = gst_v4lsrc_buffer_new (v4lsrc, num);
#if 0
GstBuffer *copy = gst_buffer_copy (buf);
gst_buffer_unref (buf);
buf = copy;
#endif
return GST_FLOW_OK;
}