diff options
Diffstat (limited to 'sys/v4l2/gstv4l2src.c')
-rw-r--r-- | sys/v4l2/gstv4l2src.c | 837 |
1 files changed, 837 insertions, 0 deletions
diff --git a/sys/v4l2/gstv4l2src.c b/sys/v4l2/gstv4l2src.c new file mode 100644 index 00000000..7dce792d --- /dev/null +++ b/sys/v4l2/gstv4l2src.c @@ -0,0 +1,837 @@ +/* G-Streamer Video4linux2 video-capture plugin + * Copyright (C) 2002 Ronald Bultje <rbultje@ronald.bitfreak.net> + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ + +#include <string.h> +#include <sys/time.h> +#include "v4l2src_calls.h" + + +static GstElementDetails gst_v4l2src_details = { + "Video (video4linux2) Source", + "Source/Video", + "Reads frames (compressed or uncompressed) from a video4linux2 device", + VERSION, + "Ronald Bultje <rbultje@ronald.bitfreak.net>", + "(C) 2002", +}; + +/* V4l2Src signals and args */ +enum { + /* FILL ME */ + LAST_SIGNAL +}; + +/* arguments */ +enum { + ARG_0, + ARG_WIDTH, + ARG_HEIGHT, + ARG_PALETTE, + ARG_PALETTE_NAMES, + ARG_FOURCC, + ARG_FOURCC_LIST, + ARG_NUMBUFS, + ARG_BUFSIZE +}; + + +/* init functions */ +static void gst_v4l2src_class_init (GstV4l2SrcClass *klass); +static void gst_v4l2src_init (GstV4l2Src *v4l2src); + +/* pad/buffer functions */ +static GstPadConnectReturn gst_v4l2src_srcconnect (GstPad *pad, + GstCaps *caps); +static GstBuffer * gst_v4l2src_get (GstPad *pad); + +/* get/set params */ +static void gst_v4l2src_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec); +static void gst_v4l2src_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec); + +/* state handling */ +static GstElementStateReturn gst_v4l2src_change_state (GstElement *element); + +/* bufferpool functions */ +static GstBuffer * gst_v4l2src_buffer_new (GstBufferPool *pool, + guint64 offset, + guint size, + gpointer user_data); +static GstBuffer * gst_v4l2src_buffer_copy (GstBufferPool *pool, + const GstBuffer *srcbuf, + gpointer user_data); +static void gst_v4l2src_buffer_free (GstBufferPool *pool, + GstBuffer *buf, + gpointer user_data); + + +static GstPadTemplate *src_template; + +static GstElementClass *parent_class = NULL; +/*static guint gst_v4l2src_signals[LAST_SIGNAL] = { 0 }; */ + + +GType +gst_v4l2src_get_type (void) +{ + static GType v4l2src_type = 0; + + if (!v4l2src_type) { + static const GTypeInfo v4l2src_info = { + sizeof(GstV4l2SrcClass), + NULL, + NULL, + (GClassInitFunc) gst_v4l2src_class_init, + NULL, + NULL, + sizeof(GstV4l2Src), + 0, + (GInstanceInitFunc) gst_v4l2src_init, + NULL + }; + v4l2src_type = g_type_register_static(GST_TYPE_V4L2ELEMENT, + "GstV4l2Src", &v4l2src_info, 0); + } + return v4l2src_type; +} + + +static void +gst_v4l2src_class_init (GstV4l2SrcClass *klass) +{ + GObjectClass *gobject_class; + GstElementClass *gstelement_class; + + gobject_class = (GObjectClass*)klass; + gstelement_class = (GstElementClass*)klass; + + parent_class = g_type_class_ref(GST_TYPE_V4L2ELEMENT); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_WIDTH, + g_param_spec_int("width","width","width", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_HEIGHT, + g_param_spec_int("height","height","height", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PALETTE, + g_param_spec_int("palette","palette","palette", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_PALETTE_NAMES, + g_param_spec_pointer("palette_name","palette_name","palette_name", + G_PARAM_READABLE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FOURCC, + g_param_spec_string("fourcc","fourcc","fourcc", + NULL,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_FOURCC_LIST, + g_param_spec_pointer("fourcc_list","fourcc_list","fourcc_list", + G_PARAM_READABLE)); + + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_NUMBUFS, + g_param_spec_int("num_buffers","num_buffers","num_buffers", + G_MININT,G_MAXINT,0,G_PARAM_READWRITE)); + g_object_class_install_property(G_OBJECT_CLASS(klass), ARG_BUFSIZE, + g_param_spec_int("buffer_size","buffer_size","buffer_size", + G_MININT,G_MAXINT,0,G_PARAM_READABLE)); + + gobject_class->set_property = gst_v4l2src_set_property; + gobject_class->get_property = gst_v4l2src_get_property; + + gstelement_class->change_state = gst_v4l2src_change_state; +} + + +static void +gst_v4l2src_init (GstV4l2Src *v4l2src) +{ + v4l2src->srcpad = gst_pad_new_from_template(src_template, "src"); + gst_element_add_pad(GST_ELEMENT(v4l2src), v4l2src->srcpad); + + gst_pad_set_get_function(v4l2src->srcpad, gst_v4l2src_get); + gst_pad_set_connect_function(v4l2src->srcpad, gst_v4l2src_srcconnect); + + v4l2src->bufferpool = gst_buffer_pool_new(NULL, NULL, + gst_v4l2src_buffer_new, + gst_v4l2src_buffer_copy, + gst_v4l2src_buffer_free, + v4l2src); + + v4l2src->palette = 0; /* means 'any' - user can specify a specific palette */ + v4l2src->width = 160; + v4l2src->height = 120; + v4l2src->breq.count = 0; +} + + +static GstCaps * +gst_v4l2src_v4l2fourcc_to_caps (guint32 fourcc, + gint width, + gint height, + gboolean compressed) +{ + GstCaps *capslist = NULL, *caps; + + switch (fourcc) { + case V4L2_PIX_FMT_MJPEG: /* v4l2_fourcc('M','J','P','G') */ + caps = gst_caps_new("v4l2src_caps", + "video/jpeg", + gst_props_new( + "width", GST_PROPS_INT(width), + "height", GST_PROPS_INT(height), + NULL)); + capslist = gst_caps_append(capslist, caps); + break; + case V4L2_PIX_FMT_RGB332: + case V4L2_PIX_FMT_RGB555: + case V4L2_PIX_FMT_RGB555X: + case V4L2_PIX_FMT_RGB565: + case V4L2_PIX_FMT_RGB565X: + case V4L2_PIX_FMT_RGB24: + case V4L2_PIX_FMT_BGR24: + case V4L2_PIX_FMT_RGB32: + case V4L2_PIX_FMT_BGR32: { + guint depth=0, bpp=0; + gint endianness=0; + gulong r_mask=0, b_mask=0, g_mask=0; + switch (fourcc) { + case V4L2_PIX_FMT_RGB332: + bpp = depth = 8; + endianness = G_BYTE_ORDER; /* 'like, whatever' */ + r_mask = 0xe0; g_mask = 0x1c; b_mask = 0x03; + break; + case V4L2_PIX_FMT_RGB555: + bpp = 16; depth = 15; + endianness = G_LITTLE_ENDIAN; + r_mask = 0x7c00; g_mask = 0x03e0; b_mask = 0x001f; + break; + case V4L2_PIX_FMT_RGB555X: + bpp = 16; depth = 15; + endianness = G_BIG_ENDIAN; + r_mask = 0x7c00; g_mask = 0x03e0; b_mask = 0x001f; + break; + case V4L2_PIX_FMT_RGB565: + bpp = depth = 16; + endianness = G_LITTLE_ENDIAN; + r_mask = 0xf800; g_mask = 0x07e0; b_mask = 0x001f; + break; + case V4L2_PIX_FMT_RGB565X: + bpp = depth = 16; + endianness = G_BIG_ENDIAN; + r_mask = 0xf800; g_mask = 0x07e0; b_mask = 0x001f; + break; + case V4L2_PIX_FMT_RGB24: + bpp = depth = 24; + endianness = G_BIG_ENDIAN; + r_mask = 0xff0000; g_mask = 0x00ff00; b_mask = 0x0000ff; + break; + case V4L2_PIX_FMT_BGR24: + bpp = depth = 24; + endianness = G_LITTLE_ENDIAN; + r_mask = 0xff0000; g_mask = 0x00ff00; b_mask = 0x0000ff; + break; + case V4L2_PIX_FMT_RGB32: + bpp = depth = 32; + endianness = G_BIG_ENDIAN; + r_mask = 0x00ff0000; g_mask = 0x0000ff00; b_mask = 0x000000ff; + break; + case V4L2_PIX_FMT_BGR32: + endianness = G_LITTLE_ENDIAN; + bpp = depth = 32; + r_mask = 0x00ff0000; g_mask = 0x0000ff00; b_mask = 0x000000ff; + break; + } + caps = gst_caps_new("v4l2src_caps", + "video/raw", + gst_props_new( + "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('R','G','B',' ')), + "width", GST_PROPS_INT(width), + "height", GST_PROPS_INT(height), + "bpp", GST_PROPS_INT(bpp), + "depth", GST_PROPS_INT(depth), + "red_mask", GST_PROPS_INT(r_mask), + "green_mask", GST_PROPS_INT(g_mask), + "blue_mask", GST_PROPS_INT(b_mask), + "endianness", GST_PROPS_INT(endianness), + NULL)); + capslist = gst_caps_append(capslist, caps); + break; } + case V4L2_PIX_FMT_YUV420: /* I420/IYUV */ + caps = gst_caps_new("v4l2src_caps", + "video/raw", + gst_props_new( + "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','4','2','0')), + "width", GST_PROPS_INT(width), + "height", GST_PROPS_INT(height), + NULL)); + capslist = gst_caps_append(capslist, caps); + caps = gst_caps_new("v4l2src_caps", + "video/raw", + gst_props_new( + "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('I','Y','U','V')), + "width", GST_PROPS_INT(width), + "height", GST_PROPS_INT(height), + NULL)); + capslist = gst_caps_append(capslist, caps); + break; + case V4L2_PIX_FMT_YUYV: + caps = gst_caps_new("v4l2src_caps", + "video/raw", + gst_props_new( + "format", GST_PROPS_FOURCC(GST_MAKE_FOURCC('Y','U','Y','2')), + "width", GST_PROPS_INT(width), + "height", GST_PROPS_INT(height), + NULL)); + capslist = gst_caps_append(capslist, caps); + break; + default: + break; + } + + /* add the standard one */ + if (compressed) { + guint32 print_format = GUINT32_FROM_LE(fourcc); + gchar *print_format_str = (gchar *) &print_format, *string_format; + gint i; + for (i=0;i<4;i++) + print_format_str[i] = g_ascii_tolower(print_format_str[i]); + string_format = g_strdup_printf("video/%4.4s", print_format_str); + caps = gst_caps_new("v4l2src_caps", + string_format, + gst_props_new( + "width", GST_PROPS_INT(width), + "height", GST_PROPS_INT(height), + NULL)); + capslist = gst_caps_append(capslist, caps); + g_free(string_format); + } else { + caps = gst_caps_new("v4l2src_caps", + "video/raw", + gst_props_new( + "format", GST_PROPS_FOURCC(fourcc), + "width", GST_PROPS_INT(width), + "height", GST_PROPS_INT(height), + NULL)); + capslist = gst_caps_append(capslist, caps); + } + + return capslist; +} + + +static GList * +gst_v4l2_caps_to_v4l2fourcc (GstV4l2Src *v4l2src, + GstCaps *capslist) +{ + GList *fourcclist = NULL; + GstCaps *caps; + guint32 fourcc; + gint i; + + for (caps = capslist;caps != NULL; caps = caps->next) { + const gchar *format = gst_caps_get_mime(caps); + + if (!strcmp(format, "video/raw")) { + /* non-compressed */ + gst_caps_get_fourcc_int(caps, "format", &fourcc); + switch (fourcc) { + case GST_MAKE_FOURCC('I','4','2','0'): + case GST_MAKE_FOURCC('I','Y','U','V'): + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_YUV420); + break; + case GST_MAKE_FOURCC('Y','U','Y','2'): + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_YUYV); + break; + case GST_MAKE_FOURCC('R','G','B',' '): { + gint depth, endianness; + gst_caps_get_int(caps, "depth", &depth); + gst_caps_get_int(caps, "endianness", &endianness); + if (depth == 8) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB332); + } else if (depth == 15 && endianness == G_LITTLE_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB555); + } else if (depth == 15 && endianness == G_BIG_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB555X); + } else if (depth == 16 && endianness == G_LITTLE_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB565); + } else if (depth == 16 && endianness == G_BIG_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB565X); + } else if (depth == 24 && endianness == G_LITTLE_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_BGR24); + } else if (depth == 24 && endianness == G_BIG_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB24); + } else if (depth == 32 && endianness == G_LITTLE_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_BGR32); + } else if (depth == 32 && endianness == G_BIG_ENDIAN) { + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_RGB32); + } + break; } + } + + for (i=0;i<g_list_length(GST_V4L2ELEMENT(v4l2src)->formats);i++) { + struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *) g_list_nth_data(GST_V4L2ELEMENT(v4l2src)->formats, i); + if (fmt->pixelformat == fourcc) + fourcclist = g_list_append(fourcclist, (gpointer)fourcc); + } + } else { + /* compressed */ + gchar *format_us; + gint i; + if (strncmp(format, "video/", 6)) + continue; + format = &format[6]; + if (strlen(format) != 4) + continue; + format_us = g_strdup(format); + for (i=0;i<4;i++) + format_us[i] = g_ascii_toupper(format_us[i]); + fourcc = GST_MAKE_FOURCC(format_us[0],format_us[1],format_us[2],format_us[3]); + switch (fourcc) { + case GST_MAKE_FOURCC('J','P','E','G'): + fourcclist = g_list_append(fourcclist, (gpointer)V4L2_PIX_FMT_MJPEG); + break; + } + fourcclist = g_list_append(fourcclist, (gpointer)fourcc); + } + } + + return fourcclist; +} + + +static GstCaps * +gst_v4l2src_caps_intersect (GstCaps *caps1, + GstCaps *_caps2) +{ + GstCaps *_list = NULL; + + if (!_caps2) + return caps1; + + for (;caps1!=NULL;caps1=caps1->next) { + GstCaps *caps2 = _caps2; + + for (;caps2!=NULL;caps2=caps2->next) { + if (!strcmp(gst_caps_get_mime(caps1), gst_caps_get_mime(caps2))) { + if (!strcmp(gst_caps_get_mime(caps1), "video/raw")) { + guint32 fmt1, fmt2; + gst_caps_get_fourcc_int(caps1, "format", &fmt1); + gst_caps_get_fourcc_int(caps2, "format", &fmt2); + if (fmt1 == fmt2) + goto try_it_out; + } else if (!strncmp(gst_caps_get_mime(caps1), "video/", 6)) { + goto try_it_out; + } + continue; + } + continue; + + try_it_out: + if (!strcmp(gst_caps_get_mime(caps1), "video/raw")) { + GstCaps *list = _list; + for (;list!=NULL;list=list->next) { + if (!strcmp(gst_caps_get_mime(list), gst_caps_get_mime(caps1))) { + guint32 fmt1, fmt2; + gst_caps_get_fourcc_int(caps1, "format", &fmt1); + gst_caps_get_fourcc_int(list, "format", &fmt2); + if (fmt1 == fmt2) + break; + } + } + if (list == NULL) + goto add_it; + } else { + GstCaps *list = _list; + for (;list!=NULL;list=list->next) { + if (!strcmp(gst_caps_get_mime(list), gst_caps_get_mime(caps1))) { + break; + } + } + if (list == NULL) + goto add_it; + } + continue; + + add_it: + _list = gst_caps_append(_list, gst_caps_copy_1(caps1)); + break; + } + } + + return _list; +} + + +static GstPadConnectReturn +gst_v4l2src_srcconnect (GstPad *pad, + GstCaps *vscapslist) +{ + GstV4l2Src *v4l2src; + GstV4l2Element *v4l2element; + GstCaps *owncapslist; + GstCaps *caps; + GList *fourccs; + gint i; + + v4l2src = GST_V4L2SRC(gst_pad_get_parent (pad)); + v4l2element = GST_V4L2ELEMENT(v4l2src); + + /* clean up if we still haven't cleaned up our previous + * capture session */ + if (GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) + if (!gst_v4l2src_capture_deinit(v4l2src)) + return GST_PAD_CONNECT_REFUSED; + + /* build our own capslist */ + if (v4l2src->palette) { + struct v4l2_fmtdesc *format = g_list_nth_data(v4l2element->formats, v4l2src->palette); + owncapslist = gst_v4l2src_v4l2fourcc_to_caps(format->pixelformat, + v4l2src->width, v4l2src->height, + format->flags & V4L2_FMT_FLAG_COMPRESSED); + } else { + gint i; + owncapslist = NULL; + for (i=0;i<g_list_length(v4l2element->formats);i++) { + struct v4l2_fmtdesc *format = g_list_nth_data(v4l2element->formats, i); + caps = gst_v4l2src_v4l2fourcc_to_caps(format->pixelformat, + v4l2src->width, v4l2src->height, + format->flags & V4L2_FMT_FLAG_COMPRESSED); + owncapslist = gst_caps_append(owncapslist, caps); + } + } + + /* and now, get the caps that we have in common */ + if (!(caps = gst_v4l2src_caps_intersect(owncapslist, vscapslist))) + return GST_PAD_CONNECT_REFUSED; + + /* and get them back to V4L2-compatible fourcc codes */ + if (!(fourccs = gst_v4l2_caps_to_v4l2fourcc(v4l2src, caps))) + return GST_PAD_CONNECT_REFUSED; + + /* we now have the fourcc codes. try out each of them */ + for (i=0;i<g_list_length(fourccs);i++) { + guint32 fourcc = (guint32)g_list_nth_data(fourccs, i); + gint n; + for (n=0;n<g_list_length(v4l2element->formats);n++) { + struct v4l2_fmtdesc *format = g_list_nth_data(v4l2element->formats, n); + if (format->pixelformat == fourcc) { + /* we found the pixelformat! - try it out */ + if (gst_v4l2src_set_capture(v4l2src, format, + v4l2src->width, v4l2src->height)) { + /* it fits! Now, get the proper counterpart and retry + * it on the other side (again...) - if it works, we're + * done -> GST_PAD_CONNECT_OK */ + GstCaps *lastcaps = gst_v4l2src_v4l2fourcc_to_caps(format->pixelformat, + v4l2src->format.fmt.pix.width, v4l2src->format.fmt.pix.height, + format->flags & V4L2_FMT_FLAG_COMPRESSED); + GstCaps *onecaps; + for (;lastcaps != NULL; lastcaps = lastcaps->next) { + onecaps = gst_caps_copy_1(lastcaps); + if (gst_pad_try_set_caps(v4l2src->srcpad, onecaps)) + if (gst_v4l2src_capture_init(v4l2src)) + return GST_PAD_CONNECT_OK; + } + } + } + } + } + + return GST_PAD_CONNECT_REFUSED; +} + + +static GstBuffer* +gst_v4l2src_get (GstPad *pad) +{ + GstV4l2Src *v4l2src; + GstBuffer *buf; + gint num; + + g_return_val_if_fail (pad != NULL, NULL); + + v4l2src = GST_V4L2SRC(gst_pad_get_parent (pad)); + + buf = gst_buffer_new_from_pool(v4l2src->bufferpool, 0, 0); + if (!buf) { + gst_element_error(GST_ELEMENT(v4l2src), + "Failed to create a new GstBuffer"); + return NULL; + } + + /* grab a frame from the device */ + if (!gst_v4l2src_grab_frame(v4l2src, &num)) + return NULL; + GST_BUFFER_DATA(buf) = GST_V4L2ELEMENT(v4l2src)->buffer[num]; + GST_BUFFER_SIZE(buf) = v4l2src->bufsettings.bytesused; + GST_BUFFER_MAXSIZE(buf) = v4l2src->bufsettings.length; + if (!v4l2src->first_timestamp) + v4l2src->first_timestamp = v4l2src->bufsettings.timestamp; + GST_BUFFER_TIMESTAMP(buf) = v4l2src->bufsettings.length - v4l2src->first_timestamp; + + return buf; +} + + +static void +gst_v4l2src_set_property (GObject *object, + guint prop_id, + const GValue *value, + GParamSpec *pspec) +{ + GstV4l2Src *v4l2src; + + g_return_if_fail(GST_IS_V4L2SRC(object)); + v4l2src = GST_V4L2SRC(object); + + switch (prop_id) { + case ARG_WIDTH: + if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) { + v4l2src->width = g_value_get_int(value); + } + break; + + case ARG_HEIGHT: + if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) { + v4l2src->height = g_value_get_int(value); + } + break; + + case ARG_PALETTE: + if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) { + v4l2src->palette = g_value_get_int(value); + } + break; + + case ARG_FOURCC: + if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) { + gint i; + const gchar *formatstr = g_value_get_string(value); + guint32 fourcc = GST_MAKE_FOURCC(formatstr[0],formatstr[1],formatstr[2],formatstr[3]); + for (i=0;i<g_list_length(GST_V4L2ELEMENT(v4l2src)->formats);i++) { + struct v4l2_fmtdesc *fmt = (struct v4l2_fmtdesc *) g_list_nth_data(GST_V4L2ELEMENT(v4l2src)->formats, i); + if (fmt->pixelformat == fourcc) + v4l2src->palette = i; + } + } + break; + + case ARG_NUMBUFS: + if (!GST_V4L2_IS_ACTIVE(GST_V4L2ELEMENT(v4l2src))) { + v4l2src->breq.count = g_value_get_int(value); + } + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static void +gst_v4l2src_get_property (GObject *object, + guint prop_id, + GValue *value, + GParamSpec *pspec) +{ + GstV4l2Src *v4l2src; + + g_return_if_fail(GST_IS_V4L2SRC(object)); + v4l2src = GST_V4L2SRC(object); + + switch (prop_id) { + case ARG_WIDTH: + g_value_set_int(value, v4l2src->width); + break; + + case ARG_HEIGHT: + g_value_set_int(value, v4l2src->height); + break; + + case ARG_PALETTE: + g_value_set_int(value, v4l2src->palette); + break; + + case ARG_PALETTE_NAMES: + g_value_set_pointer(value, gst_v4l2src_get_format_list(v4l2src)); + break; + + case ARG_FOURCC: { + struct v4l2_fmtdesc *fmt = g_list_nth_data(GST_V4L2ELEMENT(v4l2src)->formats, v4l2src->palette); + guint32 print_format = GUINT32_FROM_LE(fmt->pixelformat); + gchar *print_format_str = (gchar *) &print_format; + g_value_set_string(value, g_strndup(print_format_str, 4)); + break; } + + case ARG_FOURCC_LIST: + g_value_set_pointer(value, gst_v4l2src_get_fourcc_list(v4l2src)); + break; + + case ARG_NUMBUFS: + g_value_set_int(value, v4l2src->breq.count); + break; + + case ARG_BUFSIZE: + g_value_set_int(value, v4l2src->format.fmt.pix.sizeimage); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); + break; + } +} + + +static GstElementStateReturn +gst_v4l2src_change_state (GstElement *element) +{ + GstV4l2Src *v4l2src; + gint transition = GST_STATE_TRANSITION (element); + GstElementStateReturn parent_return; + + g_return_val_if_fail(GST_IS_V4L2SRC(element), GST_STATE_FAILURE); + v4l2src = GST_V4L2SRC(element); + + if (GST_ELEMENT_CLASS (parent_class)->change_state) { + parent_return = GST_ELEMENT_CLASS (parent_class)->change_state (element); + if (parent_return != GST_STATE_SUCCESS) + return parent_return; + } + + switch (transition) { + case GST_STATE_NULL_TO_READY: + if (!gst_v4l2src_get_capture(v4l2src)) + return GST_STATE_FAILURE; + break; + case GST_STATE_READY_TO_PAUSED: + v4l2src->first_timestamp = 0; + /* buffer setup moved to capsnego */ + break; + case GST_STATE_PAUSED_TO_PLAYING: + /* queue all buffer, start streaming capture */ + if (!gst_v4l2src_capture_start(v4l2src)) + return GST_STATE_FAILURE; + break; + case GST_STATE_PLAYING_TO_PAUSED: + /* de-queue all queued buffers */ + if (!gst_v4l2src_capture_stop(v4l2src)) + return GST_STATE_FAILURE; + break; + case GST_STATE_PAUSED_TO_READY: + /* stop capturing, unmap all buffers */ + if (!gst_v4l2src_capture_deinit(v4l2src)) + return GST_STATE_FAILURE; + break; + case GST_STATE_READY_TO_NULL: + break; + } + + return GST_STATE_SUCCESS; +} + + +static GstBuffer* +gst_v4l2src_buffer_new (GstBufferPool *pool, + guint64 offset, + guint size, + gpointer user_data) +{ + GstBuffer *buffer; + + buffer = gst_buffer_new(); + if (!buffer) return NULL; + /* TODO: add interlacing info to buffer as metadata (height>288 or 240 = topfieldfirst, else noninterlaced) */ + + return buffer; +} + + +static GstBuffer* +gst_v4l2src_buffer_copy (GstBufferPool *pool, + const GstBuffer *srcbuf, + gpointer user_data) +{ + GstBuffer *buffer; + + buffer = gst_buffer_new(); + if (!buffer) return NULL; + GST_BUFFER_DATA(buffer) = g_malloc(GST_BUFFER_SIZE(srcbuf)); + if (!GST_BUFFER_DATA(buffer)) return NULL; + GST_BUFFER_MAXSIZE(buffer) = GST_BUFFER_SIZE(srcbuf); + GST_BUFFER_SIZE(buffer) = GST_BUFFER_SIZE(srcbuf); + memcpy(GST_BUFFER_DATA(buffer), GST_BUFFER_DATA(srcbuf), GST_BUFFER_SIZE(srcbuf)); + GST_BUFFER_TIMESTAMP(buffer) = GST_BUFFER_TIMESTAMP(srcbuf); + + return buffer; +} + + +static void +gst_v4l2src_buffer_free (GstBufferPool *pool, + GstBuffer *buf, + gpointer user_data) +{ + GstV4l2Src *v4l2src = GST_V4L2SRC(user_data); + int n; + + for (n=0;n<v4l2src->breq.count;n++) + if (GST_BUFFER_DATA(buf) == GST_V4L2ELEMENT(v4l2src)->buffer[n]) { + gst_v4l2src_requeue_frame(v4l2src, n); + return; + } + + gst_element_error(GST_ELEMENT(v4l2src), + "Couldn\'t find the buffer"); +} + + +static gboolean +plugin_init (GModule *module, + GstPlugin *plugin) +{ + GstElementFactory *factory; + + /* create an elementfactory for the v4l2src */ + factory = gst_element_factory_new("v4l2src", GST_TYPE_V4L2SRC, + &gst_v4l2src_details); + g_return_val_if_fail(factory != NULL, FALSE); + + src_template = gst_pad_template_new("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + NULL); + + gst_element_factory_add_pad_template(factory, src_template); + + gst_plugin_add_feature (plugin, GST_PLUGIN_FEATURE(factory)); + + return TRUE; +} + + +GstPluginDesc plugin_desc = { + GST_VERSION_MAJOR, + GST_VERSION_MINOR, + "v4l2src", + plugin_init +}; |