[PATCH 5/7 v5] V4L: vb2: add support for buffers of different sizes on a single queue

2011-08-24 Thread Guennadi Liakhovetski
The two recently added ioctl()s VIDIOC_CREATE_BUFS and VIDIOC_PREPARE_BUF
allow user-space applications to allocate video buffers of different
sizes and hand them over to the driver for fast switching between
different frame formats. This patch adds support for buffers of different
sizes on the same buffer-queue to vb2.

Signed-off-by: Guennadi Liakhovetski 
Cc: Hans Verkuil 
Cc: Laurent Pinchart 
Cc: Marek Szyprowski 
Cc: Mauro Carvalho Chehab 
Cc: Pawel Osciak 
Cc: Sakari Ailus 
---
 drivers/media/video/videobuf2-core.c |  278 --
 include/media/videobuf2-core.h   |   31 +++--
 2 files changed, 252 insertions(+), 57 deletions(-)

diff --git a/drivers/media/video/videobuf2-core.c 
b/drivers/media/video/videobuf2-core.c
index 8a81a89..fed6f2d 100644
--- a/drivers/media/video/videobuf2-core.c
+++ b/drivers/media/video/videobuf2-core.c
@@ -44,7 +44,7 @@ module_param(debug, int, 0644);
  * __vb2_buf_mem_alloc() - allocate video memory for the given buffer
  */
 static int __vb2_buf_mem_alloc(struct vb2_buffer *vb,
-   unsigned long *plane_sizes)
+  const unsigned long *plane_sizes)
 {
struct vb2_queue *q = vb->vb2_queue;
void *mem_priv;
@@ -110,13 +110,22 @@ static void __vb2_buf_userptr_put(struct vb2_buffer *vb)
  * __setup_offsets() - setup unique offsets ("cookies") for every plane in
  * every buffer on the queue
  */
-static void __setup_offsets(struct vb2_queue *q)
+static void __setup_offsets(struct vb2_queue *q, unsigned int n)
 {
unsigned int buffer, plane;
struct vb2_buffer *vb;
-   unsigned long off = 0;
+   unsigned long off;
 
-   for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+   if (q->num_buffers) {
+   struct v4l2_plane *p;
+   vb = q->bufs[q->num_buffers - 1];
+   p = &vb->v4l2_planes[vb->num_planes - 1];
+   off = PAGE_ALIGN(p->m.mem_offset + p->length);
+   } else {
+   off = 0;
+   }
+
+   for (buffer = q->num_buffers; buffer < q->num_buffers + n; ++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
@@ -142,7 +151,7 @@ static void __setup_offsets(struct vb2_queue *q)
  */
 static int __vb2_queue_alloc(struct vb2_queue *q, enum v4l2_memory memory,
 unsigned int num_buffers, unsigned int num_planes,
-unsigned long plane_sizes[])
+const unsigned long *plane_sizes)
 {
unsigned int buffer;
struct vb2_buffer *vb;
@@ -163,7 +172,7 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum 
v4l2_memory memory,
vb->state = VB2_BUF_STATE_DEQUEUED;
vb->vb2_queue = q;
vb->num_planes = num_planes;
-   vb->v4l2_buf.index = buffer;
+   vb->v4l2_buf.index = q->num_buffers + buffer;
vb->v4l2_buf.type = q->type;
vb->v4l2_buf.memory = memory;
 
@@ -191,15 +200,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum 
v4l2_memory memory,
}
}
 
-   q->bufs[buffer] = vb;
+   q->bufs[q->num_buffers + buffer] = vb;
}
 
-   q->num_buffers = buffer;
-
-   __setup_offsets(q);
+   __setup_offsets(q, buffer);
 
dprintk(1, "Allocated %d buffers, %d plane(s) each\n",
-   q->num_buffers, num_planes);
+   buffer, num_planes);
 
return buffer;
 }
