X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=sys%2Fv4l2%2Fgstv4l2bufferpool.c;h=4d9493726d5aa73955bd3a7088b4c869ec343c4d;hb=1e4c9a1fb85d617292c41b1b12c2a8eca0fc26fe;hp=cf8c29e078d8200b77e4e633eac528c3ff3b7016;hpb=5a6fe4ed29737aa9d9c3c18fb35d0058468c5c1d;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/sys/v4l2/gstv4l2bufferpool.c b/sys/v4l2/gstv4l2bufferpool.c index cf8c29e..4d94937 100644 --- a/sys/v4l2/gstv4l2bufferpool.c +++ b/sys/v4l2/gstv4l2bufferpool.c @@ -45,6 +45,9 @@ #include "gstv4l2object.h" #include "gst/gst-i18n-plugin.h" #include +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT +#include +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ GST_DEBUG_CATEGORY_STATIC (v4l2bufferpool_debug); GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE); @@ -52,7 +55,6 @@ GST_DEBUG_CATEGORY_STATIC (CAT_PERFORMANCE); #define GST_V4L2_IMPORT_QUARK gst_v4l2_buffer_pool_import_quark () - /* * GstV4l2BufferPool: */ @@ -68,6 +70,74 @@ enum _GstV4l2BufferPoolAcquireFlags static void gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer); +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT +typedef struct _GstV4l2TizenBuffer GstV4l2TizenBuffer; +struct _GstV4l2TizenBuffer { + int index; + GstBuffer *gst_buffer; + GstBuffer *v4l2_buffer; + GstV4l2BufferPool *v4l2_pool; +}; + +static void gst_v4l2_tizen_buffer_finalize (GstV4l2TizenBuffer *tizen_buffer) +{ + GstV4l2BufferPool *pool = NULL; + + if (!tizen_buffer) { + GST_ERROR ("NULL buffer"); + return; + } + + pool = tizen_buffer->v4l2_pool; + + gst_v4l2_buffer_pool_release_buffer (GST_BUFFER_POOL_CAST (pool), tizen_buffer->v4l2_buffer); + + g_mutex_lock (&pool->buffer_lock); + + pool->live_buffer_count--; + + GST_DEBUG_OBJECT (pool, "release buffer[%d][tizen:%p,v4l2:%p,gst:%p], live[%d]", + tizen_buffer->index, tizen_buffer, tizen_buffer->v4l2_buffer, + tizen_buffer->gst_buffer, pool->live_buffer_count); + + g_cond_signal (&pool->buffer_cond); + + g_mutex_unlock (&pool->buffer_lock); + + g_free(tizen_buffer); +} + +static GstV4l2TizenBuffer *gst_v4l2_tizen_buffer_new (GstBuffer *v4l2_buffer, int index, GstV4l2BufferPool *v4l2_pool) +{ + GstV4l2TizenBuffer *tizen_buffer = NULL; + GstMemory *memory = NULL; + + tizen_buffer = g_new0 (GstV4l2TizenBuffer, 1); + tizen_buffer->index = index; + tizen_buffer->v4l2_buffer = v4l2_buffer; + tizen_buffer->gst_buffer = gst_buffer_new (); + tizen_buffer->v4l2_pool = v4l2_pool; + + memory = gst_tizen_allocator_alloc_surface (v4l2_pool->tallocator, + &v4l2_pool->obj->info, v4l2_pool->vallocator->groups[index]->surface, (gpointer)tizen_buffer, + (GDestroyNotify)gst_v4l2_tizen_buffer_finalize); + + gst_buffer_append_memory (tizen_buffer->gst_buffer, memory); + gst_buffer_set_size (tizen_buffer->gst_buffer, v4l2_pool->vallocator->s_info.size); + + g_mutex_lock (&v4l2_pool->buffer_lock); + + v4l2_pool->live_buffer_count++; + + GST_DEBUG_OBJECT (v4l2_pool, "new buffer[tizen:%p,v4l2:%p,gst:%p], size[%d], live[%d]", + tizen_buffer, v4l2_buffer, tizen_buffer->gst_buffer, + v4l2_pool->vallocator->s_info.size, v4l2_pool->live_buffer_count); + + g_mutex_unlock (&v4l2_pool->buffer_lock); + + return tizen_buffer; +} +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ static gboolean gst_v4l2_is_buffer_valid (GstBuffer * buffer, GstV4l2MemoryGroup ** out_group) @@ -610,24 +680,67 @@ wrong_config: } } +static GstFlowReturn +gst_v4l2_buffer_pool_resurrect_buffer (GstV4l2BufferPool * pool) +{ + GstBufferPoolAcquireParams params = { 0 }; + GstBuffer *buffer = NULL; + GstFlowReturn ret; + + GST_DEBUG_OBJECT (pool, "A buffer was lost, reallocating it"); + + /* block recursive calls to this function */ + g_signal_handler_block (pool->vallocator, pool->group_released_handler); + + params.flags = + (GstBufferPoolAcquireFlags) GST_V4L2_BUFFER_POOL_ACQUIRE_FLAG_RESURRECT | + GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT; + ret = + gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (pool), &buffer, ¶ms); + + if (ret == GST_FLOW_OK) + gst_buffer_unref (buffer); + + g_signal_handler_unblock (pool->vallocator, pool->group_released_handler); + + return ret; +} + static gboolean gst_v4l2_buffer_pool_streamon (GstV4l2BufferPool * pool) { GstV4l2Object *obj = pool->obj; + if (pool->streaming) + return TRUE; + switch (obj->mode) { case GST_V4L2_IO_MMAP: case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF: case GST_V4L2_IO_DMABUF_IMPORT: - if (!pool->streaming) { - if (obj->ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0) - goto streamon_failed; + if (!V4L2_TYPE_IS_OUTPUT (pool->obj->type)) { + guint num_queued; + guint i, n = 0; + + num_queued = g_atomic_int_get (&pool->num_queued); + if (num_queued < pool->num_allocated) + n = pool->num_allocated - num_queued; + + /* For captures, we need to enqueue buffers before we start streaming, + * so the driver don't underflow immediatly. As we have put then back + * into the base class queue, resurrect them, then releasing will queue + * them back. */ + for (i = 0; i < n; i++) + gst_v4l2_buffer_pool_resurrect_buffer (pool); + } - pool->streaming = TRUE; + if (obj->ioctl (pool->video_fd, VIDIOC_STREAMON, &obj->type) < 0) + goto streamon_failed; - GST_DEBUG_OBJECT (pool, "Started streaming"); - } + pool->streaming = TRUE; + + GST_DEBUG_OBJECT (pool, "Started streaming"); break; default: break; @@ -643,58 +756,81 @@ streamon_failed: } } +/* Call with streamlock held, or when streaming threads are down */ static void gst_v4l2_buffer_pool_streamoff (GstV4l2BufferPool * pool) { + GstBufferPoolClass *pclass = GST_BUFFER_POOL_CLASS (parent_class); GstV4l2Object *obj = pool->obj; + gint i; +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + gint64 end_time = 0; +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ + + if (!pool->streaming) + return; + +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + if (obj->tbm_output && !V4L2_TYPE_IS_OUTPUT(pool->obj->type)) { + g_mutex_lock (&pool->buffer_lock); + GST_INFO_OBJECT (pool, "live buffer[%d]", pool->live_buffer_count); + + if (pool->live_buffer_count > 0) { + end_time = g_get_monotonic_time () + G_TIME_SPAN_SECOND; + + do { + GST_WARNING_OBJECT (pool, "wait for live buffer[%d]", pool->live_buffer_count); + + if (!g_cond_wait_until (&pool->buffer_cond, &pool->buffer_lock, end_time)) { + GST_ERROR_OBJECT (pool, "failed to wait live buffer[%d]", pool->live_buffer_count); + break; + } + + GST_WARNING_OBJECT (pool, "signal received, check again : live count[%d]", + pool->live_buffer_count); + } while (pool->live_buffer_count > 0); + } + + g_mutex_unlock (&pool->buffer_lock); + } +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ switch (obj->mode) { case GST_V4L2_IO_MMAP: case GST_V4L2_IO_USERPTR: case GST_V4L2_IO_DMABUF: case GST_V4L2_IO_DMABUF_IMPORT: - if (pool->streaming) { - if (obj->ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0) - GST_WARNING_OBJECT (pool, "STREAMOFF failed with errno %d (%s)", - errno, g_strerror (errno)); - pool->streaming = FALSE; + if (obj->ioctl (pool->video_fd, VIDIOC_STREAMOFF, &obj->type) < 0) + GST_WARNING_OBJECT (pool, "STREAMOFF failed with errno %d (%s)", + errno, g_strerror (errno)); - GST_DEBUG_OBJECT (pool, "Stopped streaming"); + pool->streaming = FALSE; - if (pool->vallocator) - gst_v4l2_allocator_flush (pool->vallocator); - } + GST_DEBUG_OBJECT (pool, "Stopped streaming"); + + if (pool->vallocator) + gst_v4l2_allocator_flush (pool->vallocator); break; default: break; } -} - -static GstFlowReturn -gst_v4l2_buffer_pool_resurect_buffer (GstV4l2BufferPool * pool) -{ - GstBufferPoolAcquireParams params = { 0 }; - GstBuffer *buffer = NULL; - GstFlowReturn ret; - - GST_DEBUG_OBJECT (pool, "A buffer was lost, reallocating it"); - /* block recursive calls to this function */ - g_signal_handler_block (pool->vallocator, pool->group_released_handler); - - params.flags = - (GstBufferPoolAcquireFlags) GST_V4L2_BUFFER_POOL_ACQUIRE_FLAG_RESURRECT | - GST_BUFFER_POOL_ACQUIRE_FLAG_DONTWAIT; - ret = - gst_buffer_pool_acquire_buffer (GST_BUFFER_POOL (pool), &buffer, ¶ms); + for (i = 0; i < VIDEO_MAX_FRAME; i++) { + if (pool->buffers[i]) { + GstBuffer *buffer = pool->buffers[i]; + GstBufferPool *bpool = GST_BUFFER_POOL (pool); - if (ret == GST_FLOW_OK) - gst_buffer_unref (buffer); + pool->buffers[i] = NULL; - g_signal_handler_unblock (pool->vallocator, pool->group_released_handler); + if (V4L2_TYPE_IS_OUTPUT (pool->obj->type)) + gst_v4l2_buffer_pool_release_buffer (bpool, buffer); + else /* Don't re-enqueue capture buffer on stop */ + pclass->release_buffer (bpool, buffer); - return ret; + g_atomic_int_add (&pool->num_queued, -1); + } + } } static gboolean @@ -707,10 +843,27 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool) GstCaps *caps; guint size, min_buffers, max_buffers; guint max_latency, min_latency, copy_threshold = 0; - gboolean can_allocate = FALSE; + gboolean can_allocate = FALSE, ret = TRUE; GST_DEBUG_OBJECT (pool, "activating pool"); + if (pool->other_pool) { + GstBuffer *buffer; + + if (!gst_buffer_pool_set_active (pool->other_pool, TRUE)) + goto other_pool_failed; + + if (gst_buffer_pool_acquire_buffer (pool->other_pool, &buffer, NULL) != + GST_FLOW_OK) + goto other_pool_failed; + + if (!gst_v4l2_object_try_import (obj, buffer)) { + gst_buffer_unref (buffer); + goto cannot_import; + } + gst_buffer_unref (buffer); + } + config = gst_buffer_pool_get_config (bpool); if (!gst_buffer_pool_config_get_params (config, &caps, &size, &min_buffers, &max_buffers)) @@ -743,6 +896,7 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool) count = gst_v4l2_allocator_start (pool->vallocator, min_buffers, V4L2_MEMORY_MMAP); + pool->num_allocated = count; if (count < GST_V4L2_MIN_BUFFERS) { min_buffers = count; @@ -830,20 +984,21 @@ gst_v4l2_buffer_pool_start (GstBufferPool * bpool) pclass->set_config (bpool, config); gst_structure_free (config); - if (pool->other_pool) - if (!gst_buffer_pool_set_active (pool->other_pool, TRUE)) - goto other_pool_failed; - /* now, allocate the buffers: */ if (!pclass->start (bpool)) goto start_failed; - if (!V4L2_TYPE_IS_OUTPUT (obj->type)) + if (!V4L2_TYPE_IS_OUTPUT (obj->type)) { + if (g_atomic_int_get (&pool->num_queued) < min_buffers) + goto queue_failed; + pool->group_released_handler = g_signal_connect_swapped (pool->vallocator, "group-released", - G_CALLBACK (gst_v4l2_buffer_pool_resurect_buffer), pool); + G_CALLBACK (gst_v4l2_buffer_pool_resurrect_buffer), pool); + ret = gst_v4l2_buffer_pool_streamon (pool); + } - return TRUE; + return ret; /* ERRORS */ wrong_config: @@ -867,19 +1022,46 @@ start_failed: } other_pool_failed: { - GST_ERROR_OBJECT (pool, "failed to active the other pool %" + GST_ERROR_OBJECT (pool, "failed to activate the other pool %" GST_PTR_FORMAT, pool->other_pool); return FALSE; } +queue_failed: + { + GST_ERROR_OBJECT (pool, "failed to queue buffers into the capture queue"); + return FALSE; + } +cannot_import: + { + GST_ERROR_OBJECT (pool, "cannot import buffers from downstream pool"); + return FALSE; + } +} + +static gboolean +gst_v4l2_buffer_pool_vallocator_stop (GstV4l2BufferPool * pool) +{ + GstV4l2Return vret; + + if (!pool->vallocator) + return TRUE; + + vret = gst_v4l2_allocator_stop (pool->vallocator); + + if (vret == GST_V4L2_BUSY) + GST_WARNING_OBJECT (pool, "some buffers are still outstanding"); + + return (vret == GST_V4L2_OK); } static gboolean gst_v4l2_buffer_pool_stop (GstBufferPool * bpool) { GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - GstBufferPoolClass *pclass = GST_BUFFER_POOL_CLASS (parent_class); gboolean ret; - gint i; + + if (pool->orphaned) + return gst_v4l2_buffer_pool_vallocator_stop (pool); GST_DEBUG_OBJECT (pool, "stopping pool"); @@ -897,34 +1079,46 @@ gst_v4l2_buffer_pool_stop (GstBufferPool * bpool) gst_v4l2_buffer_pool_streamoff (pool); - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - if (pool->buffers[i]) { - GstBuffer *buffer = pool->buffers[i]; + ret = GST_BUFFER_POOL_CLASS (parent_class)->stop (bpool); - pool->buffers[i] = NULL; + if (ret) + ret = gst_v4l2_buffer_pool_vallocator_stop (pool); - if (V4L2_TYPE_IS_OUTPUT (pool->obj->type)) - gst_v4l2_buffer_pool_release_buffer (bpool, buffer); - else /* Don't re-enqueue capture buffer on stop */ - pclass->release_buffer (bpool, buffer); + return ret; +} - g_atomic_int_add (&pool->num_queued, -1); - } - } +gboolean +gst_v4l2_buffer_pool_orphan (GstBufferPool ** bpool) +{ + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (*bpool); + gboolean ret; - ret = GST_BUFFER_POOL_CLASS (parent_class)->stop (bpool); + if (!GST_V4L2_ALLOCATOR_CAN_ORPHAN_BUFS (pool->vallocator)) + return FALSE; - if (ret && pool->vallocator) { - GstV4l2Return vret; + if (g_getenv ("GST_V4L2_FORCE_DRAIN")) + return FALSE; + + GST_DEBUG_OBJECT (pool, "orphaning pool"); - vret = gst_v4l2_allocator_stop (pool->vallocator); + gst_buffer_pool_set_active (*bpool, FALSE); + /* + * If the buffer pool has outstanding buffers, it will not be stopped + * by the base class when set inactive. Stop it manually and mark it + * as orphaned + */ + ret = gst_v4l2_buffer_pool_stop (*bpool); + if (!ret) + ret = gst_v4l2_allocator_orphan (pool->vallocator); - if (vret == GST_V4L2_BUSY) - GST_WARNING_OBJECT (pool, "some buffers are still outstanding"); + if (!ret) + goto orphan_failed; - ret = (vret == GST_V4L2_OK); - } + pool->orphaned = TRUE; + gst_object_unref (*bpool); + *bpool = NULL; +orphan_failed: return ret; } @@ -950,89 +1144,53 @@ static void gst_v4l2_buffer_pool_flush_stop (GstBufferPool * bpool) { GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); - GstV4l2Object *obj = pool->obj; - GstBuffer *buffers[VIDEO_MAX_FRAME]; - gint i; GST_DEBUG_OBJECT (pool, "stop flushing"); - /* If we haven't started streaming yet, simply call streamon */ - if (!pool->streaming) - goto streamon; - if (pool->other_pool) gst_buffer_pool_set_flushing (pool->other_pool, FALSE); - GST_OBJECT_LOCK (pool); - gst_v4l2_buffer_pool_streamoff (pool); - /* Remember buffers to re-enqueue */ - memcpy (buffers, pool->buffers, sizeof (buffers)); - memset (pool->buffers, 0, sizeof (pool->buffers)); - GST_OBJECT_UNLOCK (pool); - - /* Reset our state */ - switch (obj->mode) { - case GST_V4L2_IO_RW: - break; - case GST_V4L2_IO_MMAP: - case GST_V4L2_IO_USERPTR: - case GST_V4L2_IO_DMABUF: - case GST_V4L2_IO_DMABUF_IMPORT: - { - for (i = 0; i < VIDEO_MAX_FRAME; i++) { - /* Re-enqueue buffers */ - if (buffers[i]) { - GstBufferPool *bpool = (GstBufferPool *) pool; - GstBuffer *buffer = buffers[i]; - - /* Remove qdata, this will unmap any map data in - * userptr/dmabuf-import */ - gst_mini_object_set_qdata (GST_MINI_OBJECT (buffer), - GST_V4L2_IMPORT_QUARK, NULL, NULL); - - if (buffer->pool == NULL) - gst_v4l2_buffer_pool_release_buffer (bpool, buffer); - - g_atomic_int_add (&pool->num_queued, -1); - } - } - - break; - } - default: - g_assert_not_reached (); - break; - } - -streamon: - /* Start streaming on capture device only */ - if (!V4L2_TYPE_IS_OUTPUT (obj->type)) - gst_v4l2_buffer_pool_streamon (pool); - gst_poll_set_flushing (pool->poll, FALSE); } static GstFlowReturn -gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool) +gst_v4l2_buffer_pool_poll (GstV4l2BufferPool * pool, gboolean wait) { gint ret; + GstClockTime timeout; + + if (wait) + timeout = GST_CLOCK_TIME_NONE; + else + timeout = 0; /* In RW mode there is no queue, hence no need to wait while the queue is * empty */ if (pool->obj->mode != GST_V4L2_IO_RW) { GST_OBJECT_LOCK (pool); + + if (!wait && pool->empty) { + GST_OBJECT_UNLOCK (pool); + goto no_buffers; + } + while (pool->empty) g_cond_wait (&pool->empty_cond, GST_OBJECT_GET_LOCK (pool)); + GST_OBJECT_UNLOCK (pool); } - if (!pool->can_poll_device) - goto done; + if (!pool->can_poll_device) { + if (wait) + goto done; + else + goto no_buffers; + } GST_LOG_OBJECT (pool, "polling device"); again: - ret = gst_poll_wait (pool->poll, GST_CLOCK_TIME_NONE); + ret = gst_poll_wait (pool->poll, timeout); if (G_UNLIKELY (ret < 0)) { switch (errno) { case EBUSY: @@ -1054,6 +1212,9 @@ again: if (gst_poll_fd_has_error (pool->poll, &pool->pollfd)) goto select_error; + if (ret == 0) + goto no_buffers; + done: return GST_FLOW_OK; @@ -1069,22 +1230,18 @@ select_error: ("poll error %d: %s (%d)", ret, g_strerror (errno), errno)); return GST_FLOW_ERROR; } +no_buffers: + return GST_FLOW_CUSTOM_SUCCESS; } static GstFlowReturn -gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf) +gst_v4l2_buffer_pool_qbuf (GstV4l2BufferPool * pool, GstBuffer * buf, + GstV4l2MemoryGroup * group) { - GstV4l2MemoryGroup *group = NULL; const GstV4l2Object *obj = pool->obj; GstClockTime timestamp; gint index; - if (!gst_v4l2_is_buffer_valid (buf, &group)) { - GST_LOG_OBJECT (pool, "unref copied/invalid buffer %p", buf); - gst_buffer_unref (buf); - return GST_FLOW_OK; - } - index = group->buffer.index; if (pool->buffers[index] != NULL) @@ -1150,18 +1307,29 @@ queue_failed: } static GstFlowReturn -gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) +gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer, + gboolean wait) { GstFlowReturn res; - GstBuffer *outbuf; + GstBuffer *outbuf = NULL; GstV4l2Object *obj = pool->obj; GstClockTime timestamp; GstV4l2MemoryGroup *group; + GstVideoMeta *vmeta; + gsize size; gint i; +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + GstV4l2TizenBuffer *tizen_buffer = NULL; +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ - if ((res = gst_v4l2_buffer_pool_poll (pool)) != GST_FLOW_OK) + if ((res = gst_v4l2_buffer_pool_poll (pool, wait)) < GST_FLOW_OK) goto poll_failed; + if (res == GST_FLOW_CUSTOM_SUCCESS) { + GST_LOG_OBJECT (pool, "nothing to dequeue"); + goto done; + } + GST_LOG_OBJECT (pool, "dequeueing a buffer"); res = gst_v4l2_allocator_dqbuf (pool->vallocator, &group); @@ -1187,7 +1355,8 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) timestamp = GST_TIMEVAL_TO_TIME (group->buffer.timestamp); -#ifndef GST_DISABLE_GST_DEBUG + size = 0; + vmeta = gst_buffer_get_video_meta (outbuf); for (i = 0; i < group->n_mem; i++) { GST_LOG_OBJECT (pool, "dequeued buffer %p seq:%d (ix=%d), mem %p used %d, plane=%d, flags %08x, ts %" @@ -1195,8 +1364,12 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) group->buffer.sequence, group->buffer.index, group->mem[i], group->planes[i].bytesused, i, group->buffer.flags, GST_TIME_ARGS (timestamp), pool->num_queued, outbuf); + + if (vmeta) { + vmeta->offset[i] = size; + size += gst_memory_get_sizes (group->mem[i], NULL, NULL); + } } -#endif /* Ignore timestamp and field for OUTPUT device */ if (V4L2_TYPE_IS_OUTPUT (obj->type)) @@ -1264,7 +1437,10 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) } if (GST_VIDEO_INFO_FORMAT (&obj->info) == GST_VIDEO_FORMAT_ENCODED) { - if (group->buffer.flags & V4L2_BUF_FLAG_KEYFRAME) + if ((group->buffer.flags & V4L2_BUF_FLAG_KEYFRAME) || + GST_V4L2_PIXELFORMAT (obj) == V4L2_PIX_FMT_MJPEG || + GST_V4L2_PIXELFORMAT (obj) == V4L2_PIX_FMT_JPEG || + GST_V4L2_PIXELFORMAT (obj) == V4L2_PIX_FMT_PJPG) GST_BUFFER_FLAG_UNSET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); else GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DELTA_UNIT); @@ -1277,10 +1453,20 @@ gst_v4l2_buffer_pool_dqbuf (GstV4l2BufferPool * pool, GstBuffer ** buffer) GST_BUFFER_OFFSET (outbuf) = group->buffer.sequence; GST_BUFFER_OFFSET_END (outbuf) = group->buffer.sequence + 1; +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + if (group->surface) { + tizen_buffer = gst_v4l2_tizen_buffer_new (outbuf, group->buffer.index, pool); + if (!tizen_buffer) { + GST_ERROR_OBJECT (pool, "tizen buffer failed for index[%d]", group->buffer.index); + goto no_buffer; + } + outbuf = tizen_buffer->gst_buffer; + } +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ done: *buffer = outbuf; - return GST_FLOW_OK; + return res; /* ERRORS */ poll_failed: @@ -1315,7 +1501,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, GST_DEBUG_OBJECT (pool, "acquire"); - /* If this is being called to resurect a lost buffer */ + /* If this is being called to resurrect a lost buffer */ if (params && params->flags & GST_V4L2_BUFFER_POOL_ACQUIRE_FLAG_RESURRECT) { ret = pclass->acquire_buffer (bpool, buffer, params); goto done; @@ -1340,7 +1526,7 @@ gst_v4l2_buffer_pool_acquire_buffer (GstBufferPool * bpool, GstBuffer ** buffer, /* just dequeue a buffer, we basically use the queue of v4l2 as the * storage for our buffers. This function does poll first so we can * interrupt it fine. */ - ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer); + ret = gst_v4l2_buffer_pool_dqbuf (pool, buffer, TRUE); break; } default: @@ -1393,6 +1579,14 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) GST_DEBUG_OBJECT (pool, "release buffer %p", buffer); + /* If the buffer's pool has been orphaned, dispose of it so that + * the pool resources can be freed */ + if (pool->orphaned) { + GST_BUFFER_FLAG_SET (buffer, GST_BUFFER_FLAG_TAG_MEMORY); + pclass->release_buffer (bpool, buffer); + return; + } + switch (obj->type) { case V4L2_BUF_TYPE_VIDEO_CAPTURE: case V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE: @@ -1411,11 +1605,14 @@ gst_v4l2_buffer_pool_release_buffer (GstBufferPool * bpool, GstBuffer * buffer) { GstV4l2MemoryGroup *group; if (gst_v4l2_is_buffer_valid (buffer, &group)) { + GstFlowReturn ret = GST_FLOW_OK; + gst_v4l2_allocator_reset_group (pool->vallocator, group); /* queue back in the device */ if (pool->other_pool) - gst_v4l2_buffer_pool_prepare_buffer (pool, buffer, NULL); - if (gst_v4l2_buffer_pool_qbuf (pool, buffer) != GST_FLOW_OK) + ret = gst_v4l2_buffer_pool_prepare_buffer (pool, buffer, NULL); + if (ret != GST_FLOW_OK || + gst_v4l2_buffer_pool_qbuf (pool, buffer, group) != GST_FLOW_OK) pclass->release_buffer (bpool, buffer); } else { /* Simply release invalide/modified buffer, the allocator will @@ -1504,6 +1701,14 @@ gst_v4l2_buffer_pool_dispose (GObject * object) gst_object_unref (pool->allocator); pool->allocator = NULL; +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + g_cond_clear (&pool->buffer_cond); + g_mutex_clear (&pool->buffer_lock); + + if (pool->tallocator) + gst_object_unref (pool->tallocator); + pool->tallocator = NULL; +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ if (pool->other_pool) gst_object_unref (pool->other_pool); pool->other_pool = NULL; @@ -1540,6 +1745,7 @@ gst_v4l2_buffer_pool_init (GstV4l2BufferPool * pool) pool->can_poll_device = TRUE; g_cond_init (&pool->empty_cond); pool->empty = TRUE; + pool->orphaned = FALSE; } static void @@ -1608,6 +1814,14 @@ gst_v4l2_buffer_pool_new (GstV4l2Object * obj, GstCaps * caps) pool->obj = obj; pool->can_poll_device = TRUE; +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + pool->tallocator = gst_tizen_allocator_new (); + if (pool->tallocator == NULL) + goto allocator_failed; + + g_mutex_init (&pool->buffer_lock); + g_cond_init (&pool->buffer_cond); +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ pool->vallocator = gst_v4l2_allocator_new (GST_OBJECT (pool), obj); if (pool->vallocator == NULL) goto allocator_failed; @@ -1630,6 +1844,12 @@ dup_failed: } allocator_failed: { +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + if (pool->tallocator) { + gst_object_unref (pool->tallocator); + pool->tallocator = NULL; + } +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ GST_ERROR_OBJECT (pool, "Failed to create V4L2 allocator"); gst_object_unref (pool); return NULL; @@ -1652,7 +1872,7 @@ gst_v4l2_do_read (GstV4l2BufferPool * pool, GstBuffer * buf) gst_buffer_map (buf, &map, GST_MAP_WRITE); do { - if ((res = gst_v4l2_buffer_pool_poll (pool)) != GST_FLOW_OK) + if ((res = gst_v4l2_buffer_pool_poll (pool, TRUE)) != GST_FLOW_OK) goto poll_error; amount = obj->read (obj->video_fd, map.data, toread); @@ -1740,20 +1960,13 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) guint num_queued; gsize size = gst_buffer_get_size (*buf); - if (size == 0) { - if (GST_BUFFER_FLAG_IS_SET (*buf, GST_BUFFER_FLAG_CORRUPTED)) - goto buffer_corrupted; - else - goto eos; - } + /* Legacy M2M devices return empty buffer when drained */ + if (size == 0 && GST_V4L2_IS_M2M (obj->device_caps)) + goto eos; - /* verify that buffer contains a full frame for raw video */ - if (GST_VIDEO_INFO_FORMAT (&obj->info) != GST_VIDEO_FORMAT_ENCODED - && size < GST_VIDEO_INFO_SIZE (&obj->info)) { - GST_WARNING_OBJECT (pool, "Invalid buffer size, this is likely " - "due to a bug in your driver, dropping"); - goto buffer_corrupted; - } + if (GST_VIDEO_INFO_FORMAT (&pool->caps_info) != + GST_VIDEO_FORMAT_ENCODED && size < pool->size) + goto buffer_truncated; num_queued = g_atomic_int_get (&pool->num_queued); GST_TRACE_OBJECT (pool, "Only %i buffer left in the capture queue.", @@ -1762,7 +1975,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) /* If we have no more buffer, and can allocate it time to do so */ if (num_queued == 0) { if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) { - ret = gst_v4l2_buffer_pool_resurect_buffer (pool); + ret = gst_v4l2_buffer_pool_resurrect_buffer (pool); if (ret == GST_FLOW_OK) goto done; } @@ -1773,7 +1986,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) GstBuffer *copy; if (GST_V4L2_ALLOCATOR_CAN_ALLOCATE (pool->vallocator, MMAP)) { - ret = gst_v4l2_buffer_pool_resurect_buffer (pool); + ret = gst_v4l2_buffer_pool_resurrect_buffer (pool); if (ret == GST_FLOW_OK) goto done; } @@ -1794,26 +2007,31 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) } /* buffer not from our pool, grab a frame and copy it into the target */ - if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp)) != GST_FLOW_OK) + if ((ret = gst_v4l2_buffer_pool_dqbuf (pool, &tmp, TRUE)) + != GST_FLOW_OK) goto done; /* An empty buffer on capture indicates the end of stream */ if (gst_buffer_get_size (tmp) == 0) { - gboolean corrupted = GST_BUFFER_FLAG_IS_SET (tmp, - GST_BUFFER_FLAG_CORRUPTED); - gst_v4l2_buffer_pool_release_buffer (bpool, tmp); - if (corrupted) - goto buffer_corrupted; - else + /* Legacy M2M devices return empty buffer when drained */ + if (GST_V4L2_IS_M2M (obj->device_caps)) goto eos; } - +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + if (pool->obj->tbm_output && pool->obj->mode == GST_V4L2_IO_DMABUF) { + gst_buffer_unref (*buf); + *buf = tmp; + } else { +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ ret = gst_v4l2_buffer_pool_copy_buffer (pool, *buf, tmp); /* an queue the buffer again after the copy */ gst_v4l2_buffer_pool_release_buffer (bpool, tmp); +#ifdef TIZEN_FEATURE_V4L2_TBM_SUPPORT + } +#endif /* TIZEN_FEATURE_V4L2_TBM_SUPPORT */ if (ret != GST_FLOW_OK) goto copy_failed; @@ -1823,12 +2041,20 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) case GST_V4L2_IO_USERPTR: { struct UserPtrData *data; + GstBuffer *tmp; /* Replace our buffer with downstream allocated buffer */ data = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); - gst_buffer_replace (buf, data->buffer); + tmp = gst_buffer_ref (data->buffer); _unmap_userptr_frame (data); + + /* Now tmp is writable, copy the flags and timestamp */ + gst_buffer_copy_into (tmp, *buf, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1); + + gst_buffer_replace (buf, tmp); + gst_buffer_unref (tmp); break; } @@ -1839,6 +2065,10 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) /* Replace our buffer with downstream allocated buffer */ tmp = gst_mini_object_steal_qdata (GST_MINI_OBJECT (*buf), GST_V4L2_IMPORT_QUARK); + + gst_buffer_copy_into (tmp, *buf, + GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS, 0, -1); + gst_buffer_replace (buf, tmp); gst_buffer_unref (tmp); break; @@ -1865,6 +2095,7 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) case GST_V4L2_IO_MMAP: { GstBuffer *to_queue = NULL; + GstBuffer *buffer; GstV4l2MemoryGroup *group; gint index; @@ -1905,9 +2136,13 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) gst_buffer_unref (to_queue); goto prepare_failed; } + + /* retreive the group */ + gst_v4l2_is_buffer_valid (to_queue, &group); } - if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue)) != GST_FLOW_OK) + if ((ret = gst_v4l2_buffer_pool_qbuf (pool, to_queue, group)) + != GST_FLOW_OK) goto queue_failed; /* if we are not streaming yet (this is the first buffer, start @@ -1935,15 +2170,21 @@ gst_v4l2_buffer_pool_process (GstV4l2BufferPool * pool, GstBuffer ** buf) * otherwise the pool will think it is outstanding and will refuse to stop. */ gst_buffer_unref (to_queue); + /* release as many buffer as possible */ + while (gst_v4l2_buffer_pool_dqbuf (pool, &buffer, FALSE) == + GST_FLOW_OK) { + if (buffer->pool == NULL) + gst_v4l2_buffer_pool_release_buffer (bpool, buffer); + } + if (g_atomic_int_get (&pool->num_queued) >= pool->min_latency) { - GstBuffer *out; /* all buffers are queued, try to dequeue one and release it back * into the pool so that _acquire can get to it again. */ - ret = gst_v4l2_buffer_pool_dqbuf (pool, &out); - if (ret == GST_FLOW_OK && out->pool == NULL) + ret = gst_v4l2_buffer_pool_dqbuf (pool, &buffer, TRUE); + if (ret == GST_FLOW_OK && buffer->pool == NULL) /* release the rendered buffer back into the pool. This wakes up any * thread waiting for a buffer in _acquire(). */ - gst_v4l2_buffer_pool_release_buffer (bpool, out); + gst_v4l2_buffer_pool_release_buffer (bpool, buffer); } break; } @@ -1965,9 +2206,10 @@ copy_failed: GST_ERROR_OBJECT (pool, "failed to copy buffer"); return ret; } -buffer_corrupted: +buffer_truncated: { - GST_WARNING_OBJECT (pool, "Dropping corrupted buffer without payload"); + GST_WARNING_OBJECT (pool, + "Dropping truncated buffer, this is likely a driver bug."); gst_buffer_unref (*buf); *buf = NULL; return GST_V4L2_FLOW_CORRUPTED_BUFFER; @@ -2023,3 +2265,17 @@ gst_v4l2_buffer_pool_copy_at_threshold (GstV4l2BufferPool * pool, gboolean copy) pool->enable_copy_threshold = copy; GST_OBJECT_UNLOCK (pool); } + +gboolean +gst_v4l2_buffer_pool_flush (GstBufferPool * bpool) +{ + GstV4l2BufferPool *pool = GST_V4L2_BUFFER_POOL (bpool); + gboolean ret = TRUE; + + gst_v4l2_buffer_pool_streamoff (pool); + + if (!V4L2_TYPE_IS_OUTPUT (pool->obj->type)) + ret = gst_v4l2_buffer_pool_streamon (pool); + + return ret; +}