From 20538dadc99a42339d7c1d19d12f3323858adfe6 Mon Sep 17 00:00:00 2001 From: "Ronald S. Bultje" Date: Fri, 20 Sep 2002 09:28:46 +0000 Subject: [PATCH] This patch fixes some issues caused by design issues in video4linux, adds some nicety to video4linux2 plugins and doe... Original commit message from CVS: This patch fixes some issues caused by design issues in video4linux, adds some nicety to video4linux2 plugins and does some more evil stuff: * video4linux doesn't tell us which formats are supported by a card, so the only way to know this is by simply trying it out. This patch adds that. * v4lmjpegsink didnt have a bufferpool yet - is integrated now. * all copy() bufferpool functions have been removed since they're not needed. * v4lmjpegsink doesnt have a free() function, because hen playing the frames, all this is already handled. When the frame is not played, nothing has to be done. In total, the function is not needed. * adds a get_caps() function to v4l2src * some minor crap --- sys/v4l/TODO | 14 +------ sys/v4l/gstv4lmjpegsink.c | 81 ++++++++++++++++++++++++++++++++------ sys/v4l/gstv4lmjpegsink.h | 3 ++ sys/v4l/gstv4lmjpegsrc.c | 44 +++++++++------------ sys/v4l/gstv4lsrc.c | 54 +++++++++++++------------- sys/v4l/v4lsrc_calls.c | 82 +++++++++++++++++++++++++++++++++++++++ sys/v4l/v4lsrc_calls.h | 3 ++ 7 files changed, 205 insertions(+), 76 deletions(-) diff --git a/sys/v4l/TODO b/sys/v4l/TODO index 7e91b3b73c..4afe5fbe4f 100644 --- a/sys/v4l/TODO +++ b/sys/v4l/TODO @@ -1,24 +1,14 @@ TODO list (short term): ======================= -* as soon as we've trashed Gtk-1.2, change 'gint palette' - to 'guint16 palette' in gstv4lsrc.[ch] -* v4lsrc: actually try the format out on capsnego -* all plugins: on try_set_caps(), loop try_set_caps() per caps - instead of using a multi-caps so we know the end-format -* all three: fix interlacing (not handled at all...) -* add overlay handling in v4lelement -* libgstrec -* avidemux: add events (seek) -* avimux: fps calculations (or make that a set/get_property()?) +* v4lsrc/v4lmjpegsrc/v4l2src: fix interlacing (not handled at all...) TODO list (long term): ====================== * v4lmpegsrc (*hint* MPEG card needed *hint*) -* v4l2element && v4l2src +* v4l2sink * BSD-videosrc (meteorsrc?) * color correction (brightness, hue, etc.) * gamma correction -* dxr3sink Useful Documentation: ===================== diff --git a/sys/v4l/gstv4lmjpegsink.c b/sys/v4l/gstv4lmjpegsink.c index 092cb5a166..93bfd5a837 100644 --- a/sys/v4l/gstv4lmjpegsink.c +++ b/sys/v4l/gstv4lmjpegsink.c @@ -71,6 +71,12 @@ static void gst_v4lmjpegsink_get_property (GObject static GstElementStateReturn gst_v4lmjpegsink_change_state (GstElement *element); static void gst_v4lmjpegsink_set_clock (GstElement *element, GstClock *clock); +/* bufferpool functions */ +static GstBuffer* gst_v4lmjpegsink_buffer_new (GstBufferPool *pool, + guint64 offset, + guint size, + gpointer user_data); + static GstCaps *capslist = NULL; static GstPadTemplate *sink_template; @@ -174,6 +180,14 @@ gst_v4lmjpegsink_init (GstV4lMjpegSink *v4lmjpegsink) v4lmjpegsink->bufsize = 256; GST_FLAG_SET(v4lmjpegsink, GST_ELEMENT_THREAD_SUGGESTED); + + v4lmjpegsink->bufferpool = gst_buffer_pool_new( + NULL, + NULL, + gst_v4lmjpegsink_buffer_new, + NULL, + NULL, + v4lmjpegsink); } @@ -259,20 +273,28 @@ gst_v4lmjpegsink_chain (GstPad *pad, gst_element_clock_wait(GST_ELEMENT(v4lmjpegsink), v4lmjpegsink->clock, GST_BUFFER_TIMESTAMP(buf), NULL); } - /* check size */ - if (GST_BUFFER_SIZE(buf) > v4lmjpegsink->breq.size) + if (GST_BUFFER_POOL(buf) == v4lmjpegsink->bufferpool) { - gst_element_error(GST_ELEMENT(v4lmjpegsink), - "Buffer too big (%d KB), max. buffersize is %d KB", - GST_BUFFER_SIZE(buf)/1024, v4lmjpegsink->breq.size/1024); - return; + num = GPOINTER_TO_INT(GST_BUFFER_POOL_PRIVATE(buf)); + gst_v4lmjpegsink_play_frame(v4lmjpegsink, num); } + else + { + /* check size */ + if (GST_BUFFER_SIZE(buf) > v4lmjpegsink->breq.size) + { + gst_element_error(GST_ELEMENT(v4lmjpegsink), + "Buffer too big (%d KB), max. buffersize is %d KB", + GST_BUFFER_SIZE(buf)/1024, v4lmjpegsink->breq.size/1024); + return; + } - /* put JPEG data to the device */ - gst_v4lmjpegsink_wait_frame(v4lmjpegsink, &num); - memcpy(gst_v4lmjpegsink_get_buffer(v4lmjpegsink, num), - GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf)); - gst_v4lmjpegsink_play_frame(v4lmjpegsink, num); + /* put JPEG data to the device */ + gst_v4lmjpegsink_wait_frame(v4lmjpegsink, &num); + memcpy(gst_v4lmjpegsink_get_buffer(v4lmjpegsink, num), + GST_BUFFER_DATA(buf), GST_BUFFER_SIZE(buf)); + gst_v4lmjpegsink_play_frame(v4lmjpegsink, num); + } g_signal_emit(G_OBJECT(v4lmjpegsink),gst_v4lmjpegsink_signals[SIGNAL_FRAME_DISPLAYED],0); @@ -280,6 +302,43 @@ gst_v4lmjpegsink_chain (GstPad *pad, } +static GstBuffer * +gst_v4lmjpegsink_buffer_new (GstBufferPool *pool, + guint64 offset, + guint size, + gpointer user_data) +{ + GstV4lMjpegSink *v4lmjpegsink = GST_V4LMJPEGSINK(user_data); + GstBuffer *buffer = NULL; + guint8 *data; + gint num; + + if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lmjpegsink))) + return NULL; + if (v4lmjpegsink->breq.size < size) { + GST_DEBUG(GST_CAT_PLUGIN_INFO, "Requested buffer size is too large (%d > %ld)", + size, v4lmjpegsink->breq.size); + return NULL; + } + if (!gst_v4lmjpegsink_wait_frame(v4lmjpegsink, &num)) + return NULL; + data = gst_v4lmjpegsink_get_buffer(v4lmjpegsink, num); + if (!data) + return NULL; + buffer = gst_buffer_new(); + GST_BUFFER_DATA(buffer) = data; + GST_BUFFER_MAXSIZE(buffer) = v4lmjpegsink->breq.size; + GST_BUFFER_SIZE(buffer) = size; + GST_BUFFER_POOL(buffer) = pool; + GST_BUFFER_POOL_PRIVATE(buffer) = GINT_TO_POINTER(num); + + /* with this flag set, we don't need our own buffer_free() function */ + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_DONTFREE); + + return buffer; +} + + static void gst_v4lmjpegsink_set_property (GObject *object, guint prop_id, diff --git a/sys/v4l/gstv4lmjpegsink.h b/sys/v4l/gstv4lmjpegsink.h index a9810499ca..1eee8d9b80 100644 --- a/sys/v4l/gstv4lmjpegsink.h +++ b/sys/v4l/gstv4lmjpegsink.h @@ -68,6 +68,9 @@ struct _GstV4lMjpegSink { pthread_cond_t *cond_queued_frames; gint current_frame; + /* something to get our buffers from */ + GstBufferPool *bufferpool; + /* width/height/norm of the jpeg stream */ gint width; gint height; diff --git a/sys/v4l/gstv4lmjpegsrc.c b/sys/v4l/gstv4lmjpegsrc.c index 0b0e676a4b..5ca95343f7 100644 --- a/sys/v4l/gstv4lmjpegsrc.c +++ b/sys/v4l/gstv4lmjpegsrc.c @@ -86,9 +86,6 @@ static GstBuffer* gst_v4lmjpegsrc_buffer_new (GstBufferPool *pool, guint64 location, guint size, gpointer user_data); -static GstBuffer* gst_v4lmjpegsrc_buffer_copy (GstBufferPool *pool, - const GstBuffer *srcbuf, - gpointer user_data); static void gst_v4lmjpegsrc_buffer_free (GstBufferPool *pool, GstBuffer *buf, gpointer user_data); @@ -195,7 +192,7 @@ gst_v4lmjpegsrc_init (GstV4lMjpegSrc *v4lmjpegsrc) NULL, NULL, gst_v4lmjpegsrc_buffer_new, - gst_v4lmjpegsrc_buffer_copy, + NULL, gst_v4lmjpegsrc_buffer_free, v4lmjpegsrc); @@ -553,28 +550,18 @@ gst_v4lmjpegsrc_buffer_new (GstBufferPool *pool, gpointer user_data) { GstBuffer *buffer; + GstV4lMjpegSrc *v4lmjpegsrc = GST_V4LMJPEGSRC(user_data); + + if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lmjpegsrc))) + return NULL; buffer = gst_buffer_new(); - if (!buffer) return NULL; + if (!buffer) + return NULL; /* TODO: add interlacing info to buffer as metadata */ - - return buffer; -} - - -static GstBuffer* -gst_v4lmjpegsrc_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_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); + GST_BUFFER_MAXSIZE(buffer) = v4lmjpegsrc->breq.size; + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_DONTFREE); return buffer; } @@ -586,15 +573,22 @@ gst_v4lmjpegsrc_buffer_free (GstBufferPool *pool, GstBuffer *buf, gpointer user_ GstV4lMjpegSrc *v4lmjpegsrc = GST_V4LMJPEGSRC (user_data); int n; + if (gst_element_get_state(GST_ELEMENT(v4lmjpegsrc)) != GST_STATE_PLAYING) + return; /* we've already cleaned up ourselves */ + for (n=0;nbreq.count;n++) if (GST_BUFFER_DATA(buf) == gst_v4lmjpegsrc_get_buffer(v4lmjpegsrc, n)) { gst_v4lmjpegsrc_requeue_frame(v4lmjpegsrc, n); - return; + break; } - gst_element_error(GST_ELEMENT(v4lmjpegsrc), - "Couldn't find the buffer"); + if (n == v4lmjpegsrc->breq.count) + gst_element_error(GST_ELEMENT(v4lmjpegsrc), + "Couldn't find the buffer"); + + /* free the buffer struct et all */ + gst_buffer_default_free(buf); } diff --git a/sys/v4l/gstv4lsrc.c b/sys/v4l/gstv4lsrc.c index a51d6875bd..d3d9c0df1d 100644 --- a/sys/v4l/gstv4lsrc.c +++ b/sys/v4l/gstv4lsrc.c @@ -82,9 +82,6 @@ static GstBuffer* gst_v4lsrc_buffer_new (GstBufferPool *pool, guint64 offset, guint size, gpointer user_data); -static GstBuffer* gst_v4lsrc_buffer_copy (GstBufferPool *pool, - const GstBuffer *srcbuf, - gpointer user_data); static void gst_v4lsrc_buffer_free (GstBufferPool *pool, GstBuffer *buf, gpointer user_data); @@ -172,7 +169,7 @@ gst_v4lsrc_init (GstV4lSrc *v4lsrc) NULL, NULL, gst_v4lsrc_buffer_new, - gst_v4lsrc_buffer_copy, + NULL, gst_v4lsrc_buffer_free, v4lsrc); @@ -388,9 +385,8 @@ gst_v4lsrc_srcconnect (GstPad *pad, /* if this caps was useful, try it out */ try_caps: - /* TODO: try the current 'palette' out on the video device */ - - if (!gst_v4lsrc_set_capture(v4lsrc, v4lsrc->width, v4lsrc->height, palette)) + /* try the current 'palette' out on the video device */ + if (!gst_v4lsrc_try_palette(v4lsrc, palette)) continue; /* try to connect the pad/caps with the actual width/height */ @@ -428,6 +424,9 @@ gst_v4lsrc_srcconnect (GstPad *pad, else if (ret_val == GST_PAD_CONNECT_DELAYED) return GST_PAD_CONNECT_DELAYED; + if (!gst_v4lsrc_set_capture(v4lsrc, v4lsrc->width, v4lsrc->height, palette)) + return GST_PAD_CONNECT_REFUSED; + if (!gst_v4lsrc_capture_init(v4lsrc)) return GST_PAD_CONNECT_REFUSED; @@ -616,27 +615,19 @@ gst_v4lsrc_buffer_new (GstBufferPool *pool, gpointer user_data) { GstBuffer *buffer; + GstV4lSrc *v4lsrc = GST_V4LSRC(user_data); + + if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lsrc))) + return NULL; buffer = gst_buffer_new(); - if (!buffer) return NULL; - /* TODO: add interlacing info to buffer as metadata (height>288 or 240 = topfieldfirst, else noninterlaced) */ + if (!buffer) + return NULL; - return buffer; -} - - -static GstBuffer* -gst_v4lsrc_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_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); + /* TODO: add interlacing info to buffer as metadata + * (height>288 or 240 = topfieldfirst, else noninterlaced) */ + GST_BUFFER_MAXSIZE(buffer) = v4lsrc->mbuf.size / v4lsrc->mbuf.frames; + GST_BUFFER_FLAG_SET(buffer, GST_BUFFER_DONTFREE); return buffer; } @@ -648,15 +639,22 @@ gst_v4lsrc_buffer_free (GstBufferPool *pool, GstBuffer *buf, gpointer user_data) GstV4lSrc *v4lsrc = GST_V4LSRC (user_data); int n; + if (gst_element_get_state(GST_ELEMENT(v4lsrc)) != GST_STATE_PLAYING) + return; /* we've already cleaned up ourselves */ + for (n=0;nmbuf.frames;n++) if (GST_BUFFER_DATA(buf) == gst_v4lsrc_get_buffer(v4lsrc, n)) { gst_v4lsrc_requeue_frame(v4lsrc, n); - return; + break; } - gst_element_error(GST_ELEMENT(v4lsrc), - "Couldn\'t find the buffer"); + if (n == v4lsrc->mbuf.frames) + gst_element_error(GST_ELEMENT(v4lsrc), + "Couldn\'t find the buffer"); + + /* free struct */ + gst_buffer_default_free(buf); } diff --git a/sys/v4l/v4lsrc_calls.c b/sys/v4l/v4lsrc_calls.c index 960fc77ce4..40cd6cfcf2 100644 --- a/sys/v4l/v4lsrc_calls.c +++ b/sys/v4l/v4lsrc_calls.c @@ -493,3 +493,85 @@ gst_v4lsrc_capture_deinit (GstV4lSrc *v4lsrc) return TRUE; } + + +/****************************************************** + * gst_v4lsrc_try_palette(): + * try out a palette on the device + * This has to be done before initializing the + * actual capture system, to make sure we don't + * mess up anything. So we need to mini-mmap() + * a buffer here, queue and sync on one buffer, + * and unmap it. + * This is ugly, yes, I know - but it's a major + * design flaw of v4l1 that you don't know in + * advance which formats will be supported... + * This is better than "just assuming that it'll + * work"... + * return value: TRUE on success, FALSE on error + ******************************************************/ + +gboolean +gst_v4lsrc_try_palette (GstV4lSrc *v4lsrc, + gint palette) +{ + /* so, we need a buffer and some more stuff */ + int frame = 0; + guint8 *buffer; + struct video_mbuf vmbuf; + struct video_mmap vmmap; + + DEBUG("gonna try out palette format %d (%s)", + palette, palette_name[palette]); + GST_V4L_CHECK_OPEN(GST_V4LELEMENT(v4lsrc)); + GST_V4L_CHECK_NOT_ACTIVE(GST_V4LELEMENT(v4lsrc)); + + /* let's start by requesting a buffer and mmap()'ing it */ + if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCGMBUF, &vmbuf) < 0) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error getting buffer information: %s", + sys_errlist[errno]); + return FALSE; + } + /* Map the buffers */ + buffer = mmap(0, vmbuf.size, PROT_READ|PROT_WRITE, + MAP_SHARED, GST_V4LELEMENT(v4lsrc)->video_fd, 0); + if (buffer == MAP_FAILED) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error mapping our try-out buffer: %s", + sys_errlist[errno]); + return FALSE; + } + + /* now that we have a buffer, let's try out our format */ + vmmap.width = GST_V4LELEMENT(v4lsrc)->vcap.minwidth; + vmmap.height = GST_V4LELEMENT(v4lsrc)->vcap.minheight; + vmmap.format = palette; + vmmap.frame = frame; + if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCMCAPTURE, &vmmap) < 0) + { + if (errno != EINVAL) /* our format failed! */ + gst_element_error(GST_ELEMENT(v4lsrc), + "Error queueing our try-out buffer: %s", + sys_errlist[errno]); + munmap(buffer, vmbuf.size); + return FALSE; + } + + if (ioctl(GST_V4LELEMENT(v4lsrc)->video_fd, VIDIOCSYNC, &frame) < 0) + { + gst_element_error(GST_ELEMENT(v4lsrc), + "Error syncing on a buffer (%d): %s", + frame, sys_errlist[errno]); + munmap(buffer, vmbuf.size); + return FALSE; + } + + munmap(buffer, vmbuf.size); + + /* if we got here, it worked! woohoo, the format is supported! */ + return TRUE; +} + diff --git a/sys/v4l/v4lsrc_calls.h b/sys/v4l/v4lsrc_calls.h index a85735c6a4..0af1f81fd3 100644 --- a/sys/v4l/v4lsrc_calls.h +++ b/sys/v4l/v4lsrc_calls.h @@ -41,6 +41,9 @@ gboolean gst_v4lsrc_requeue_frame (GstV4lSrc *v4lsrc, gint num); gboolean gst_v4lsrc_capture_stop (GstV4lSrc *v4lsrc); gboolean gst_v4lsrc_capture_deinit (GstV4lSrc *v4lsrc); +/* "the ugliest hack ever, now available at your local mirror" */ +gboolean gst_v4lsrc_try_palette (GstV4lSrc *v4lsrc, gint palette); + #ifdef __cplusplus }