X-Git-Url: http://review.tizen.org/git/?a=blobdiff_plain;f=sys%2Fv4l2%2Fgstv4l2allocator.c;h=5f6f554d97b0a9aced6531000ca87b86a134aa31;hb=a9ea40efb522a2f013d853e08dbaf703610c8027;hp=ebc1f3154280acd7e39fa49337e400c1c7c9432f;hpb=dfdd1ba9d9ff94de887efe8309b64847dc562a38;p=platform%2Fupstream%2Fgst-plugins-good.git diff --git a/sys/v4l2/gstv4l2allocator.c b/sys/v4l2/gstv4l2allocator.c index ebc1f31..5f6f554 100644 --- a/sys/v4l2/gstv4l2allocator.c +++ b/sys/v4l2/gstv4l2allocator.c @@ -21,9 +21,14 @@ #include "config.h" +#ifndef _GNU_SOURCE +# define _GNU_SOURCE /* O_CLOEXEC */ +#endif + #include "ext/videodev2.h" + +#include "gstv4l2object.h" #include "gstv4l2allocator.h" -#include "v4l2_calls.h" #include @@ -32,6 +37,11 @@ #include #include #include +#include +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER +#include +#include +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ #define GST_V4L2_MEMORY_TYPE "V4l2Memory" @@ -141,14 +151,6 @@ _v4l2mem_dispose (GstV4l2Memory * mem) return ret; } -static void -_v4l2mem_free (GstV4l2Memory * mem) -{ - if (mem->dmafd >= 0) - close (mem->dmafd); - g_slice_free (GstV4l2Memory, mem); -} - static inline GstV4l2Memory * _v4l2mem_new (GstMemoryFlags flags, GstAllocator * allocator, GstMemory * parent, gsize maxsize, gsize align, gsize offset, gsize size, @@ -204,19 +206,23 @@ _v4l2mem_is_span (GstV4l2Memory * mem1, GstV4l2Memory * mem2, gsize * offset) return mem1->mem.offset + mem1->mem.size == mem2->mem.offset; } -static void -_v4l2mem_parent_to_dmabuf (GstV4l2Memory * mem, GstMemory * dma_mem) -{ - gst_memory_lock (&mem->mem, GST_LOCK_FLAG_EXCLUSIVE); - dma_mem->parent = gst_memory_ref (&mem->mem); -} - gboolean gst_is_v4l2_memory (GstMemory * mem) { return gst_memory_is_type (mem, GST_V4L2_MEMORY_TYPE); } +GQuark +gst_v4l2_memory_quark (void) +{ + static GQuark quark = 0; + + if (quark == 0) + quark = g_quark_from_string ("GstV4l2Memory"); + + return quark; +} + /*************************************/ /* GstV4l2MemoryGroup implementation */ @@ -233,6 +239,13 @@ gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group) if (mem) gst_memory_unref (mem); } +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + if (group->surface) { + GST_INFO ("unref surface[%p]", group->surface); + tbm_surface_destroy (group->surface); + group->surface = NULL; + } +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ g_slice_free (GstV4l2MemoryGroup, group); } @@ -240,9 +253,9 @@ gst_v4l2_memory_group_free (GstV4l2MemoryGroup * group) static GstV4l2MemoryGroup * gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index) { - gint video_fd = allocator->video_fd; + GstV4l2Object *obj = allocator->obj; guint32 memory = allocator->memory; - struct v4l2_format *format = &allocator->format; + struct v4l2_format *format = &obj->format; GstV4l2MemoryGroup *group; gsize img_size, buf_size; @@ -259,22 +272,30 @@ gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index) group->n_mem = 1; } - if (v4l2_ioctl (video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0) + if (obj->ioctl (obj->video_fd, VIDIOC_QUERYBUF, &group->buffer) < 0) goto querybuf_failed; + if (group->buffer.index != index) { + GST_ERROR_OBJECT (allocator, "Buffer index returned by VIDIOC_QUERYBUF " + "didn't match, this indicate the presence of a bug in your driver or " + "libv4l2"); + g_slice_free (GstV4l2MemoryGroup, group); + return NULL; + } + /* Check that provided size matches the format we have negotiation. Failing * there usually means a driver of libv4l bug. */ - if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { gint i; for (i = 0; i < group->n_mem; i++) { - img_size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage; + img_size = obj->format.fmt.pix_mp.plane_fmt[i].sizeimage; buf_size = group->planes[i].length; if (buf_size < img_size) goto buffer_too_short; } } else { - img_size = allocator->format.fmt.pix.sizeimage; + img_size = obj->format.fmt.pix.sizeimage; buf_size = group->buffer.length; if (buf_size < img_size) goto buffer_too_short; @@ -285,6 +306,7 @@ gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index) if (!V4L2_TYPE_IS_MULTIPLANAR (format->type)) { group->planes[0].bytesused = group->buffer.bytesused; group->planes[0].length = group->buffer.length; + group->planes[0].data_offset = 0; g_assert (sizeof (group->planes[0].m) == sizeof (group->buffer.m)); memcpy (&group->planes[0].m, &group->buffer.m, sizeof (group->buffer.m)); } @@ -301,8 +323,10 @@ gst_v4l2_memory_group_new (GstV4l2Allocator * allocator, guint32 index) if (memory == V4L2_MEMORY_MMAP) { gint i; for (i = 0; i < group->n_mem; i++) { - GST_LOG_OBJECT (allocator, " [%u] bytesused: %u, length: %u", i, - group->planes[i].bytesused, group->planes[i].length); + GST_LOG_OBJECT (allocator, + " [%u] bytesused: %u, length: %u, offset: %u", i, + group->planes[i].bytesused, group->planes[i].length, + group->planes[i].data_offset); GST_LOG_OBJECT (allocator, " [%u] MMAP offset: %u", i, group->planes[i].m.mem_offset); } @@ -369,26 +393,26 @@ static void gst_v4l2_allocator_free (GstAllocator * gallocator, GstMemory * gmem) { GstV4l2Allocator *allocator = (GstV4l2Allocator *) gallocator; + GstV4l2Object *obj = allocator->obj; GstV4l2Memory *mem = (GstV4l2Memory *) gmem; GstV4l2MemoryGroup *group = mem->group; - GST_LOG_OBJECT (allocator, "freeing plane %i of buffer %u", - mem->plane, group->buffer.index); + /* Only free unparented memory */ + if (mem->mem.parent == NULL) { + GST_LOG_OBJECT (allocator, "freeing plane %i of buffer %u", + mem->plane, group->buffer.index); - switch (allocator->memory) { - case V4L2_MEMORY_MMAP: - if (mem->data) { - v4l2_munmap (mem->data, group->planes[mem->plane].length); - } else if (group->planes[mem->plane].m.fd > 0) { - close (group->planes[mem->plane].m.fd); - } - break; - default: - /* Nothing to do */ - break; + if (allocator->memory == V4L2_MEMORY_MMAP) { + if (mem->data) + obj->munmap (mem->data, group->planes[mem->plane].length); + } + + /* This apply for both mmap with expbuf, and dmabuf imported memory */ + if (mem->dmafd >= 0) + close (mem->dmafd); } - _v4l2mem_free (mem); + g_slice_free (GstV4l2Memory, mem); } static void @@ -415,9 +439,16 @@ gst_v4l2_allocator_finalize (GObject * obj) GstV4l2Allocator *allocator = (GstV4l2Allocator *) obj; GST_LOG_OBJECT (obj, "called"); +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + if (allocator->bufmgr) { + GST_INFO_OBJECT (obj, "deinit tbm bufmgr %p", allocator->bufmgr); + tbm_bufmgr_deinit (allocator->bufmgr); + allocator->bufmgr = NULL; + } +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ - v4l2_close (allocator->video_fd); gst_atomic_queue_unref (allocator->free_queue); + gst_object_unref (allocator->obj->element); G_OBJECT_CLASS (parent_class)->finalize (obj); } @@ -470,49 +501,60 @@ static guint32 gst_v4l2_allocator_probe (GstV4l2Allocator * allocator, guint32 memory, guint32 breq_flag, guint32 bcreate_flag) { + GstV4l2Object *obj = allocator->obj; struct v4l2_requestbuffers breq = { 0 }; guint32 flags = 0; - breq.type = allocator->type; + breq.type = obj->type; breq.count = 0; breq.memory = memory; - if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) == 0) { + if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) == 0) { struct v4l2_create_buffers bcreate = { 0 }; flags |= breq_flag; - bcreate.memory = V4L2_MEMORY_MMAP; - bcreate.format = allocator->format; + bcreate.memory = memory; + bcreate.format = obj->format; - if ((v4l2_ioctl (allocator->video_fd, VIDIOC_CREATE_BUFS, &bcreate) == 0)) + if ((obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) == 0)) flags |= bcreate_flag; } + if (breq.capabilities & V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS) + flags |= GST_V4L2_ALLOCATOR_FLAG_SUPPORTS_ORPHANED_BUFS; + return flags; } static GstV4l2MemoryGroup * gst_v4l2_allocator_create_buf (GstV4l2Allocator * allocator) { + GstV4l2Object *obj = allocator->obj; struct v4l2_create_buffers bcreate = { 0 }; GstV4l2MemoryGroup *group = NULL; GST_OBJECT_LOCK (allocator); - if (!allocator->active) + if (!g_atomic_int_get (&allocator->active)) goto done; + if (GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator)) + goto orphaned_bug; + bcreate.memory = allocator->memory; - bcreate.format = allocator->format; + bcreate.format = obj->format; bcreate.count = 1; if (!allocator->can_allocate) goto done; - if (v4l2_ioctl (allocator->video_fd, VIDIOC_CREATE_BUFS, &bcreate) < 0) + if (obj->ioctl (obj->video_fd, VIDIOC_CREATE_BUFS, &bcreate) < 0) goto create_bufs_failed; + if (allocator->groups[bcreate.index] != NULL) + goto create_bufs_bug; + group = gst_v4l2_memory_group_new (allocator, bcreate.index); if (group) { @@ -524,12 +566,25 @@ done: GST_OBJECT_UNLOCK (allocator); return group; +orphaned_bug: + { + GST_ERROR_OBJECT (allocator, "allocator was orphaned, " + "not creating new buffers"); + goto done; + } create_bufs_failed: { GST_WARNING_OBJECT (allocator, "error creating a new buffer: %s", g_strerror (errno)); goto done; } +create_bufs_bug: + { + GST_ERROR_OBJECT (allocator, "created buffer has already used buffer " + "index %i, this means there is an bug in your driver or libv4l2", + bcreate.index); + goto done; + } } static GstV4l2MemoryGroup * @@ -559,35 +614,11 @@ static void gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { - gsize size; - gboolean imported = FALSE; - - switch (allocator->memory) { - case V4L2_MEMORY_USERPTR: - case V4L2_MEMORY_DMABUF: - imported = TRUE; - break; - } - - if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { - gint i; - - for (i = 0; i < group->n_mem; i++) { - size = allocator->format.fmt.pix_mp.plane_fmt[i].sizeimage; - - if (imported) - group->mem[i]->maxsize = size; - - gst_memory_resize (group->mem[i], 0, size); - } - - } else { - size = allocator->format.fmt.pix.sizeimage; - - if (imported) - group->mem[0]->maxsize = size; - - gst_memory_resize (group->mem[0], 0, size); + gint i; + for (i = 0; i < group->n_mem; i++) { + group->mem[i]->maxsize = group->planes[i].length; + group->mem[i]->offset = 0; + group->mem[i]->size = group->planes[i].length; } } @@ -607,11 +638,23 @@ _cleanup_failed_alloc (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) } } - +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER +static tbm_format __get_tbm_format (GstVideoFormat gst_format) +{ + switch (gst_format) { + case GST_VIDEO_FORMAT_NV12: + case GST_VIDEO_FORMAT_SN12: + return TBM_FORMAT_NV12; + case GST_VIDEO_FORMAT_I420: + case GST_VIDEO_FORMAT_S420: + default: + return TBM_FORMAT_YUV420; + } +} +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ GstV4l2Allocator * -gst_v4l2_allocator_new (GstObject * parent, gint video_fd, - struct v4l2_format *format) +gst_v4l2_allocator_new (GstObject * parent, GstV4l2Object * v4l2object) { GstV4l2Allocator *allocator; guint32 flags = 0; @@ -622,38 +665,67 @@ gst_v4l2_allocator_new (GstObject * parent, gint video_fd, g_free (parent_name); allocator = g_object_new (GST_TYPE_V4L2_ALLOCATOR, "name", name, NULL); + gst_object_ref_sink (allocator); g_free (name); /* Save everything */ - allocator->video_fd = v4l2_dup (video_fd); - allocator->type = format->type; - allocator->format = *format; + allocator->obj = v4l2object; + + /* Keep a ref on the elemnt so obj does not disapear */ + gst_object_ref (allocator->obj->element); flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, MMAP); flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, USERPTR); flags |= GST_V4L2_ALLOCATOR_PROBE (allocator, DMABUF); - GST_OBJECT_FLAG_SET (allocator, flags); - if (flags == 0) - goto not_supported; + if (flags == 0) { + /* Drivers not ported from videobuf to videbuf2 don't allow freeing buffers + * using REQBUFS(0). This is a workaround to still support these drivers, + * which are known to have MMAP support. */ + GST_WARNING_OBJECT (allocator, "Could not probe supported memory type, " + "assuming MMAP is supported, this is expected for older drivers not " + " yet ported to videobuf2 framework"); + flags = GST_V4L2_ALLOCATOR_FLAG_MMAP_REQBUFS; + } - return allocator; + GST_OBJECT_FLAG_SET (allocator, flags); -not_supported: - { - GST_ERROR_OBJECT (allocator, - "No memory model supported by GStreamer for this device"); - g_object_unref (allocator); - return NULL; +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + if (!V4L2_TYPE_IS_OUTPUT (v4l2object->type) && + v4l2object->mode == GST_V4L2_IO_DMABUF) { + tbm_surface_h tmp_surface = NULL; + int width = GST_VIDEO_INFO_WIDTH (&v4l2object->info); + int height = GST_VIDEO_INFO_HEIGHT (&v4l2object->info); + + tmp_surface = tbm_surface_create (width, height, + __get_tbm_format (GST_VIDEO_INFO_FORMAT (&v4l2object->info))); + if (tmp_surface) { + tbm_surface_get_info (tmp_surface, &allocator->s_info); + GST_INFO_OBJECT (allocator, "[%dx%d] -> tbm surface info[%dx%d]", + width, height, allocator->s_info.width, allocator->s_info.height); + tbm_surface_destroy (tmp_surface); + } else { + GST_ERROR_OBJECT (allocator, "[%dx%d] surface failed", width, height); + } + + allocator->bufmgr = tbm_bufmgr_init (-1); + if (!allocator->bufmgr) { + GST_ERROR_OBJECT (allocator, "tbm bufmgr failed"); + gst_object_unref (allocator); + return NULL; + } } +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ + return allocator; } guint gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count, guint32 memory) { - struct v4l2_requestbuffers breq = { count, allocator->type, memory }; + GstV4l2Object *obj = allocator->obj; + struct v4l2_requestbuffers breq = { count, obj->type, memory }; gboolean can_allocate; gint i; @@ -661,10 +733,13 @@ gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count, GST_OBJECT_LOCK (allocator); - if (allocator->active) + if (g_atomic_int_get (&allocator->active)) goto already_active; - if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) < 0) + if (GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator)) + goto orphaned; + + if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0) goto reqbufs_failed; if (breq.count < 1) @@ -709,8 +784,12 @@ done: already_active: { - GST_ERROR_OBJECT (allocator, - "error requesting %d buffers: %s", count, g_strerror (errno)); + GST_ERROR_OBJECT (allocator, "allocator already active"); + goto error; + } +orphaned: + { + GST_ERROR_OBJECT (allocator, "allocator was orphaned"); goto error; } reqbufs_failed: @@ -734,7 +813,8 @@ error: GstV4l2Return gst_v4l2_allocator_stop (GstV4l2Allocator * allocator) { - struct v4l2_requestbuffers breq = { 0, allocator->type, allocator->memory }; + GstV4l2Object *obj = allocator->obj; + struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory }; gint i = 0; GstV4l2Return ret = GST_V4L2_OK; @@ -742,7 +822,7 @@ gst_v4l2_allocator_stop (GstV4l2Allocator * allocator) GST_OBJECT_LOCK (allocator); - if (!allocator->active) + if (!g_atomic_int_get (&allocator->active)) goto done; if (gst_atomic_queue_length (allocator->free_queue) != allocator->count) { @@ -762,27 +842,46 @@ gst_v4l2_allocator_stop (GstV4l2Allocator * allocator) gst_v4l2_memory_group_free (group); } - if (v4l2_ioctl (allocator->video_fd, VIDIOC_REQBUFS, &breq) < 0) - goto reqbufs_failed; + if (!GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator)) { + /* Not all drivers support rebufs(0), so warn only */ + if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0) + GST_WARNING_OBJECT (allocator, + "error releasing buffers buffers: %s", g_strerror (errno)); + } + + allocator->count = 0; g_atomic_int_set (&allocator->active, FALSE); done: GST_OBJECT_UNLOCK (allocator); return ret; +} -reqbufs_failed: - { +gboolean +gst_v4l2_allocator_orphan (GstV4l2Allocator * allocator) +{ + GstV4l2Object *obj = allocator->obj; + struct v4l2_requestbuffers breq = { 0, obj->type, allocator->memory }; + + if (!GST_V4L2_ALLOCATOR_CAN_ORPHAN_BUFS (allocator)) + return FALSE; + + GST_OBJECT_FLAG_SET (allocator, GST_V4L2_ALLOCATOR_FLAG_ORPHANED); + + if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0) { GST_ERROR_OBJECT (allocator, - "error releasing buffers buffers: %s", g_strerror (errno)); - ret = GST_V4L2_ERROR; - goto done; + "error orphaning buffers buffers: %s", g_strerror (errno)); + return FALSE; } + + return TRUE; } GstV4l2MemoryGroup * gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator) { + GstV4l2Object *obj = allocator->obj; GstV4l2MemoryGroup *group; gint i; @@ -796,8 +895,8 @@ gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator) for (i = 0; i < group->n_mem; i++) { if (group->mem[i] == NULL) { gpointer data; - data = v4l2_mmap (NULL, group->planes[i].length, PROT_READ | PROT_WRITE, - MAP_SHARED, allocator->video_fd, group->planes[i].m.mem_offset); + data = obj->mmap (NULL, group->planes[i].length, PROT_READ | PROT_WRITE, + MAP_SHARED, obj->video_fd, group->planes[i].m.mem_offset); if (data == MAP_FAILED) goto mmap_failed; @@ -807,8 +906,8 @@ gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator) group->planes[i].length, group->planes[i].data_offset, i); group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator), - NULL, group->planes[i].length, 0, 0, group->planes[i].length, i, - data, -1, group); + NULL, group->planes[i].length, 0, 0, group->planes[i].length, i, data, + -1, group); } else { /* Take back the allocator reference */ gst_object_ref (allocator); @@ -838,8 +937,12 @@ GstV4l2MemoryGroup * gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator, GstAllocator * dmabuf_allocator) { + GstV4l2Object *obj = allocator->obj; GstV4l2MemoryGroup *group; gint i; +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + tbm_bo bos[VIDEO_MAX_PLANES] = {NULL, }; +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ g_return_val_if_fail (allocator->memory == V4L2_MEMORY_MMAP, NULL); @@ -851,44 +954,61 @@ gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator, for (i = 0; i < group->n_mem; i++) { GstV4l2Memory *mem; GstMemory *dma_mem; - gint dmafd; if (group->mem[i] == NULL) { struct v4l2_exportbuffer expbuf = { 0 }; - expbuf.type = allocator->type; + expbuf.type = obj->type; expbuf.index = group->buffer.index; expbuf.plane = i; expbuf.flags = O_CLOEXEC | O_RDWR; - if (v4l2_ioctl (allocator->video_fd, VIDIOC_EXPBUF, &expbuf) < 0) + if (obj->ioctl (obj->video_fd, VIDIOC_EXPBUF, &expbuf) < 0) goto expbuf_failed; GST_LOG_OBJECT (allocator, "exported DMABUF as fd %i plane %d", expbuf.fd, i); group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator), - NULL, group->planes[i].length, 0, 0, group->planes[i].length, i, - NULL, expbuf.fd, group); + NULL, group->planes[i].length, 0, group->planes[i].data_offset, + group->planes[i].length - group->planes[i].data_offset, i, NULL, + expbuf.fd, group); +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + bos[i] = tbm_bo_import_fd (allocator->bufmgr, expbuf.fd); + GST_INFO_OBJECT (allocator, "obj[%p,i:%d]: fd[%d] -> bo[%p]", + obj, expbuf.index, expbuf.fd, bos[i]); +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ } else { /* Take back the allocator reference */ gst_object_ref (allocator); } + group->mems_allocated++; + g_assert (gst_is_v4l2_memory (group->mem[i])); mem = (GstV4l2Memory *) group->mem[i]; - if ((dmafd = dup (mem->dmafd)) < 0) - goto dup_failed; + dma_mem = gst_fd_allocator_alloc (dmabuf_allocator, mem->dmafd, + group->planes[i].length, GST_FD_MEMORY_FLAG_DONT_CLOSE); + gst_memory_resize (dma_mem, group->planes[i].data_offset, + group->planes[i].length - group->planes[i].data_offset); - dma_mem = gst_dmabuf_allocator_alloc (dmabuf_allocator, dmafd, - mem->mem.maxsize); - _v4l2mem_parent_to_dmabuf (mem, dma_mem); + gst_mini_object_set_qdata (GST_MINI_OBJECT (dma_mem), + GST_V4L2_MEMORY_QUARK, mem, (GDestroyNotify) gst_memory_unref); group->mem[i] = dma_mem; - group->mems_allocated++; } +#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER + if (!group->surface) { + group->surface = tbm_surface_internal_create_with_bos (&allocator->s_info, bos, group->n_mem); + GST_INFO_OBJECT (allocator, "new surface[%p] in memory group[%p]", group->surface, group); + } + /* release bos - they will be kept in surface. */ + for (i = 0 ; i < VIDEO_MAX_PLANES && bos[i] ; i++) + tbm_bo_unref (bos[i]); +#endif /* TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER */ + gst_v4l2_allocator_reset_size (allocator, group); return group; @@ -899,12 +1019,6 @@ expbuf_failed: g_strerror (errno)); goto cleanup; } -dup_failed: - { - GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s", - g_strerror (errno)); - goto cleanup; - } cleanup: { _cleanup_failed_alloc (allocator, group); @@ -916,6 +1030,7 @@ static void gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { + GstV4l2Object *obj = allocator->obj; GstV4l2Memory *mem; gint i; @@ -925,11 +1040,8 @@ gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator, mem = (GstV4l2Memory *) group->mem[i]; - GST_LOG_OBJECT (allocator, "clearing DMABUF import, fd %i plane %d", - mem->dmafd, i); - - if (mem->dmafd >= 0) - close (mem->dmafd); + GST_LOG_OBJECT (allocator, "[%i] clearing DMABUF import, fd %i plane %d", + group->buffer.index, mem->dmafd, i); /* Update memory */ mem->mem.maxsize = 0; @@ -944,7 +1056,7 @@ gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator, group->planes[i].data_offset = 0; } - if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { + if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { group->buffer.bytesused = 0; group->buffer.length = 0; group->buffer.m.fd = -1; @@ -964,9 +1076,9 @@ gst_v4l2_allocator_alloc_dmabufin (GstV4l2Allocator * allocator) if (group == NULL) return NULL; - for (i = 0; i < group->n_mem; i++) { - GST_LOG_OBJECT (allocator, "allocation empty DMABUF import group"); + GST_LOG_OBJECT (allocator, "allocating empty DMABUF import group"); + for (i = 0; i < group->n_mem; i++) { if (group->mem[i] == NULL) { group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator), NULL, 0, 0, 0, 0, i, NULL, -1, group); @@ -987,6 +1099,7 @@ static void gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { + GstV4l2Object *obj = allocator->obj; GstV4l2Memory *mem; gint i; @@ -995,8 +1108,8 @@ gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator, for (i = 0; i < group->n_mem; i++) { mem = (GstV4l2Memory *) group->mem[i]; - GST_LOG_OBJECT (allocator, "clearing USERPTR %p plane %d size %" - G_GSIZE_FORMAT, mem->data, i, mem->mem.size); + GST_LOG_OBJECT (allocator, "[%i] clearing USERPTR %p plane %d size %" + G_GSIZE_FORMAT, group->buffer.index, mem->data, i, mem->mem.size); mem->mem.maxsize = 0; mem->mem.size = 0; @@ -1007,7 +1120,7 @@ gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator, group->planes[i].m.userptr = 0; } - if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { + if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { group->buffer.bytesused = 0; group->buffer.length = 0; group->buffer.m.userptr = 0; @@ -1027,9 +1140,9 @@ gst_v4l2_allocator_alloc_userptr (GstV4l2Allocator * allocator) if (group == NULL) return NULL; - for (i = 0; i < group->n_mem; i++) { + GST_LOG_OBJECT (allocator, "allocating empty USERPTR group"); - GST_LOG_OBJECT (allocator, "allocating empty USERPTR group"); + for (i = 0; i < group->n_mem; i++) { if (group->mem[i] == NULL) { group->mem[i] = (GstMemory *) _v4l2mem_new (0, GST_ALLOCATOR (allocator), @@ -1051,6 +1164,7 @@ gboolean gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group, gint n_mem, GstMemory ** dma_mem) { + GstV4l2Object *obj = allocator->obj; GstV4l2Memory *mem; gint i; @@ -1068,10 +1182,10 @@ gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator, size = gst_memory_get_sizes (dma_mem[i], &offset, &maxsize); - if ((dmafd = dup (gst_dmabuf_memory_get_fd (dma_mem[i]))) < 0) - goto dup_failed; + dmafd = gst_dmabuf_memory_get_fd (dma_mem[i]); - GST_LOG_OBJECT (allocator, "imported DMABUF as fd %i plane %d", dmafd, i); + GST_LOG_OBJECT (allocator, "[%i] imported DMABUF as fd %i plane %d", + group->buffer.index, dmafd, i); mem = (GstV4l2Memory *) group->mem[i]; @@ -1083,16 +1197,19 @@ gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator, /* Update v4l2 structure */ group->planes[i].length = maxsize; - group->planes[i].bytesused = size; + group->planes[i].bytesused = size + offset; group->planes[i].m.fd = dmafd; group->planes[i].data_offset = offset; } /* Copy into buffer structure if not using planes */ - if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { + if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { group->buffer.bytesused = group->planes[0].bytesused; group->buffer.length = group->planes[0].length; group->buffer.m.fd = group->planes[0].m.userptr; + + /* FIXME Check if data_offset > 0 and fail for non-multi-planar */ + g_assert (group->planes[0].data_offset == 0); } else { group->buffer.length = group->n_mem; } @@ -1110,63 +1227,51 @@ not_dmabuf: GST_ERROR_OBJECT (allocator, "Memory %i is not of DMABUF", i); return FALSE; } -dup_failed: - { - GST_ERROR_OBJECT (allocator, "Failed to dup DMABUF descriptor: %s", - g_strerror (errno)); - return FALSE; - } } gboolean gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group, gsize img_size, int n_planes, - gpointer * data, gsize * offset) + gpointer * data, gsize * size) { + GstV4l2Object *obj = allocator->obj; GstV4l2Memory *mem; gint i; g_return_val_if_fail (allocator->memory == V4L2_MEMORY_USERPTR, FALSE); /* TODO Support passing N plane from 1 memory to MPLANE v4l2 format */ - if (n_planes != group->n_mem) + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type) && n_planes != group->n_mem) goto n_mem_missmatch; for (i = 0; i < group->n_mem; i++) { - gsize size, maxsize; - - if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { - struct v4l2_pix_format_mplane *pix = &allocator->format.fmt.pix_mp; - maxsize = pix->plane_fmt[i].sizeimage; - } else { - maxsize = allocator->format.fmt.pix.sizeimage; - } + gsize maxsize, psize; - if ((i + 1) == n_planes) { - size = img_size - offset[i]; - } else { - size = offset[i + 1] - offset[i]; - } + /* TODO request used size and maxsize seperatly */ + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) + maxsize = psize = size[i]; + else + maxsize = psize = img_size; - g_assert (size <= img_size); + g_assert (psize <= img_size); - GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %" - G_GSIZE_FORMAT, data[i], i, size); + GST_LOG_OBJECT (allocator, "[%i] imported USERPTR %p plane %d size %" + G_GSIZE_FORMAT, group->buffer.index, data[i], i, psize); mem = (GstV4l2Memory *) group->mem[i]; mem->mem.maxsize = maxsize; - mem->mem.size = size; + mem->mem.size = psize; mem->data = data[i]; group->planes[i].length = maxsize; - group->planes[i].bytesused = size; + group->planes[i].bytesused = psize; group->planes[i].m.userptr = (unsigned long) data[i]; group->planes[i].data_offset = 0; } /* Copy into buffer structure if not using planes */ - if (!V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { + if (!V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { group->buffer.bytesused = group->planes[0].bytesused; group->buffer.length = group->planes[0].length; group->buffer.m.userptr = group->planes[0].m.userptr; @@ -1191,7 +1296,7 @@ gst_v4l2_allocator_flush (GstV4l2Allocator * allocator) GST_OBJECT_LOCK (allocator); - if (!allocator->active) + if (!g_atomic_int_get (&allocator->active)) goto done; for (i = 0; i < allocator->count; i++) { @@ -1216,11 +1321,14 @@ gboolean gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator, GstV4l2MemoryGroup * group) { + GstV4l2Object *obj = allocator->obj; gboolean ret = TRUE; gint i; + g_return_val_if_fail (g_atomic_int_get (&allocator->active), FALSE); + /* update sizes */ - if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { for (i = 0; i < group->n_mem; i++) group->planes[i].bytesused = gst_memory_get_sizes (group->mem[i], NULL, NULL); @@ -1228,9 +1336,18 @@ gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator, group->buffer.bytesused = gst_memory_get_sizes (group->mem[0], NULL, NULL); } - if (v4l2_ioctl (allocator->video_fd, VIDIOC_QBUF, &group->buffer) < 0) { - GST_ERROR_OBJECT (allocator, "failed queing buffer %i: %s", + /* Ensure the memory will stay around and is RO */ + for (i = 0; i < group->n_mem; i++) + gst_memory_ref (group->mem[i]); + + if (obj->ioctl (obj->video_fd, VIDIOC_QBUF, &group->buffer) < 0) { + GST_ERROR_OBJECT (allocator, "failed queueing buffer %i: %s", group->buffer.index, g_strerror (errno)); + + /* Release the memory, possibly making it RW again */ + for (i = 0; i < group->n_mem; i++) + gst_memory_unref (group->mem[i]); + ret = FALSE; if (IS_QUEUED (group->buffer)) { GST_DEBUG_OBJECT (allocator, @@ -1249,35 +1366,42 @@ gst_v4l2_allocator_qbuf (GstV4l2Allocator * allocator, SET_QUEUED (group->buffer); } - /* Ensure the memory will stay around and is RO */ - for (i = 0; i < group->n_mem; i++) - gst_memory_ref (group->mem[i]); - done: return ret; } -GstV4l2MemoryGroup * -gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator) +GstFlowReturn +gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator, + GstV4l2MemoryGroup ** group_out) { + GstV4l2Object *obj = allocator->obj; struct v4l2_buffer buffer = { 0 }; struct v4l2_plane planes[VIDEO_MAX_PLANES] = { {0} }; gint i; GstV4l2MemoryGroup *group = NULL; - buffer.type = allocator->type; + g_return_val_if_fail (g_atomic_int_get (&allocator->active), GST_FLOW_ERROR); + + buffer.type = obj->type; buffer.memory = allocator->memory; - if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { - buffer.length = allocator->format.fmt.pix_mp.num_planes; + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { + buffer.length = obj->format.fmt.pix_mp.num_planes; buffer.m.planes = planes; } - if (v4l2_ioctl (allocator->video_fd, VIDIOC_DQBUF, &buffer) < 0) + if (obj->ioctl (obj->video_fd, VIDIOC_DQBUF, &buffer) < 0) goto error; group = allocator->groups[buffer.index]; + + if (!IS_QUEUED (group->buffer)) { + GST_ERROR_OBJECT (allocator, + "buffer %i was not queued, this indicate a driver bug.", buffer.index); + return GST_FLOW_ERROR; + } + group->buffer = buffer; GST_LOG_OBJECT (allocator, "dequeued buffer %i (flags 0x%X)", buffer.index, @@ -1289,7 +1413,7 @@ gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator) UNSET_QUEUED (group->buffer); } - if (V4L2_TYPE_IS_MULTIPLANAR (allocator->type)) { + if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) { group->buffer.m.planes = group->planes; memcpy (group->planes, buffer.m.planes, sizeof (planes)); } else { @@ -1300,12 +1424,40 @@ gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator) } /* And update memory size */ - if (V4L2_TYPE_IS_OUTPUT (allocator->type)) { + if (V4L2_TYPE_IS_OUTPUT (obj->type)) { gst_v4l2_allocator_reset_size (allocator, group); } else { /* for capture, simply read the size */ for (i = 0; i < group->n_mem; i++) { - gst_memory_resize (group->mem[i], 0, group->planes[i].bytesused); + gsize size, offset; + + GST_LOG_OBJECT (allocator, + "Dequeued capture buffer, length: %u bytesused: %u data_offset: %u", + group->planes[i].length, group->planes[i].bytesused, + group->planes[i].data_offset); + + offset = group->planes[i].data_offset; + + if (group->planes[i].bytesused > group->planes[i].data_offset) { + size = group->planes[i].bytesused - group->planes[i].data_offset; + } else { + GST_WARNING_OBJECT (allocator, "V4L2 provided buffer has bytesused %" + G_GUINT32_FORMAT " which is too small to include data_offset %" + G_GUINT32_FORMAT, group->planes[i].bytesused, + group->planes[i].data_offset); + size = group->planes[i].bytesused; + } + + if (G_LIKELY (size + offset <= group->mem[i]->maxsize)) + gst_memory_resize (group->mem[i], offset, size); + else { + GST_WARNING_OBJECT (allocator, + "v4l2 provided buffer that is too big for the memory it was " + "writing into. v4l2 claims %" G_GSIZE_FORMAT " bytes used but " + "memory is only %" G_GSIZE_FORMAT "B. This is probably a driver " + "bug.", size, group->mem[i]->maxsize); + gst_memory_resize (group->mem[i], 0, group->mem[i]->maxsize); + } } } @@ -1313,9 +1465,15 @@ gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator) for (i = 0; i < group->n_mem; i++) gst_memory_unref (group->mem[i]); - return group; + *group_out = group; + return GST_FLOW_OK; error: + if (errno == EPIPE) { + GST_DEBUG_OBJECT (allocator, "broken pipe signals last buffer"); + return GST_FLOW_EOS; + } + GST_ERROR_OBJECT (allocator, "failed dequeuing a %s buffer: %s", memory_type_to_str (allocator->memory), g_strerror (errno)); @@ -1343,7 +1501,7 @@ error: " returning an error, or even stop capturing."); /* have we de-queued a buffer ? */ if (!IS_QUEUED (buffer)) { - GST_DEBUG_OBJECT (allocator, "reenqueing buffer"); + GST_DEBUG_OBJECT (allocator, "reenqueueing buffer"); /* FIXME ... should we do something here? */ } break; @@ -1357,7 +1515,7 @@ error: break; } - return NULL; + return GST_FLOW_ERROR; } void @@ -1380,17 +1538,3 @@ gst_v4l2_allocator_reset_group (GstV4l2Allocator * allocator, gst_v4l2_allocator_reset_size (allocator, group); } - -gsize -gst_v4l2_allocator_num_allocated (GstV4l2Allocator * allocator) -{ - gsize num_allocated; - - GST_OBJECT_LOCK (allocator); - - num_allocated = allocator->count; - - GST_OBJECT_UNLOCK (allocator); - - return num_allocated; -}