Path: blob/master/Documentation/DocBook/v4l/capture.c.xml
10821 views
<programlisting>1/*2* V4L2 video capture example3*4* This program can be used and distributed without restrictions.5*6* This program is provided with the V4L2 API7* see http://linuxtv.org/docs.php for more information8*/910#include <stdio.h>11#include <stdlib.h>12#include <string.h>13#include <assert.h>1415#include <getopt.h> /* getopt_long() */1617#include <fcntl.h> /* low-level i/o */18#include <unistd.h>19#include <errno.h>20#include <sys/stat.h>21#include <sys/types.h>22#include <sys/time.h>23#include <sys/mman.h>24#include <sys/ioctl.h>2526#include <linux/videodev2.h>2728#define CLEAR(x) memset(&(x), 0, sizeof(x))2930enum io_method {31IO_METHOD_READ,32IO_METHOD_MMAP,33IO_METHOD_USERPTR,34};3536struct buffer {37void *start;38size_t length;39};4041static char *dev_name;42static enum io_method io = IO_METHOD_MMAP;43static int fd = -1;44struct buffer *buffers;45static unsigned int n_buffers;46static int out_buf;47static int force_format;48static int frame_count = 70;4950static void errno_exit(const char *s)51{52fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));53exit(EXIT_FAILURE);54}5556static int xioctl(int fh, int request, void *arg)57{58int r;5960do {61r = ioctl(fh, request, arg);62} while (-1 == r && EINTR == errno);6364return r;65}6667static void process_image(const void *p, int size)68{69if (out_buf)70fwrite(p, size, 1, stdout);7172fflush(stderr);73fprintf(stderr, ".");74fflush(stdout);75}7677static int read_frame(void)78{79struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;80unsigned int i;8182switch (io) {83case IO_METHOD_READ:84if (-1 == read(fd, buffers[0].start, buffers[0].length)) {85switch (errno) {86case EAGAIN:87return 0;8889case EIO:90/* Could ignore EIO, see spec. */9192/* fall through */9394default:95errno_exit("read");96}97}9899process_image(buffers[0].start, buffers[0].length);100break;101102case IO_METHOD_MMAP:103CLEAR(buf);104105buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;106buf.memory = V4L2_MEMORY_MMAP;107108if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {109switch (errno) {110case EAGAIN:111return 0;112113case EIO:114/* Could ignore EIO, see spec. */115116/* fall through */117118default:119errno_exit("VIDIOC_DQBUF");120}121}122123assert(buf.index < n_buffers);124125process_image(buffers[buf.index].start, buf.bytesused);126127if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))128errno_exit("VIDIOC_QBUF");129break;130131case IO_METHOD_USERPTR:132CLEAR(buf);133134buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;135buf.memory = V4L2_MEMORY_USERPTR;136137if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {138switch (errno) {139case EAGAIN:140return 0;141142case EIO:143/* Could ignore EIO, see spec. */144145/* fall through */146147default:148errno_exit("VIDIOC_DQBUF");149}150}151152for (i = 0; i < n_buffers; ++i)153if (buf.m.userptr == (unsigned long)buffers[i].start154&& buf.length == buffers[i].length)155break;156157assert(i < n_buffers);158159process_image((void *)buf.m.userptr, buf.bytesused);160161if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))162errno_exit("VIDIOC_QBUF");163break;164}165166return 1;167}168169static void mainloop(void)170{171unsigned int count;172173count = frame_count;174175while (count-- > 0) {176for (;;) {177fd_set fds;178struct timeval tv;179int r;180181FD_ZERO(&fds);182FD_SET(fd, &fds);183184/* Timeout. */185tv.tv_sec = 2;186tv.tv_usec = 0;187188r = select(fd + 1, &fds, NULL, NULL, &tv);189190if (-1 == r) {191if (EINTR == errno)192continue;193errno_exit("select");194}195196if (0 == r) {197fprintf(stderr, "select timeout\n");198exit(EXIT_FAILURE);199}200201if (read_frame())202break;203/* EAGAIN - continue select loop. */204}205}206}207208static void stop_capturing(void)209{210enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;211212switch (io) {213case IO_METHOD_READ:214/* Nothing to do. */215break;216217case IO_METHOD_MMAP:218case IO_METHOD_USERPTR:219type = V4L2_BUF_TYPE_VIDEO_CAPTURE;220if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))221errno_exit("VIDIOC_STREAMOFF");222break;223}224}225226static void start_capturing(void)227{228unsigned int i;229enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;230231switch (io) {232case IO_METHOD_READ:233/* Nothing to do. */234break;235236case IO_METHOD_MMAP:237for (i = 0; i < n_buffers; ++i) {238struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;239240CLEAR(buf);241buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;242buf.memory = V4L2_MEMORY_MMAP;243buf.index = i;244245if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))246errno_exit("VIDIOC_QBUF");247}248type = V4L2_BUF_TYPE_VIDEO_CAPTURE;249if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))250errno_exit("VIDIOC_STREAMON");251break;252253case IO_METHOD_USERPTR:254for (i = 0; i < n_buffers; ++i) {255struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;256257CLEAR(buf);258buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;259buf.memory = V4L2_MEMORY_USERPTR;260buf.index = i;261buf.m.userptr = (unsigned long)buffers[i].start;262buf.length = buffers[i].length;263264if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))265errno_exit("VIDIOC_QBUF");266}267type = V4L2_BUF_TYPE_VIDEO_CAPTURE;268if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))269errno_exit("VIDIOC_STREAMON");270break;271}272}273274static void uninit_device(void)275{276unsigned int i;277278switch (io) {279case IO_METHOD_READ:280free(buffers[0].start);281break;282283case IO_METHOD_MMAP:284for (i = 0; i < n_buffers; ++i)285if (-1 == munmap(buffers[i].start, buffers[i].length))286errno_exit("munmap");287break;288289case IO_METHOD_USERPTR:290for (i = 0; i < n_buffers; ++i)291free(buffers[i].start);292break;293}294295free(buffers);296}297298static void init_read(unsigned int buffer_size)299{300buffers = calloc(1, sizeof(*buffers));301302if (!buffers) {303fprintf(stderr, "Out of memory\n");304exit(EXIT_FAILURE);305}306307buffers[0].length = buffer_size;308buffers[0].start = malloc(buffer_size);309310if (!buffers[0].start) {311fprintf(stderr, "Out of memory\n");312exit(EXIT_FAILURE);313}314}315316static void init_mmap(void)317{318struct <link linkend="v4l2-requestbuffers">v4l2_requestbuffers</link> req;319320CLEAR(req);321322req.count = 4;323req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;324req.memory = V4L2_MEMORY_MMAP;325326if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {327if (EINVAL == errno) {328fprintf(stderr, "%s does not support "329"memory mapping\n", dev_name);330exit(EXIT_FAILURE);331} else {332errno_exit("VIDIOC_REQBUFS");333}334}335336if (req.count < 2) {337fprintf(stderr, "Insufficient buffer memory on %s\n",338dev_name);339exit(EXIT_FAILURE);340}341342buffers = calloc(req.count, sizeof(*buffers));343344if (!buffers) {345fprintf(stderr, "Out of memory\n");346exit(EXIT_FAILURE);347}348349for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {350struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;351352CLEAR(buf);353354buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;355buf.memory = V4L2_MEMORY_MMAP;356buf.index = n_buffers;357358if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))359errno_exit("VIDIOC_QUERYBUF");360361buffers[n_buffers].length = buf.length;362buffers[n_buffers].start =363mmap(NULL /* start anywhere */,364buf.length,365PROT_READ | PROT_WRITE /* required */,366MAP_SHARED /* recommended */,367fd, buf.m.offset);368369if (MAP_FAILED == buffers[n_buffers].start)370errno_exit("mmap");371}372}373374static void init_userp(unsigned int buffer_size)375{376struct <link linkend="v4l2-requestbuffers">v4l2_requestbuffers</link> req;377378CLEAR(req);379380req.count = 4;381req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;382req.memory = V4L2_MEMORY_USERPTR;383384if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {385if (EINVAL == errno) {386fprintf(stderr, "%s does not support "387"user pointer i/o\n", dev_name);388exit(EXIT_FAILURE);389} else {390errno_exit("VIDIOC_REQBUFS");391}392}393394buffers = calloc(4, sizeof(*buffers));395396if (!buffers) {397fprintf(stderr, "Out of memory\n");398exit(EXIT_FAILURE);399}400401for (n_buffers = 0; n_buffers < 4; ++n_buffers) {402buffers[n_buffers].length = buffer_size;403buffers[n_buffers].start = malloc(buffer_size);404405if (!buffers[n_buffers].start) {406fprintf(stderr, "Out of memory\n");407exit(EXIT_FAILURE);408}409}410}411412static void init_device(void)413{414struct <link linkend="v4l2-capability">v4l2_capability</link> cap;415struct <link linkend="v4l2-cropcap">v4l2_cropcap</link> cropcap;416struct <link linkend="v4l2-crop">v4l2_crop</link> crop;417struct <link linkend="v4l2-format">v4l2_format</link> fmt;418unsigned int min;419420if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {421if (EINVAL == errno) {422fprintf(stderr, "%s is no V4L2 device\n",423dev_name);424exit(EXIT_FAILURE);425} else {426errno_exit("VIDIOC_QUERYCAP");427}428}429430if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {431fprintf(stderr, "%s is no video capture device\n",432dev_name);433exit(EXIT_FAILURE);434}435436switch (io) {437case IO_METHOD_READ:438if (!(cap.capabilities & V4L2_CAP_READWRITE)) {439fprintf(stderr, "%s does not support read i/o\n",440dev_name);441exit(EXIT_FAILURE);442}443break;444445case IO_METHOD_MMAP:446case IO_METHOD_USERPTR:447if (!(cap.capabilities & V4L2_CAP_STREAMING)) {448fprintf(stderr, "%s does not support streaming i/o\n",449dev_name);450exit(EXIT_FAILURE);451}452break;453}454455456/* Select video input, video standard and tune here. */457458459CLEAR(cropcap);460461cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;462463if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {464crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;465crop.c = cropcap.defrect; /* reset to default */466467if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {468switch (errno) {469case EINVAL:470/* Cropping not supported. */471break;472default:473/* Errors ignored. */474break;475}476}477} else {478/* Errors ignored. */479}480481482CLEAR(fmt);483484fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;485if (force_format) {486fmt.fmt.pix.width = 640;487fmt.fmt.pix.height = 480;488fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;489fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;490491if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))492errno_exit("VIDIOC_S_FMT");493494/* Note VIDIOC_S_FMT may change width and height. */495} else {496/* Preserve original settings as set by v4l2-ctl for example */497if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))498errno_exit("VIDIOC_G_FMT");499}500501/* Buggy driver paranoia. */502min = fmt.fmt.pix.width * 2;503if (fmt.fmt.pix.bytesperline < min)504fmt.fmt.pix.bytesperline = min;505min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;506if (fmt.fmt.pix.sizeimage < min)507fmt.fmt.pix.sizeimage = min;508509switch (io) {510case IO_METHOD_READ:511init_read(fmt.fmt.pix.sizeimage);512break;513514case IO_METHOD_MMAP:515init_mmap();516break;517518case IO_METHOD_USERPTR:519init_userp(fmt.fmt.pix.sizeimage);520break;521}522}523524static void close_device(void)525{526if (-1 == close(fd))527errno_exit("close");528529fd = -1;530}531532static void open_device(void)533{534struct stat st;535536if (-1 == stat(dev_name, &st)) {537fprintf(stderr, "Cannot identify '%s': %d, %s\n",538dev_name, errno, strerror(errno));539exit(EXIT_FAILURE);540}541542if (!S_ISCHR(st.st_mode)) {543fprintf(stderr, "%s is no device\n", dev_name);544exit(EXIT_FAILURE);545}546547fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);548549if (-1 == fd) {550fprintf(stderr, "Cannot open '%s': %d, %s\n",551dev_name, errno, strerror(errno));552exit(EXIT_FAILURE);553}554}555556static void usage(FILE *fp, int argc, char **argv)557{558fprintf(fp,559"Usage: %s [options]\n\n"560"Version 1.3\n"561"Options:\n"562"-d | --device name Video device name [%s]\n"563"-h | --help Print this message\n"564"-m | --mmap Use memory mapped buffers [default]\n"565"-r | --read Use read() calls\n"566"-u | --userp Use application allocated buffers\n"567"-o | --output Outputs stream to stdout\n"568"-f | --format Force format to 640x480 YUYV\n"569"-c | --count Number of frames to grab [%i]\n"570"",571argv[0], dev_name, frame_count);572}573574static const char short_options[] = "d:hmruofc:";575576static const struct option577long_options[] = {578{ "device", required_argument, NULL, 'd' },579{ "help", no_argument, NULL, 'h' },580{ "mmap", no_argument, NULL, 'm' },581{ "read", no_argument, NULL, 'r' },582{ "userp", no_argument, NULL, 'u' },583{ "output", no_argument, NULL, 'o' },584{ "format", no_argument, NULL, 'f' },585{ "count", required_argument, NULL, 'c' },586{ 0, 0, 0, 0 }587};588589int main(int argc, char **argv)590{591dev_name = "/dev/video0";592593for (;;) {594int idx;595int c;596597c = getopt_long(argc, argv,598short_options, long_options, &idx);599600if (-1 == c)601break;602603switch (c) {604case 0: /* getopt_long() flag */605break;606607case 'd':608dev_name = optarg;609break;610611case 'h':612usage(stdout, argc, argv);613exit(EXIT_SUCCESS);614615case 'm':616io = IO_METHOD_MMAP;617break;618619case 'r':620io = IO_METHOD_READ;621break;622623case 'u':624io = IO_METHOD_USERPTR;625break;626627case 'o':628out_buf++;629break;630631case 'f':632force_format++;633break;634635case 'c':636errno = 0;637frame_count = strtol(optarg, NULL, 0);638if (errno)639errno_exit(optarg);640break;641642default:643usage(stderr, argc, argv);644exit(EXIT_FAILURE);645}646}647648open_device();649init_device();650start_capturing();651mainloop();652stop_capturing();653uninit_device();654close_device();655fprintf(stderr, "\n");656return 0;657}658</programlisting>659660661