@@ -207,12 +214,13 @@ static int __vb2_queue_alloc(struct vb2_queue *q, enum 
v4l2_memory memory,
 /**
  * __vb2_free_mem() - release all video buffer memory for a given queue
  */
-static void __vb2_free_mem(struct vb2_queue *q)
+static void __vb2_free_mem(struct vb2_queue *q, unsigned int buffers)
 {
unsigned int buffer;
struct vb2_buffer *vb;
 
-   for (buffer = 0; buffer < q->num_buffers; ++buffer) {
+   for (buffer = q->num_buffers - buffers; buffer < q->num_buffers;
+++buffer) {
vb = q->bufs[buffer];
if (!vb)
continue;
@@ -226,17 +234,18 @@ static void __vb2_free_mem(struct vb2_queue *q)
 }
 
 /**
- * __vb2_queue_free() - free the queue - video memory and related information
- * and return the queue to an uninitialized state. Might be called even if the
- * queue has already been freed.
+ * __vb2_queue_free() - free buffers at the end of the queue - video memory and
+ * related information, if no buffers are left return the queue to an
+ * uninitialized state. Might be called even if the queue has already been 
freed.
  */
-static void __vb2_queue_free(struct vb2_queue *q)
+static void __vb2_queue_free(struct vb2_queue *q, unsigned int buffers)
 {
unsigned int buffer;
 
/* Call driver-provided cleanup function for each buffer, if provided */
if (q->o

Re: [PATCH 5/7 v5] V4L: vb2: add support for buffers of different sizes on a single queue

2011-09-01 Thread Guennadi Liakhovetski
Hi Pawel

On Mon, 29 Aug 2011, Guennadi Liakhovetski wrote:

> On Sun, 28 Aug 2011, Pawel Osciak wrote:

[snip]

> > Could you share the userspace code that you used for testing this? I
> > just wanted to get a feel of how those new ioctls fall into place
> > together.
> 
> Theoretically - yes, just will have to clean up the code a bit;-) Will try 
> to find some time for this in the next couple of days.

Below is a patch to the current capture-example.c. It is not extremely 
intelligent, many things are hard-coded, but you should get the idea.

> > Also, did you try multiple CREATE_BUFS calls?
> 
> Yes, it currently does two CREATE_BUFS, I might try to mix REQBUFS with 
> CREATE_BUFS too though.

With this patch you can use the "-q" flag to choose, whether you want your 
first buffers to be allocated per REQBUFS or CREATE_BUFS. Both options 
work.

Locally I have an even much dirtier, but more useful version of this 
patch, that draws one buffer size on the framebuffer, and stores the other 
one in .pgm files, all works well.

Thanks
Guennadi
---
Guennadi Liakhovetski, Ph.D.
Freelance Open-Source Software Developer
http://www.open-technology.de/


diff --git a/contrib/test/capture-example.c b/contrib/test/capture-example.c
index 417615a..ab0d8fb 100644
--- a/contrib/test/capture-example.c
+++ b/contrib/test/capture-example.c
@@ -38,14 +38,23 @@ struct buffer {
size_t  length;
 };
 
+enum {
+   false   = 0,
+   true= 1
+};
+
 static char*dev_name;
 static enum io_method   io = IO_METHOD_MMAP;
 static int  fd = -1;
 struct buffer  *buffers;
-static unsigned int n_buffers;
+static unsigned int n_buffers, n_buffers_alt;
 static int out_buf;
 static int  force_format;
 static int  frame_count = 70;
+static unsigned intwidth, height, fourcc, colorspace;
+static unsigned intwidth_alt = 320, height_alt = 240,
+   fourcc_alt = V4L2_PIX_FMT_NV12, colorspace_alt = V4L2_COLORSPACE_JPEG;
+static _Bool   use_request;
 
 static void errno_exit(const char *s)
 {
@@ -64,6 +73,61 @@ static int xioctl(int fh, unsigned long int request, void 
*arg)
return r;
 }
 
+static int create_buffers(unsigned int *width, unsigned int *height,
+ unsigned int fcc, unsigned int clrspc, int n)
+{
+   int i, ret;
+   struct v4l2_create_buffers create = {
+   .memory = V4L2_MEMORY_MMAP,
+   .count = n,
+   .format = {
+   .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+   .fmt.pix = {
+   .width = *width,
+   .height = *height,
+   .pixelformat = fcc,
+   .field = V4L2_FIELD_ANY,
+   .colorspace = clrspc,
+   },
+   },
+   };
+   struct v4l2_buffer buf = {
+   .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+   .memory = V4L2_MEMORY_MMAP,
+   .field = V4L2_FIELD_ANY,
+   };
+   struct v4l2_pix_format *pix = &create.format.fmt.pix;
+
+   ret = xioctl(fd, VIDIOC_TRY_FMT, &create.format);
+   if (ret < 0) {
+   fprintf(stderr, "TRY_FMT should never fail!: %d\n", ret);
+   errno_exit("VIDIOC_TRY_FMT");
+   }
+   fprintf(stderr, "TRY_FMT(%ux%u:%x) -> %u\n", pix->width, pix->height,
+pix->pixelformat, pix->sizeimage);
+
+   *width = pix->width;
+   *height = pix->height;
+
+   ret = xioctl(fd, VIDIOC_CREATE_BUFS, &create);
+   if (ret < 0)
+   errno_exit("VIDIOC_CREATE_BUFS");
+
+   fprintf(stderr, "CREATE_BUFS(%d@%d)\n", create.count, create.index);
+
+   for (i = create.index; i < create.index + create.count; i++) {
+   buf.index = i;
+   ret = xioctl(fd, VIDIOC_PREPARE_BUF, &buf);
+   if (ret < 0)
+   errno_exit("VIDIOC_PREPARE_BUF");
+   }
+
+   if (i > create.index)
+   return i - create.index;
+
+   return ret;
+}
+
 static void process_image(const void *p, int size)
 {
if (out_buf)
@@ -74,7 +138,7 @@ static void process_image(const void *p, int size)
fflush(stdout);
 }
 
-static int read_frame(void)
+static int read_frame(_Bool do_queue)
 {
struct v4l2_buffer buf;
unsigned int i;
@@ -120,11 +184,11 @@ static int read_frame(void)
}
}
 
-   assert(buf.index < n_buffers);
+   assert(buf.index < n_buffers + n_buffers_alt);
 
process_image(buffers[buf.index].start, buf.bytesused);
 
-   if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
+   if (do_queue && -1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
break;
 
@@ -154,11 +218,11 @@ static int read_frame(void)
  

Re: [PATCH 5/7 v5] V4L: vb2: add support for buffers of different sizes on a single queue

2011-08-28 Thread Pawel Osciak
Hi Guennadi,

On Wed, Aug 24, 2011 at 11:41, Guennadi Liakhovetski
 wrote:
> The two recently added ioctl()s VIDIOC_CREATE_BUFS and VIDIOC_PREPARE_BUF
> allow user-space applications to allocate video buffers of different
> sizes and hand them over to the driver for fast switching between
> different frame formats. This patch adds support for buffers of different
> sizes on the same buffer-queue to vb2.
>
> Signed-off-by: Guennadi Liakhovetski 
> Cc: Hans Verkuil 
> Cc: Laurent Pinchart 
> Cc: Marek Szyprowski 
> Cc: Mauro Carvalho Chehab 
> Cc: Pawel Osciak 
> Cc: Sakari Ailus 
> ---
>  drivers/media/video/videobuf2-core.c |  278 
> --
>  include/media/videobuf2-core.h       |   31 +++--
>  2 files changed, 252 insertions(+), 57 deletions(-)
>
> diff --git a/drivers/media/video/videobuf2-core.c 
> b/drivers/media/video/videobuf2-core.c
> index 8a81a89..fed6f2d 100644
> --- a/drivers/media/video/videobuf2-core.c
> +++ b/drivers/media/video/videobuf2-core.c



> @@ -454,7 +465,7 @@ static bool __buffers_in_use(struct vb2_queue *q)
>  */
>  int vb2_reqbufs(struct vb2_queue *q, struct v4l2_requestbuffers *req)
>  {
> -       unsigned int num_buffers, num_planes;
> +       unsigned int num_buffers, allocated_buffers, num_planes = 0;
>        unsigned long plane_sizes[VIDEO_MAX_PLANES];
>        int ret = 0;
>
> @@ -503,7 +514,7 @@ int vb2_reqbufs(struct vb2_queue *q, struct 
> v4l2_requestbuffers *req)
>                        return -EBUSY;
>                }
>
> -               __vb2_queue_free(q);
> +               __vb2_queue_free(q, q->num_buffers);
>
>                /*
>                 * In case of REQBUFS(0) return immediately without calling
> @@ -538,44 +549,166 @@ int vb2_reqbufs(struct vb2_queue *q, struct 
> v4l2_requestbuffers *req)
>                return -ENOMEM;
>        }
>
> +       allocated_buffers = ret;
> +
>        /*
>         * Check if driver can handle the allocated number of buffers.
>         */
> -       if (ret < num_buffers) {
> -               unsigned int orig_num_buffers;
> +       if (allocated_buffers < num_buffers) {
> +               num_buffers = allocated_buffers;
>
> -               orig_num_buffers = num_buffers = ret;
>                ret = call_qop(q, queue_setup, q, NULL, &num_buffers,
>                               &num_planes, plane_sizes, q->alloc_ctx);
> -               if (ret)
> -                       goto free_mem;
>
> -               if (orig_num_buffers < num_buffers) {
> +               if (!ret && allocated_buffers < num_buffers)
>                        ret = -ENOMEM;
> -                       goto free_mem;
> -               }
>
>                /*
> -                * Ok, driver accepted smaller number of buffers.
> +                * Either the driver has accepted a smaller number of buffers,
> +                * or .queue_setup() returned an error
>                 */
> -               ret = num_buffers;
> +       }
> +
> +       q->num_buffers = allocated_buffers;
> +
> +       if (ret < 0) {
> +               __vb2_queue_free(q, allocated_buffers);
> +               return ret;
>        }
>
>        /*
>         * Return the number of successfully allocated buffers
>         * to the userspace.
>         */
> -       req->count = ret;
> +       req->count = allocated_buffers;
>
>        return 0;
> -
> -free_mem:
> -       __vb2_queue_free(q);
> -       return ret;
>  }
>  EXPORT_SYMBOL_GPL(vb2_reqbufs);
>
>  /**
> + * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
> + * @q:         videobuf2 queue
> + * @create:    creation parameters, passed from userspace to 
> vidioc_create_bufs
> + *             handler in driver
> + *
> + * Should be called from vidioc_create_bufs ioctl handler of a driver.
> + * This function:
> + * 1) verifies parameter sanity
> + * 2) calls the .queue_setup() queue operation
> + * 3) performs any necessary memory allocations
> + *
> + * The return values from this function are intended to be directly returned
> + * from vidioc_create_bufs handler in driver.
> + */
> +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers *create)
> +{
> +       unsigned int num_planes, num_buffers = create->count, 
> allocated_buffers;
> +       unsigned long plane_sizes[VIDEO_MAX_PLANES];
> +       int ret = 0;
> +
> +       if (q->fileio) {
> +               dprintk(1, "%s(): file io in progress\n", __func__);
> +               return -EBUSY;
> +       }
> +
> +       if (create->memory != V4L2_MEMORY_MMAP
> +                       && create->memory != V4L2_MEMORY_USERPTR) {
> +               dprintk(1, "%s(): unsupported memory type\n", __func__);
> +               return -EINVAL;
> +       }
> +
> +       if (create->format.type != q->type) {
> +               dprintk(1, "%s(): requested type is incorrect\n", __func__);
> +               return -EINVAL;
> +       }
> +
> +       /*
> +        * Make sure all the required memory ops for given memory type
> +     

Re: [PATCH 5/7 v5] V4L: vb2: add support for buffers of different sizes on a single queue

2011-08-29 Thread Guennadi Liakhovetski
Hi Pawel

Thanks for the review.

On Sun, 28 Aug 2011, Pawel Osciak wrote:

> Hi Guennadi,
> 
> On Wed, Aug 24, 2011 at 11:41, Guennadi Liakhovetski
>  wrote:
> > The two recently added ioctl()s VIDIOC_CREATE_BUFS and VIDIOC_PREPARE_BUF
> > allow user-space applications to allocate video buffers of different
> > sizes and hand them over to the driver for fast switching between
> > different frame formats. This patch adds support for buffers of different
> > sizes on the same buffer-queue to vb2.
> >
> > Signed-off-by: Guennadi Liakhovetski 
> > Cc: Hans Verkuil 
> > Cc: Laurent Pinchart 
> > Cc: Marek Szyprowski 
> > Cc: Mauro Carvalho Chehab 
> > Cc: Pawel Osciak 
> > Cc: Sakari Ailus 
> > ---
> >  drivers/media/video/videobuf2-core.c |  278 
> > --
> >  include/media/videobuf2-core.h       |   31 +++--
> >  2 files changed, 252 insertions(+), 57 deletions(-)
> >
> > diff --git a/drivers/media/video/videobuf2-core.c 
> > b/drivers/media/video/videobuf2-core.c
> > index 8a81a89..fed6f2d 100644
> > --- a/drivers/media/video/videobuf2-core.c
> > +++ b/drivers/media/video/videobuf2-core.c

[snip]

> >  /**
> > + * vb2_create_bufs() - Allocate buffers and any required auxiliary structs
> > + * @q:         videobuf2 queue
> > + * @create:    creation parameters, passed from userspace to 
> > vidioc_create_bufs
> > + *             handler in driver
> > + *
> > + * Should be called from vidioc_create_bufs ioctl handler of a driver.
> > + * This function:
> > + * 1) verifies parameter sanity
> > + * 2) calls the .queue_setup() queue operation
> > + * 3) performs any necessary memory allocations
> > + *
> > + * The return values from this function are intended to be directly 
> > returned
> > + * from vidioc_create_bufs handler in driver.
> > + */
> > +int vb2_create_bufs(struct vb2_queue *q, struct v4l2_create_buffers 
> > *create)
> > +{
> > +       unsigned int num_planes, num_buffers = create->count, 
> > allocated_buffers;
> > +       unsigned long plane_sizes[VIDEO_MAX_PLANES];
> > +       int ret = 0;
> > +
> > +       if (q->fileio) {
> > +               dprintk(1, "%s(): file io in progress\n", __func__);
> > +               return -EBUSY;
> > +       }
> > +
> > +       if (create->memory != V4L2_MEMORY_MMAP
> > +                       && create->memory != V4L2_MEMORY_USERPTR) {
> > +               dprintk(1, "%s(): unsupported memory type\n", __func__);
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (create->format.type != q->type) {
> > +               dprintk(1, "%s(): requested type is incorrect\n", __func__);
> > +               return -EINVAL;
> > +       }
> > +
> > +       /*
> > +        * Make sure all the required memory ops for given memory type
> > +        * are available.
> > +        */
> > +       if (create->memory == V4L2_MEMORY_MMAP && __verify_mmap_ops(q)) {
> > +               dprintk(1, "%s(): MMAP for current setup unsupported\n", 
> > __func__);
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (create->memory == V4L2_MEMORY_USERPTR && 
> > __verify_userptr_ops(q)) {
> > +               dprintk(1, "%s(): USERPTR for current setup unsupported\n", 
> > __func__);
> > +               return -EINVAL;
> > +       }
> > +
> > +       if (q->num_buffers == VIDEO_MAX_FRAME) {
> > +               dprintk(1, "%s(): maximum number of buffers already 
> > allocated\n",
> > +                       __func__);
> > +               return -ENOBUFS;
> > +       }
> 
> I think we should be verifying that q->num_buffers + create->count <=
> VIDEO_MAX_FRAME.

Yeah, something like that, thanks. Some of us didn't like this 
VIDEO_MAX_FRAME limit at all, but as long as we have it, e-g-. as long as 
struct vb2_queue::bufs[] is a fixed-size array with exactly that many 
elements, we have to check for it. Increasing / eliminating it could be a 
separate patch.

> 
> > +
> > +       create->index = q->num_buffers;
> > +
> > +       if (!q->num_buffers) {
> > +               memset(q->alloc_ctx, 0, sizeof(q->alloc_ctx));
> > +               q->memory = create->memory;
> > +       }
> > +
> > +       /*
> > +        * Ask the driver, whether the requested number of buffers, planes 
> > per
> > +        * buffer and their sizes are acceptable
> > +        */
> > +       ret = call_qop(q, queue_setup, q, &create->format, &num_buffers,
> > +                      &num_planes, plane_sizes, q->alloc_ctx);
> 
> I don't see you zeroing neither num_planes nor plane_sizes[] in
> vb2_create_bufs and vb2_reqbufs. Since instead of always requiring the
> driver to fill them, you've introduced the "non-zero num_planes and/or
> plane_sizes" case (see my comment for queue_setup() documentation in
> videobuf2-core.h), it looks to me that the drivers will be getting
> random values in num_planes and plane_sizes in queue_setup() and will
> have to attempt to use them. Ditto for all other qop calls to
> queue_setup in this file (in vb2_reqbufs as well).