@@ -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 int width, height, fourcc, colorspace;
+static unsigned int width_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)
&& buf.length == buffers[i].length)
break;
- assert(i < n_buffers);
+ assert(i < n_buffers + n_buffers_alt);
process_image((void *)buf.m.userptr, buf.bytesused);
- if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
+ if (do_queue && -1 == xioctl(fd, VIDIOC_QBUF, &buf))
errno_exit("VIDIOC_QBUF");
break;
}
@@ -166,12 +230,8 @@ static int read_frame(void)
return 1;
}
-static void mainloop(void)
+static void mainloop(unsigned int count, _Bool do_queue)
{
- unsigned int count;
-
- count = frame_count;
-
while (count-- > 0) {
for (;;) {
fd_set fds;
@@ -198,7 +258,7 @@ static void mainloop(void)
exit(EXIT_FAILURE);
}
- if (read_frame())
+ if (read_frame(do_queue))
break;
/* EAGAIN - continue select loop. */
}
@@ -223,7 +283,7 @@ static void stop_capturing(void)
}
}
-static void start_capturing(void)
+static void start_capturing(unsigned int index, unsigned int count)
{
unsigned int i;
enum v4l2_buf_type type;
@@ -234,7 +294,7 @@ static void start_capturing(void)
break;
case IO_METHOD_MMAP:
- for (i = 0; i < n_buffers; ++i) {
+ for (i = index; i < index + count; ++i) {
struct v4l2_buffer buf;
CLEAR(buf);
@@ -251,7 +311,7 @@ static void start_capturing(void)
break;
case IO_METHOD_USERPTR:
- for (i = 0; i < n_buffers; ++i) {
+ for (i = index; i < index + count; ++i) {
struct v4l2_buffer buf;
CLEAR(buf);
@@ -281,7 +341,7 @@ static void uninit_device(void)
break;
case IO_METHOD_MMAP:
- for (i = 0; i < n_buffers; ++i)
+ for (i = 0; i < n_buffers + n_buffers_alt; ++i)
if (-1 == munmap(buffers[i].start, buffers[i].length))
errno_exit("munmap");
break;
@@ -313,15 +373,13 @@ static void init_read(unsigned int buffer_size)
}
}
-static void init_mmap(void)
+static int request_buffers(unsigned int count)
{
- struct v4l2_requestbuffers req;
-
- CLEAR(req);
-
- req.count = 4;
- req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- req.memory = V4L2_MEMORY_MMAP;
+ struct v4l2_requestbuffers req = {
+ .count = count,
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .memory = V4L2_MEMORY_MMAP,
+ };
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
if (EINVAL == errno) {
@@ -333,40 +391,56 @@ static void init_mmap(void)
}
}
- if (req.count < 2) {
- fprintf(stderr, "Insufficient buffer memory on %s\n",
- dev_name);
- exit(EXIT_FAILURE);
- }
+ return req.count;
+}
+
+static void init_mmap(void)
+{
+ int ret;
+ int nbuf, i;
+
+ if (use_request)
+ ret = request_buffers(4);
+ else
+ ret = create_buffers(&width, &height, fourcc, colorspace, 4);
+
+ if (ret < 0)
+ errno_exit("{create,request}_buffers(main)");
- buffers = calloc(req.count, sizeof(*buffers));
+ n_buffers = ret;
+ ret = create_buffers(&width_alt, &height_alt, fourcc_alt, colorspace_alt, 4);
+ if (ret < 0)
+ errno_exit("create_buffers(alternate)");
+
+ n_buffers_alt = ret;
+ nbuf = n_buffers_alt + n_buffers;
+
+ buffers = calloc(nbuf, sizeof(*buffers));
if (!buffers) {
fprintf(stderr, "Out of memory\n");
exit(EXIT_FAILURE);
}
- for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
- struct v4l2_buffer buf;
-
- CLEAR(buf);
-
- buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
- buf.memory = V4L2_MEMORY_MMAP;
- buf.index = n_buffers;
+ for (i = 0; i < nbuf; ++i) {
+ struct v4l2_buffer buf = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .memory = V4L2_MEMORY_MMAP,
+ .index = i,
+ };
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
errno_exit("VIDIOC_QUERYBUF");
- buffers[n_buffers].length = buf.length;
- buffers[n_buffers].start =
+ buffers[i].length = buf.length;
+ buffers[i].start =
mmap(NULL /* start anywhere */,
buf.length,
PROT_READ | PROT_WRITE /* required */,
MAP_SHARED /* recommended */,
fd, buf.m.offset);
- if (MAP_FAILED == buffers[n_buffers].start)
+ if (MAP_FAILED == buffers[i].start)
errno_exit("mmap");
}
}
@@ -409,13 +483,29 @@ static void init_userp(unsigned int buffer_size)
}
}
+static int s_fmt(unsigned int width, unsigned int height, unsigned int fcc, unsigned int clrspc)
+{
+ struct v4l2_format fmt = {
+ .type = V4L2_BUF_TYPE_VIDEO_CAPTURE,
+ .fmt.pix = {
+ .width = width,
+ .height = height,
+ .pixelformat = fcc,
+ .field = V4L2_FIELD_ANY,
+ .colorspace = clrspc,
+ },
+ };
+ fprintf(stderr, "S_FMT(%ux%u:%x)\n", width, height, fcc);
+
+ return xioctl(fd, VIDIOC_S_FMT, &fmt);
+}
+
static void init_device(void)
{
struct v4l2_capability cap;
struct v4l2_cropcap cropcap;
struct v4l2_crop crop;
struct v4l2_format fmt;
- unsigned int min;
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
if (EINVAL == errno) {
@@ -485,8 +575,8 @@ static void init_device(void)
if (force_format) {
fmt.fmt.pix.width = 640;
fmt.fmt.pix.height = 480;
- fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
- fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
+ fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_NV16;
+ fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
errno_exit("VIDIOC_S_FMT");
@@ -498,6 +588,11 @@ static void init_device(void)
errno_exit("VIDIOC_G_FMT");
}
+ width = fmt.fmt.pix.width;
+ height = fmt.fmt.pix.height;
+ colorspace = fmt.fmt.pix.colorspace;
+ fourcc = fmt.fmt.pix.pixelformat;
+
switch (io) {
case IO_METHOD_READ:
init_read(fmt.fmt.pix.sizeimage);
@@ -559,11 +654,12 @@ static void usage(FILE *fp, int argc, char **argv)
"-o | --output Outputs stream to stdout\n"
"-f | --format Force format to 640x480 YUYV\n"
"-c | --count Number of frames to grab [%i]\n"
+ "-q | --request Use REQBUFS for the first buffer set\n"
"",
argv[0], dev_name, frame_count);
}
-static const char short_options[] = "d:hmruofc:";
+static const char short_options[] = "d:hmruofc:q";
static const struct option
long_options[] = {
@@ -575,6 +671,7 @@ long_options[] = {
{ "output", no_argument, NULL, 'o' },
{ "format", no_argument, NULL, 'f' },
{ "count", required_argument, NULL, 'c' },
+ { "request",no_argument, NULL, 'q' },
{ 0, 0, 0, 0 }
};
@@ -624,6 +721,10 @@ int main(int argc, char **argv)
force_format++;
break;
+ case 'q':
+ use_request = true;
+ break;
+
case 'c':
errno = 0;
frame_count = strtol(optarg, NULL, 0);
@@ -639,9 +740,26 @@ int main(int argc, char **argv)
open_device();
init_device();
- start_capturing();
- mainloop();
+ start_capturing(0, n_buffers);
+ mainloop(200, true);
stop_capturing();
+
+ if (io == IO_METHOD_MMAP) {
+ if (-1 == s_fmt(width_alt, height_alt, fourcc_alt, colorspace_alt))
+ errno_exit("S_FMT(alternate)");
+
+ start_capturing(n_buffers, n_buffers_alt);
+ mainloop(n_buffers_alt, false);
+ stop_capturing();
+
+ if (-1 == s_fmt(width, height, fourcc, colorspace))
+ errno_exit("S_FMT(main)");
+
+ start_capturing(0, n_buffers);
+ mainloop(200, true);
+ stop_capturing();
+ }
+
uninit_device();
close_device();
fprintf(stderr, "\n");