mirror of
https://github.com/torvalds/linux.git
synced 2024-12-25 04:11:49 +00:00
991ce92f8d
While https was always supported on linuxtv.org, only in Dec 3 2015 the website is using valid certificates. As we're planning to drop pure http support on some future, change all references at the media subsystem to point to the https URL instead. Signed-off-by: Mauro Carvalho Chehab <mchehab@osg.samsung.com>
660 lines
19 KiB
XML
660 lines
19 KiB
XML
<programlisting>
|
|
/*
|
|
* V4L2 video capture example
|
|
*
|
|
* This program can be used and distributed without restrictions.
|
|
*
|
|
* This program is provided with the V4L2 API
|
|
* see https://linuxtv.org/docs.php for more information
|
|
*/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include <getopt.h> /* getopt_long() */
|
|
|
|
#include <fcntl.h> /* low-level i/o */
|
|
#include <unistd.h>
|
|
#include <errno.h>
|
|
#include <sys/stat.h>
|
|
#include <sys/types.h>
|
|
#include <sys/time.h>
|
|
#include <sys/mman.h>
|
|
#include <sys/ioctl.h>
|
|
|
|
#include <linux/videodev2.h>
|
|
|
|
#define CLEAR(x) memset(&(x), 0, sizeof(x))
|
|
|
|
enum io_method {
|
|
IO_METHOD_READ,
|
|
IO_METHOD_MMAP,
|
|
IO_METHOD_USERPTR,
|
|
};
|
|
|
|
struct buffer {
|
|
void *start;
|
|
size_t length;
|
|
};
|
|
|
|
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 int out_buf;
|
|
static int force_format;
|
|
static int frame_count = 70;
|
|
|
|
static void errno_exit(const char *s)
|
|
{
|
|
fprintf(stderr, "%s error %d, %s\n", s, errno, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
static int xioctl(int fh, int request, void *arg)
|
|
{
|
|
int r;
|
|
|
|
do {
|
|
r = ioctl(fh, request, arg);
|
|
} while (-1 == r && EINTR == errno);
|
|
|
|
return r;
|
|
}
|
|
|
|
static void process_image(const void *p, int size)
|
|
{
|
|
if (out_buf)
|
|
fwrite(p, size, 1, stdout);
|
|
|
|
fflush(stderr);
|
|
fprintf(stderr, ".");
|
|
fflush(stdout);
|
|
}
|
|
|
|
static int read_frame(void)
|
|
{
|
|
struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;
|
|
unsigned int i;
|
|
|
|
switch (io) {
|
|
case IO_METHOD_READ:
|
|
if (-1 == read(fd, buffers[0].start, buffers[0].length)) {
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
return 0;
|
|
|
|
case EIO:
|
|
/* Could ignore EIO, see spec. */
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
errno_exit("read");
|
|
}
|
|
}
|
|
|
|
process_image(buffers[0].start, buffers[0].length);
|
|
break;
|
|
|
|
case IO_METHOD_MMAP:
|
|
CLEAR(buf);
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
return 0;
|
|
|
|
case EIO:
|
|
/* Could ignore EIO, see spec. */
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
errno_exit("VIDIOC_DQBUF");
|
|
}
|
|
}
|
|
|
|
assert(buf.index < n_buffers);
|
|
|
|
process_image(buffers[buf.index].start, buf.bytesused);
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
|
errno_exit("VIDIOC_QBUF");
|
|
break;
|
|
|
|
case IO_METHOD_USERPTR:
|
|
CLEAR(buf);
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = V4L2_MEMORY_USERPTR;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_DQBUF, &buf)) {
|
|
switch (errno) {
|
|
case EAGAIN:
|
|
return 0;
|
|
|
|
case EIO:
|
|
/* Could ignore EIO, see spec. */
|
|
|
|
/* fall through */
|
|
|
|
default:
|
|
errno_exit("VIDIOC_DQBUF");
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < n_buffers; ++i)
|
|
if (buf.m.userptr == (unsigned long)buffers[i].start
|
|
&& buf.length == buffers[i].length)
|
|
break;
|
|
|
|
assert(i < n_buffers);
|
|
|
|
process_image((void *)buf.m.userptr, buf.bytesused);
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
|
errno_exit("VIDIOC_QBUF");
|
|
break;
|
|
}
|
|
|
|
return 1;
|
|
}
|
|
|
|
static void mainloop(void)
|
|
{
|
|
unsigned int count;
|
|
|
|
count = frame_count;
|
|
|
|
while (count-- > 0) {
|
|
for (;;) {
|
|
fd_set fds;
|
|
struct timeval tv;
|
|
int r;
|
|
|
|
FD_ZERO(&fds);
|
|
FD_SET(fd, &fds);
|
|
|
|
/* Timeout. */
|
|
tv.tv_sec = 2;
|
|
tv.tv_usec = 0;
|
|
|
|
r = select(fd + 1, &fds, NULL, NULL, &tv);
|
|
|
|
if (-1 == r) {
|
|
if (EINTR == errno)
|
|
continue;
|
|
errno_exit("select");
|
|
}
|
|
|
|
if (0 == r) {
|
|
fprintf(stderr, "select timeout\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (read_frame())
|
|
break;
|
|
/* EAGAIN - continue select loop. */
|
|
}
|
|
}
|
|
}
|
|
|
|
static void stop_capturing(void)
|
|
{
|
|
enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;
|
|
|
|
switch (io) {
|
|
case IO_METHOD_READ:
|
|
/* Nothing to do. */
|
|
break;
|
|
|
|
case IO_METHOD_MMAP:
|
|
case IO_METHOD_USERPTR:
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
if (-1 == xioctl(fd, VIDIOC_STREAMOFF, &type))
|
|
errno_exit("VIDIOC_STREAMOFF");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void start_capturing(void)
|
|
{
|
|
unsigned int i;
|
|
enum <link linkend="v4l2-buf-type">v4l2_buf_type</link> type;
|
|
|
|
switch (io) {
|
|
case IO_METHOD_READ:
|
|
/* Nothing to do. */
|
|
break;
|
|
|
|
case IO_METHOD_MMAP:
|
|
for (i = 0; i < n_buffers; ++i) {
|
|
struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;
|
|
|
|
CLEAR(buf);
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
buf.index = i;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
|
errno_exit("VIDIOC_QBUF");
|
|
}
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
|
|
errno_exit("VIDIOC_STREAMON");
|
|
break;
|
|
|
|
case IO_METHOD_USERPTR:
|
|
for (i = 0; i < n_buffers; ++i) {
|
|
struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;
|
|
|
|
CLEAR(buf);
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = V4L2_MEMORY_USERPTR;
|
|
buf.index = i;
|
|
buf.m.userptr = (unsigned long)buffers[i].start;
|
|
buf.length = buffers[i].length;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QBUF, &buf))
|
|
errno_exit("VIDIOC_QBUF");
|
|
}
|
|
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
if (-1 == xioctl(fd, VIDIOC_STREAMON, &type))
|
|
errno_exit("VIDIOC_STREAMON");
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void uninit_device(void)
|
|
{
|
|
unsigned int i;
|
|
|
|
switch (io) {
|
|
case IO_METHOD_READ:
|
|
free(buffers[0].start);
|
|
break;
|
|
|
|
case IO_METHOD_MMAP:
|
|
for (i = 0; i < n_buffers; ++i)
|
|
if (-1 == munmap(buffers[i].start, buffers[i].length))
|
|
errno_exit("munmap");
|
|
break;
|
|
|
|
case IO_METHOD_USERPTR:
|
|
for (i = 0; i < n_buffers; ++i)
|
|
free(buffers[i].start);
|
|
break;
|
|
}
|
|
|
|
free(buffers);
|
|
}
|
|
|
|
static void init_read(unsigned int buffer_size)
|
|
{
|
|
buffers = calloc(1, sizeof(*buffers));
|
|
|
|
if (!buffers) {
|
|
fprintf(stderr, "Out of memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
buffers[0].length = buffer_size;
|
|
buffers[0].start = malloc(buffer_size);
|
|
|
|
if (!buffers[0].start) {
|
|
fprintf(stderr, "Out of memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void init_mmap(void)
|
|
{
|
|
struct <link linkend="v4l2-requestbuffers">v4l2_requestbuffers</link> req;
|
|
|
|
CLEAR(req);
|
|
|
|
req.count = 4;
|
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
req.memory = V4L2_MEMORY_MMAP;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
|
|
if (EINVAL == errno) {
|
|
fprintf(stderr, "%s does not support "
|
|
"memory mapping\n", dev_name);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
errno_exit("VIDIOC_REQBUFS");
|
|
}
|
|
}
|
|
|
|
if (req.count < 2) {
|
|
fprintf(stderr, "Insufficient buffer memory on %s\n",
|
|
dev_name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
buffers = calloc(req.count, sizeof(*buffers));
|
|
|
|
if (!buffers) {
|
|
fprintf(stderr, "Out of memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
|
|
struct <link linkend="v4l2-buffer">v4l2_buffer</link> buf;
|
|
|
|
CLEAR(buf);
|
|
|
|
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
buf.memory = V4L2_MEMORY_MMAP;
|
|
buf.index = n_buffers;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
|
|
errno_exit("VIDIOC_QUERYBUF");
|
|
|
|
buffers[n_buffers].length = buf.length;
|
|
buffers[n_buffers].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)
|
|
errno_exit("mmap");
|
|
}
|
|
}
|
|
|
|
static void init_userp(unsigned int buffer_size)
|
|
{
|
|
struct <link linkend="v4l2-requestbuffers">v4l2_requestbuffers</link> req;
|
|
|
|
CLEAR(req);
|
|
|
|
req.count = 4;
|
|
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
req.memory = V4L2_MEMORY_USERPTR;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req)) {
|
|
if (EINVAL == errno) {
|
|
fprintf(stderr, "%s does not support "
|
|
"user pointer i/o\n", dev_name);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
errno_exit("VIDIOC_REQBUFS");
|
|
}
|
|
}
|
|
|
|
buffers = calloc(4, sizeof(*buffers));
|
|
|
|
if (!buffers) {
|
|
fprintf(stderr, "Out of memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
for (n_buffers = 0; n_buffers < 4; ++n_buffers) {
|
|
buffers[n_buffers].length = buffer_size;
|
|
buffers[n_buffers].start = malloc(buffer_size);
|
|
|
|
if (!buffers[n_buffers].start) {
|
|
fprintf(stderr, "Out of memory\n");
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
}
|
|
|
|
static void init_device(void)
|
|
{
|
|
struct <link linkend="v4l2-capability">v4l2_capability</link> cap;
|
|
struct <link linkend="v4l2-cropcap">v4l2_cropcap</link> cropcap;
|
|
struct <link linkend="v4l2-crop">v4l2_crop</link> crop;
|
|
struct <link linkend="v4l2-format">v4l2_format</link> fmt;
|
|
unsigned int min;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &cap)) {
|
|
if (EINVAL == errno) {
|
|
fprintf(stderr, "%s is no V4L2 device\n",
|
|
dev_name);
|
|
exit(EXIT_FAILURE);
|
|
} else {
|
|
errno_exit("VIDIOC_QUERYCAP");
|
|
}
|
|
}
|
|
|
|
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)) {
|
|
fprintf(stderr, "%s is no video capture device\n",
|
|
dev_name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
switch (io) {
|
|
case IO_METHOD_READ:
|
|
if (!(cap.capabilities & V4L2_CAP_READWRITE)) {
|
|
fprintf(stderr, "%s does not support read i/o\n",
|
|
dev_name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
|
|
case IO_METHOD_MMAP:
|
|
case IO_METHOD_USERPTR:
|
|
if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
|
|
fprintf(stderr, "%s does not support streaming i/o\n",
|
|
dev_name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
break;
|
|
}
|
|
|
|
|
|
/* Select video input, video standard and tune here. */
|
|
|
|
|
|
CLEAR(cropcap);
|
|
|
|
cropcap.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
|
|
if (0 == xioctl(fd, VIDIOC_CROPCAP, &cropcap)) {
|
|
crop.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
crop.c = cropcap.defrect; /* reset to default */
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_S_CROP, &crop)) {
|
|
switch (errno) {
|
|
case EINVAL:
|
|
/* Cropping not supported. */
|
|
break;
|
|
default:
|
|
/* Errors ignored. */
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
/* Errors ignored. */
|
|
}
|
|
|
|
|
|
CLEAR(fmt);
|
|
|
|
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
|
|
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;
|
|
|
|
if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
|
|
errno_exit("VIDIOC_S_FMT");
|
|
|
|
/* Note VIDIOC_S_FMT may change width and height. */
|
|
} else {
|
|
/* Preserve original settings as set by v4l2-ctl for example */
|
|
if (-1 == xioctl(fd, VIDIOC_G_FMT, &fmt))
|
|
errno_exit("VIDIOC_G_FMT");
|
|
}
|
|
|
|
/* Buggy driver paranoia. */
|
|
min = fmt.fmt.pix.width * 2;
|
|
if (fmt.fmt.pix.bytesperline < min)
|
|
fmt.fmt.pix.bytesperline = min;
|
|
min = fmt.fmt.pix.bytesperline * fmt.fmt.pix.height;
|
|
if (fmt.fmt.pix.sizeimage < min)
|
|
fmt.fmt.pix.sizeimage = min;
|
|
|
|
switch (io) {
|
|
case IO_METHOD_READ:
|
|
init_read(fmt.fmt.pix.sizeimage);
|
|
break;
|
|
|
|
case IO_METHOD_MMAP:
|
|
init_mmap();
|
|
break;
|
|
|
|
case IO_METHOD_USERPTR:
|
|
init_userp(fmt.fmt.pix.sizeimage);
|
|
break;
|
|
}
|
|
}
|
|
|
|
static void close_device(void)
|
|
{
|
|
if (-1 == close(fd))
|
|
errno_exit("close");
|
|
|
|
fd = -1;
|
|
}
|
|
|
|
static void open_device(void)
|
|
{
|
|
struct stat st;
|
|
|
|
if (-1 == stat(dev_name, &st)) {
|
|
fprintf(stderr, "Cannot identify '%s': %d, %s\n",
|
|
dev_name, errno, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
if (!S_ISCHR(st.st_mode)) {
|
|
fprintf(stderr, "%s is no device\n", dev_name);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
fd = open(dev_name, O_RDWR /* required */ | O_NONBLOCK, 0);
|
|
|
|
if (-1 == fd) {
|
|
fprintf(stderr, "Cannot open '%s': %d, %s\n",
|
|
dev_name, errno, strerror(errno));
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
static void usage(FILE *fp, int argc, char **argv)
|
|
{
|
|
fprintf(fp,
|
|
"Usage: %s [options]\n\n"
|
|
"Version 1.3\n"
|
|
"Options:\n"
|
|
"-d | --device name Video device name [%s]\n"
|
|
"-h | --help Print this message\n"
|
|
"-m | --mmap Use memory mapped buffers [default]\n"
|
|
"-r | --read Use read() calls\n"
|
|
"-u | --userp Use application allocated buffers\n"
|
|
"-o | --output Outputs stream to stdout\n"
|
|
"-f | --format Force format to 640x480 YUYV\n"
|
|
"-c | --count Number of frames to grab [%i]\n"
|
|
"",
|
|
argv[0], dev_name, frame_count);
|
|
}
|
|
|
|
static const char short_options[] = "d:hmruofc:";
|
|
|
|
static const struct option
|
|
long_options[] = {
|
|
{ "device", required_argument, NULL, 'd' },
|
|
{ "help", no_argument, NULL, 'h' },
|
|
{ "mmap", no_argument, NULL, 'm' },
|
|
{ "read", no_argument, NULL, 'r' },
|
|
{ "userp", no_argument, NULL, 'u' },
|
|
{ "output", no_argument, NULL, 'o' },
|
|
{ "format", no_argument, NULL, 'f' },
|
|
{ "count", required_argument, NULL, 'c' },
|
|
{ 0, 0, 0, 0 }
|
|
};
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
dev_name = "/dev/video0";
|
|
|
|
for (;;) {
|
|
int idx;
|
|
int c;
|
|
|
|
c = getopt_long(argc, argv,
|
|
short_options, long_options, &idx);
|
|
|
|
if (-1 == c)
|
|
break;
|
|
|
|
switch (c) {
|
|
case 0: /* getopt_long() flag */
|
|
break;
|
|
|
|
case 'd':
|
|
dev_name = optarg;
|
|
break;
|
|
|
|
case 'h':
|
|
usage(stdout, argc, argv);
|
|
exit(EXIT_SUCCESS);
|
|
|
|
case 'm':
|
|
io = IO_METHOD_MMAP;
|
|
break;
|
|
|
|
case 'r':
|
|
io = IO_METHOD_READ;
|
|
break;
|
|
|
|
case 'u':
|
|
io = IO_METHOD_USERPTR;
|
|
break;
|
|
|
|
case 'o':
|
|
out_buf++;
|
|
break;
|
|
|
|
case 'f':
|
|
force_format++;
|
|
break;
|
|
|
|
case 'c':
|
|
errno = 0;
|
|
frame_count = strtol(optarg, NULL, 0);
|
|
if (errno)
|
|
errno_exit(optarg);
|
|
break;
|
|
|
|
default:
|
|
usage(stderr, argc, argv);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
}
|
|
|
|
open_device();
|
|
init_device();
|
|
start_capturing();
|
|
mainloop();
|
|
stop_capturing();
|
|
uninit_device();
|
|
close_device();
|
|
fprintf(stderr, "\n");
|
|
return 0;
|
|
}
|
|
</programlisting>
|