v4l2bufferpool: fix import_userptr() in single-planar API when n_planes > 1
authorAurélien Zanelli <aurelien.zanelli@parrot.com>
Mon, 6 Oct 2014 15:30:06 +0000 (17:30 +0200)
committerNicolas Dufresne <nicolas.dufresne@collabora.co.uk>
Wed, 25 Feb 2015 19:25:01 +0000 (14:25 -0500)
In the V4L2 single-planar API, when format is semi-planar/planar,
drivers expect the planes to be contiguous in memory.
So this commit change the way we handle semi-planar/planar format
(n_planes > 1) when we use the single-planar API (group->n_mem == 1).

To check that planes are contiguous and have expected size, ie: no
padding. We test the fact that plane 'i' start address + plane 'i'
expected size equals to plane 'i + 1' start address. If not, we return
in error.

Math are done in bufferpool rather than in allocator because the
former is aware of video info.

https://bugzilla.gnome.org/show_bug.cgi?id=738013

sys/v4l2/gstv4l2allocator.c
sys/v4l2/gstv4l2bufferpool.c
sys/v4l2/gstv4l2object.c
sys/v4l2/gstv4l2object.h

index a2c96a1..b3d02eb 100644 (file)
@@ -1143,32 +1143,34 @@ gst_v4l2_allocator_import_userptr (GstV4l2Allocator * allocator,
   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 (allocator->type) && n_planes != group->n_mem)
     goto n_mem_missmatch;
 
   for (i = 0; i < group->n_mem; i++) {
-    gsize 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;
+      psize = size[i];
     } else {
       maxsize = allocator->format.fmt.pix.sizeimage;
+      psize = img_size;
     }
 
-    g_assert (size[i] <= img_size);
+    g_assert (psize <= img_size);
 
     GST_LOG_OBJECT (allocator, "imported USERPTR %p plane %d size %"
-        G_GSIZE_FORMAT, data[i], i, size[i]);
+        G_GSIZE_FORMAT, data[i], i, psize);
 
     mem = (GstV4l2Memory *) group->mem[i];
 
     mem->mem.maxsize = maxsize;
-    mem->mem.size = size[i];
+    mem->mem.size = psize;
     mem->data = data[i];
 
     group->planes[i].length = maxsize;
-    group->planes[i].bytesused = size[i];
+    group->planes[i].bytesused = psize;
     group->planes[i].m.userptr = (unsigned long) data[i];
     group->planes[i].data_offset = 0;
   }
index 2096423..543ce91 100644 (file)
@@ -246,6 +246,25 @@ gst_v4l2_buffer_pool_import_userptr (GstV4l2BufferPool * pool,
       }
     }
 
+    /* In the single planar API, planes must be contiguous in memory and
+     * therefore they must have expected size. ie: no padding.
+     * To check these conditions, we check that plane 'i' start address
+     * + plane 'i' size equals to plane 'i+1' start address */
+    if (!V4L2_TYPE_IS_MULTIPLANAR (pool->obj->type)) {
+      for (i = 0; i < (GST_VIDEO_FORMAT_INFO_N_PLANES (finfo) - 1); i++) {
+        const struct v4l2_pix_format *pix_fmt = &pool->obj->format.fmt.pix;
+        gpointer tmp;
+        gint estride = gst_v4l2_object_extrapolate_stride (finfo, i,
+            pix_fmt->bytesperline);
+        guint eheight = GST_VIDEO_FORMAT_INFO_SCALE_HEIGHT (finfo, i,
+            pix_fmt->height);
+
+        tmp = ((guint8 *) data->frame.data[i]) + estride * eheight;
+        if (tmp != data->frame.data[i + 1])
+          goto non_contiguous_mem;
+      }
+    }
+
     if (!gst_v4l2_allocator_import_userptr (pool->vallocator, group,
             data->frame.info.size, finfo->n_planes, data->frame.data, size))
       goto import_failed;
@@ -284,6 +303,12 @@ invalid_buffer:
     g_slice_free (struct UserPtrData, data);
     return GST_FLOW_ERROR;
   }
+non_contiguous_mem:
+  {
+    GST_ERROR_OBJECT (pool, "memory is not contiguous or plane size mismatch");
+    _unmap_userptr_frame (data);
+    return GST_FLOW_ERROR;
+  }
 import_failed:
   {
     GST_ERROR_OBJECT (pool, "failed to import data");
index 6f6b9db..6d1856b 100644 (file)
@@ -2413,19 +2413,7 @@ gst_v4l2_object_extrapolate_info (GstV4l2Object * v4l2object,
   padded_height = info->height + align->padding_top + align->padding_bottom;
 
   for (i = 0; i < finfo->n_planes; i++) {
-    switch (finfo->format) {
-      case GST_VIDEO_FORMAT_NV12:
-      case GST_VIDEO_FORMAT_NV12_64Z32:
-      case GST_VIDEO_FORMAT_NV21:
-      case GST_VIDEO_FORMAT_NV16:
-      case GST_VIDEO_FORMAT_NV24:
-        estride = (i == 0 ? 1 : 2) *
-            GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (finfo, i, stride);
-        break;
-      default:
-        estride = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (finfo, i, stride);
-        break;
-    }
+    estride = gst_v4l2_object_extrapolate_stride (finfo, i, stride);
 
     gst_v4l2_object_set_stride (info, align, i, estride);
 
@@ -2568,6 +2556,29 @@ store_info:
   }
 }
 
+gint
+gst_v4l2_object_extrapolate_stride (const GstVideoFormatInfo * finfo,
+    gint plane, gint stride)
+{
+  gint estride;
+
+  switch (finfo->format) {
+    case GST_VIDEO_FORMAT_NV12:
+    case GST_VIDEO_FORMAT_NV12_64Z32:
+    case GST_VIDEO_FORMAT_NV21:
+    case GST_VIDEO_FORMAT_NV16:
+    case GST_VIDEO_FORMAT_NV24:
+      estride = (plane == 0 ? 1 : 2) *
+          GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (finfo, plane, stride);
+      break;
+    default:
+      estride = GST_VIDEO_FORMAT_INFO_SCALE_WIDTH (finfo, plane, stride);
+      break;
+  }
+
+  return estride;
+}
+
 gboolean
 gst_v4l2_object_set_format (GstV4l2Object * v4l2object, GstCaps * caps)
 {
index 6dbf473..380abfb 100644 (file)
@@ -247,6 +247,9 @@ GstCaps*      gst_v4l2_object_get_raw_caps (void);
 
 GstCaps*      gst_v4l2_object_get_codec_caps (void);
 
+gint          gst_v4l2_object_extrapolate_stride (const GstVideoFormatInfo * finfo,
+                                                  gint plane, gint stride);
+
 gboolean      gst_v4l2_object_set_format  (GstV4l2Object * v4l2object, GstCaps * caps);
 
 gboolean      gst_v4l2_object_caps_equal  (GstV4l2Object * v4l2object, GstCaps * caps);