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:
=====================
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;
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);
}
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);
}
+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,
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;
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);
NULL,
NULL,
gst_v4lmjpegsrc_buffer_new,
- gst_v4lmjpegsrc_buffer_copy,
+ NULL,
gst_v4lmjpegsrc_buffer_free,
v4lmjpegsrc);
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;
}
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;n<v4lmjpegsrc->breq.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);
}
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);
NULL,
NULL,
gst_v4lsrc_buffer_new,
- gst_v4lsrc_buffer_copy,
+ NULL,
gst_v4lsrc_buffer_free,
v4lsrc);
/* 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 */
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;
gpointer user_data)
{
GstBuffer *buffer;
+ GstV4lSrc *v4lsrc = GST_V4LSRC(user_data);
- 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_v4lsrc_buffer_copy (GstBufferPool *pool, const GstBuffer *srcbuf, gpointer user_data)
-{
- GstBuffer *buffer;
+ if (!GST_V4L_IS_ACTIVE(GST_V4LELEMENT(v4lsrc)))
+ return NULL;
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);
+ if (!buffer)
+ return NULL;
+
+ /* 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;
}
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;n<v4lsrc->mbuf.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);
}
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;
+}
+
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
}