[v4l2videodecoder] Support TBM for output buffer
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / gstv4l2allocator.c
index 3e56570..5f6f554 100644 (file)
 
 #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 <gst/allocators/gstdmabuf.h>
 
 #include <sys/stat.h>
 #include <sys/types.h>
 #include <sys/mman.h>
+#include <unistd.h>
+#ifdef TIZEN_FEATURE_TBM_SUPPORT_FOR_V4L2_DECODER
+#include <tbm_surface.h>
+#include <tbm_surface_internal.h>
+#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,19 +614,11 @@ static void
 gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator,
     GstV4l2MemoryGroup * group)
 {
-  gsize size;
-
-  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;
-      gst_memory_resize (group->mem[i], 0, size);
-    }
-
-  } else {
-    size = allocator->format.fmt.pix.sizeimage;
-    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;
   }
 }
 
@@ -591,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;
@@ -606,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;
+  }
+
+  GST_OBJECT_FLAG_SET (allocator, flags);
 
-  return allocator;
+#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);
+    }
 
-not_supported:
-  {
-    GST_ERROR_OBJECT (allocator,
-        "No memory model supported by GStreamer for this device");
-    g_object_unref (allocator);
-    return NULL;
+    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;
 
@@ -645,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)
@@ -693,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:
@@ -718,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;
 
@@ -726,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) {
@@ -746,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;
 
@@ -780,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;
@@ -791,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);
@@ -822,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);
 
@@ -835,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;
@@ -883,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);
@@ -900,6 +1030,7 @@ static void
 gst_v4l2_allocator_clear_dmabufin (GstV4l2Allocator * allocator,
     GstV4l2MemoryGroup * group)
 {
+  GstV4l2Object *obj = allocator->obj;
   GstV4l2Memory *mem;
   gint i;
 
@@ -909,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;
@@ -928,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;
@@ -948,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);
@@ -971,6 +1099,7 @@ static void
 gst_v4l2_allocator_clear_userptr (GstV4l2Allocator * allocator,
     GstV4l2MemoryGroup * group)
 {
+  GstV4l2Object *obj = allocator->obj;
   GstV4l2Memory *mem;
   gint i;
 
@@ -979,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;
@@ -991,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;
@@ -1011,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),
@@ -1035,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;
 
@@ -1052,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];
 
@@ -1067,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;
   }
@@ -1094,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;
+    gsize maxsize, psize;
 
-    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;
-    }
-
-    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;
@@ -1175,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++) {
@@ -1200,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);
@@ -1212,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,
@@ -1233,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,
@@ -1273,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 {
@@ -1284,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);
+      }
     }
   }
 
@@ -1297,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));
 
@@ -1327,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;
@@ -1341,7 +1515,7 @@ error:
       break;
   }
 
-  return NULL;
+  return GST_FLOW_ERROR;
 }
 
 void
@@ -1356,24 +1530,11 @@ gst_v4l2_allocator_reset_group (GstV4l2Allocator * allocator,
       gst_v4l2_allocator_clear_dmabufin (allocator, group);
       break;
     case V4L2_MEMORY_MMAP:
-      gst_v4l2_allocator_reset_size (allocator, group);
       break;
     default:
       g_assert_not_reached ();
       break;
   }
-}
-
-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;
+  gst_v4l2_allocator_reset_size (allocator, group);
 }