v4l2sink: handle pools

Create a new pool in setcaps and stop/destroy the old one.
Remove buffer_alloc functions.
Check that we have v4l2 metadata in show_frame and fall back to memcpy into a
buffer from our pool if we don't receive one of our own buffers.
This commit is contained in:
Wim Taymans 2011-07-11 13:51:52 +02:00
parent 851f550003
commit e9d80b9f14

View file

@ -622,6 +622,7 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
struct v4l2_fmtdesc *format; struct v4l2_fmtdesc *format;
guint fps_n, fps_d; guint fps_n, fps_d;
guint size; guint size;
GstV4l2BufferPool *newpool;
LOG_CAPS (v4l2sink, caps); LOG_CAPS (v4l2sink, caps);
@ -640,34 +641,52 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
GST_DEBUG_OBJECT (v4l2sink, "no they aren't!"); GST_DEBUG_OBJECT (v4l2sink, "no they aren't!");
} }
if (v4l2sink->pool) {
/* TODO: if we've already allocated buffers, we probably need to
* do something here to free and reallocate....
*
* gst_v4l2_object_stop_streaming()
* gst_v4l2_buffer_pool_destroy()
*
*/
GST_DEBUG_OBJECT (v4l2sink, "warning, changing caps not supported yet");
return FALSE;
}
/* we want our own v4l2 type of fourcc codes */ /* we want our own v4l2 type of fourcc codes */
if (!gst_v4l2_object_get_caps_info (v4l2sink->v4l2object, caps, if (!gst_v4l2_object_get_caps_info (v4l2sink->v4l2object, caps,
&format, &w, &h, &interlaced, &fps_n, &fps_d, &size)) { &format, &w, &h, &interlaced, &fps_n, &fps_d, &size))
GST_DEBUG_OBJECT (v4l2sink, "can't get capture format from caps %p", caps); goto invalid_caps;
return FALSE;
}
if (!format) { if (v4l2sink->pool) {
GST_DEBUG_OBJECT (v4l2sink, "unrecognized caps!!"); /* we have a pool already, stop and destroy the old pool */
return FALSE; if (v4l2sink->state == STATE_STREAMING) {
if (!gst_v4l2_object_stop_streaming (v4l2sink->v4l2object))
goto stop_failed;
v4l2sink->state = STATE_PENDING_STREAMON;
}
gst_v4l2_buffer_pool_destroy (v4l2sink->pool);
v4l2sink->pool = NULL;
} }
if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, format->pixelformat, if (!gst_v4l2_object_set_format (v4l2sink->v4l2object, format->pixelformat,
w, h, interlaced)) { w, h, interlaced))
/* error already posted */ goto invalid_format;
return FALSE;
if (!(v4l2sink->v4l2object->vcap.capabilities & V4L2_CAP_STREAMING))
goto no_streaming;
newpool = gst_v4l2_buffer_pool_new (GST_ELEMENT (v4l2sink),
v4l2sink->v4l2object->video_fd,
v4l2sink->num_buffers, caps, FALSE, V4L2_BUF_TYPE_VIDEO_OUTPUT);
if (newpool == NULL)
goto no_pool;
v4l2sink->pool = newpool;
gst_v4l2sink_sync_overlay_fields (v4l2sink);
gst_v4l2sink_sync_crop_fields (v4l2sink);
#ifdef HAVE_XVIDEO
gst_v4l2_xoverlay_prepare_xwindow_id (v4l2sink->v4l2object, TRUE);
#endif
v4l2sink->state = STATE_PENDING_STREAMON;
GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()");
if (v4l2sink->num_buffers != v4l2sink->pool->buffer_count) {
v4l2sink->num_buffers = v4l2sink->pool->buffer_count;
g_object_notify (G_OBJECT (v4l2sink), "queue-size");
} }
v4l2sink->video_width = w; v4l2sink->video_width = w;
@ -682,70 +701,36 @@ gst_v4l2sink_set_caps (GstBaseSink * bsink, GstCaps * caps)
v4l2sink->current_caps = gst_caps_ref (caps); v4l2sink->current_caps = gst_caps_ref (caps);
return TRUE; return TRUE;
}
#if 0 /* ERRORS */
/* buffer alloc function to implement pad_alloc for upstream element */ invalid_caps:
static GstFlowReturn {
gst_v4l2sink_buffer_alloc (GstBaseSink * bsink, guint64 offset, guint size, GST_DEBUG_OBJECT (v4l2sink,
GstCaps * caps, GstBuffer ** buf) "can't get capture format from caps %" GST_PTR_FORMAT, caps);
{ return FALSE;
GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); }
GstV4l2Buffer *v4l2buf; stop_failed:
{
if (v4l2sink->v4l2object->vcap.capabilities & V4L2_CAP_STREAMING) { GST_DEBUG_OBJECT (v4l2sink, "failed to stop streaming");
return FALSE;
/* initialize the buffer pool if not initialized yet (first buffer): */ }
if (G_UNLIKELY (!v4l2sink->pool)) { invalid_format:
{
/* set_caps() might not be called yet.. so just to make sure: */ /* error already posted */
if (!gst_v4l2sink_set_caps (bsink, caps)) { GST_DEBUG_OBJECT (v4l2sink, "can't set format");
return GST_FLOW_ERROR; return FALSE;
} }
no_streaming:
GST_V4L2_CHECK_OPEN (v4l2sink->v4l2object); {
GST_DEBUG_OBJECT (v4l2sink, "we don't support streaming");
if (!(v4l2sink->pool = gst_v4l2_buffer_pool_new (GST_ELEMENT (v4l2sink), return FALSE;
v4l2sink->v4l2object->video_fd, }
v4l2sink->num_buffers, caps, FALSE, no_pool:
V4L2_BUF_TYPE_VIDEO_OUTPUT))) { {
return GST_FLOW_ERROR; GST_DEBUG_OBJECT (v4l2sink, "can't create new pool");
} return FALSE;
gst_v4l2sink_sync_overlay_fields (v4l2sink);
gst_v4l2sink_sync_crop_fields (v4l2sink);
#ifdef HAVE_XVIDEO
gst_v4l2_xoverlay_prepare_xwindow_id (v4l2sink->v4l2object, TRUE);
#endif
v4l2sink->state = STATE_PENDING_STREAMON;
GST_INFO_OBJECT (v4l2sink, "outputting buffers via mmap()");
if (v4l2sink->num_buffers != v4l2sink->pool->buffer_count) {
v4l2sink->num_buffers = v4l2sink->pool->buffer_count;
g_object_notify (G_OBJECT (v4l2sink), "queue-size");
}
}
v4l2buf = gst_v4l2_buffer_pool_get (v4l2sink->pool, TRUE);
if (G_LIKELY (v4l2buf)) {
GST_DEBUG_OBJECT (v4l2sink, "allocated buffer: %p", v4l2buf);
*buf = v4l2buf;
return GST_FLOW_OK;
} else {
GST_DEBUG_OBJECT (v4l2sink, "failed to allocate buffer");
return GST_FLOW_ERROR;
}
} else {
GST_ERROR_OBJECT (v4l2sink, "only supporting streaming mode for now...");
return GST_FLOW_ERROR;
} }
} }
#endif
/* called after A/V sync to render frame */ /* called after A/V sync to render frame */
static GstFlowReturn static GstFlowReturn
@ -753,62 +738,32 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
{ {
GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink); GstV4l2Sink *v4l2sink = GST_V4L2SINK (bsink);
GstBuffer *newbuf = NULL; GstBuffer *newbuf = NULL;
GstMetaV4l2 *meta;
GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p", buf); GST_DEBUG_OBJECT (v4l2sink, "render buffer: %p", buf);
#if 0 meta = GST_META_V4L2_GET (buf);
if (!GST_IS_V4L2_BUFFER (buf)) {
GstFlowReturn ret;
/* special case check for sub-buffers: In certain cases, places like if (meta == NULL) {
* GstBaseTransform, which might check that the buffer is writable guint8 *data;
* before copying metadata, timestamp, and such, will find that the gsize size;
* buffer has more than one reference to it. In these cases, they
* will create a sub-buffer with an offset=0 and length equal to the
* original buffer size.
*
* This could happen in two scenarios: (1) a tee in the pipeline, and
* (2) because the refcnt is incremented in gst_mini_object_free()
* before the finalize function is called, and decremented after it
* returns.. but returning this buffer to the buffer pool in the
* finalize function, could wake up a thread blocked in _buffer_alloc()
* which could run and get a buffer w/ refcnt==2 before the thread
* originally unref'ing the buffer returns from finalize function and
* decrements the refcnt back to 1!
*/
if (buf->parent &&
(GST_BUFFER_DATA (buf) == GST_BUFFER_DATA (buf->parent)) &&
(GST_BUFFER_SIZE (buf) == GST_BUFFER_SIZE (buf->parent))) {
GST_DEBUG_OBJECT (v4l2sink, "I have a sub-buffer!");
return gst_v4l2sink_show_frame (bsink, buf->parent);
}
GST_DEBUG_OBJECT (v4l2sink, "slow-path.. I got a %s so I need to memcpy", /* not our buffer */
g_type_name (G_OBJECT_TYPE (buf))); GST_DEBUG_OBJECT (v4l2sink, "slow-path.. need to memcpy");
newbuf = gst_v4l2_buffer_pool_get (v4l2sink->pool, TRUE);
ret = gst_v4l2sink_buffer_alloc (bsink, data = gst_buffer_map (buf, &size, NULL, GST_MAP_READ);
GST_BUFFER_OFFSET (buf), GST_BUFFER_SIZE (buf), GST_BUFFER_CAPS (buf), gst_buffer_fill (newbuf, 0, data, size);
&newbuf); gst_buffer_unmap (buf, data, size);
if (GST_FLOW_OK != ret) {
GST_DEBUG_OBJECT (v4l2sink,
"dropping frame! Consider increasing 'queue-size' property!");
return GST_FLOW_OK;
}
memcpy (GST_BUFFER_DATA (newbuf),
GST_BUFFER_DATA (buf),
MIN (GST_BUFFER_SIZE (newbuf), GST_BUFFER_SIZE (buf)));
GST_DEBUG_OBJECT (v4l2sink, "render copied buffer: %p", newbuf); GST_DEBUG_OBJECT (v4l2sink, "render copied buffer: %p", newbuf);
buf = newbuf; buf = newbuf;
} }
#endif
if (!gst_v4l2_buffer_pool_qbuf (v4l2sink->pool, buf)) { if (!gst_v4l2_buffer_pool_qbuf (v4l2sink->pool, buf))
return GST_FLOW_ERROR; goto queue_failed;
}
if (v4l2sink->state == STATE_PENDING_STREAMON) { if (v4l2sink->state == STATE_PENDING_STREAMON) {
if (!gst_v4l2_object_start_streaming (v4l2sink->v4l2object)) { if (!gst_v4l2_object_start_streaming (v4l2sink->v4l2object)) {
return GST_FLOW_ERROR; return GST_FLOW_ERROR;
@ -839,6 +794,13 @@ gst_v4l2sink_show_frame (GstBaseSink * bsink, GstBuffer * buf)
} }
return GST_FLOW_OK; return GST_FLOW_OK;
/* ERRORS */
queue_failed:
{
GST_DEBUG_OBJECT (v4l2sink, "failed to queue buffer");
return GST_FLOW_ERROR;
}
} }
#ifdef HAVE_XVIDEO #ifdef HAVE_XVIDEO