[v4l2videodecoder] Support TBM for output buffer
[platform/upstream/gst-plugins-good.git] / sys / v4l2 / gstv4l2allocator.c
index 24bdc0f..5f6f554 100644 (file)
 #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"
 
@@ -235,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);
 }
@@ -295,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));
   }
@@ -311,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);
     }
@@ -425,6 +439,13 @@ 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 */
 
   gst_atomic_queue_unref (allocator->free_queue);
   gst_object_unref (allocator->obj->element);
@@ -500,6 +521,9 @@ gst_v4l2_allocator_probe (GstV4l2Allocator * allocator, guint32 memory,
       flags |= bcreate_flag;
   }
 
+  if (breq.capabilities & V4L2_BUF_CAP_SUPPORTS_ORPHANED_BUFS)
+    flags |= GST_V4L2_ALLOCATOR_FLAG_SUPPORTS_ORPHANED_BUFS;
+
   return flags;
 }
 
@@ -515,6 +539,9 @@ gst_v4l2_allocator_create_buf (GstV4l2Allocator * allocator)
   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 = obj->format;
   bcreate.count = 1;
@@ -539,6 +566,12 @@ 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",
@@ -581,36 +614,11 @@ static void
 gst_v4l2_allocator_reset_size (GstV4l2Allocator * allocator,
     GstV4l2MemoryGroup * group)
 {
-  GstV4l2Object *obj = allocator->obj;
-  gsize size;
-  gboolean imported = FALSE;
-
-  switch (allocator->memory) {
-    case V4L2_MEMORY_USERPTR:
-    case V4L2_MEMORY_DMABUF:
-      imported = TRUE;
-      break;
-  }
-
-  if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
-    gint i;
-
-    for (i = 0; i < group->n_mem; i++) {
-      size = obj->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 = obj->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;
   }
 }
 
@@ -630,7 +638,20 @@ _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, GstV4l2Object * v4l2object)
@@ -670,6 +691,32 @@ gst_v4l2_allocator_new (GstObject * parent, GstV4l2Object * v4l2object)
 
   GST_OBJECT_FLAG_SET (allocator, flags);
 
+#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;
 }
 
@@ -689,6 +736,9 @@ gst_v4l2_allocator_start (GstV4l2Allocator * allocator, guint32 count,
   if (g_atomic_int_get (&allocator->active))
     goto already_active;
 
+  if (GST_V4L2_ALLOCATOR_IS_ORPHANED (allocator))
+    goto orphaned;
+
   if (obj->ioctl (obj->video_fd, VIDIOC_REQBUFS, &breq) < 0)
     goto reqbufs_failed;
 
@@ -737,6 +787,11 @@ already_active:
     GST_ERROR_OBJECT (allocator, "allocator already active");
     goto error;
   }
+orphaned:
+  {
+    GST_ERROR_OBJECT (allocator, "allocator was orphaned");
+    goto error;
+  }
 reqbufs_failed:
   {
     GST_ERROR_OBJECT (allocator,
@@ -787,10 +842,12 @@ gst_v4l2_allocator_stop (GstV4l2Allocator * allocator)
       gst_v4l2_memory_group_free (group);
   }
 
-  /* 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));
+  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;
 
@@ -801,6 +858,26 @@ done:
   return ret;
 }
 
+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 orphaning buffers buffers: %s", g_strerror (errno));
+    return FALSE;
+  }
+
+  return TRUE;
+}
+
 GstV4l2MemoryGroup *
 gst_v4l2_allocator_alloc_mmap (GstV4l2Allocator * allocator)
 {
@@ -829,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);
@@ -863,6 +940,9 @@ gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * 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);
 
@@ -874,7 +954,6 @@ 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 };
@@ -891,8 +970,14 @@ gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
           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);
@@ -903,11 +988,10 @@ gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
     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_dmabuf_allocator_alloc (dmabuf_allocator, dmafd,
-        mem->mem.maxsize);
+    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);
 
     gst_mini_object_set_qdata (GST_MINI_OBJECT (dma_mem),
         GST_V4L2_MEMORY_QUARK, mem, (GDestroyNotify) gst_memory_unref);
@@ -915,6 +999,16 @@ gst_v4l2_allocator_alloc_dmabuf (GstV4l2Allocator * allocator,
     group->mem[i] = dma_mem;
   }
 
+#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;
@@ -925,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);
@@ -952,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;
@@ -1023,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;
@@ -1097,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];
 
@@ -1112,7 +1197,7 @@ 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;
   }
@@ -1122,6 +1207,9 @@ gst_v4l2_allocator_import_dmabuf (GstV4l2Allocator * allocator,
     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;
   }
@@ -1139,12 +1227,6 @@ 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
@@ -1165,19 +1247,16 @@ gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
   for (i = 0; i < group->n_mem; i++) {
     gsize maxsize, psize;
 
-    if (V4L2_TYPE_IS_MULTIPLANAR (obj->type)) {
-      struct v4l2_pix_format_mplane *pix = &obj->format.fmt.pix_mp;
-      maxsize = pix->plane_fmt[i].sizeimage;
-      psize = size[i];
-    } else {
-      maxsize = obj->format.fmt.pix.sizeimage;
-      psize = img_size;
-    }
+    /* 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 (psize <= img_size);
 
-    GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %"
-        G_GSIZE_FORMAT, data[i], i, psize);
+    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];
 
@@ -1350,14 +1429,33 @@ gst_v4l2_allocator_dqbuf (GstV4l2Allocator * allocator,
   } else {
     /* for capture, simply read the size */
     for (i = 0; i < group->n_mem; i++) {
-      if (G_LIKELY (group->planes[i].bytesused <= group->mem[i]->maxsize))
-        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_GUINT32_FORMAT " bytes used but "
+            "writing into.  v4l2 claims %" G_GSIZE_FORMAT " bytes used but "
             "memory is only %" G_GSIZE_FORMAT "B.  This is probably a driver "
-            "bug.", group->planes[i].bytesused, group->mem[i]->maxsize);
+            "bug.", size, group->mem[i]->maxsize);
         gst_memory_resize (group->mem[i], 0, group->mem[i]->maxsize);
       }
     }