V4L2是V4L的升级版本,为linux下视频设备程序提供了一套接口规范。包括一套数据结构和底层V4L2驱动接口。
structv4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFS struct v4l2_capability //视频设备的功能,对应命令VIDIOC_QUERYCAP structv4l2_input //视频输入信息,对应命令VIDIOC_ENUMINPUT struct v4l2_standard //视频的制式,比如PAL,NTSC,对应命令VIDIOC_ENUMSTD structv4l2_format //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等 structv4l2_buffer //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF structv4l2_crop //视频信号矩形边框 v4l2_std_id //视频制式
VIDIOC_REQBUFS //分配内存 VIDIOC_QUERYBUF //把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址 VIDIOC_QUERYCAP //查询驱动功能 VIDIOC_ENUM_FMT //获取当前驱动支持的视频格式 VIDIOC_S_FMT //设置当前驱动的频捕获格式 VIDIOC_G_FMT //读取当前驱动的频捕获格式 VIDIOC_TRY_FMT //验证当前驱动的显示格式 VIDIOC_CROPCAP //查询驱动的修剪能力 VIDIOC_S_CROP //设置视频信号的矩形边框 VIDIOC_G_CROP //读取视频信号的矩形边框 VIDIOC_QBUF //把数据从缓存中读取出来 VIDIOC_DQBUF //把数据放回缓存队列 VIDIOC_STREAMON //开始视频显示函数 VIDIOC_STREAMOFF //结束视频显示函数 VIDIOC_QUERYSTD //检查当前视频设备支持的标准,例如PAL或NTSC。
struct v4l2_capability { __u8 driver[16];//驱动名 __u8 card[32];//例如Hauppauge winTV __u8 bus_info[32];//PCI总线信息 __u32 version;//内核版本 __u32 capabilities;//设备能力 __u32 reserved[4]; };
struct v4l2_format { enum v4l2_buf_type type;//本结构的数据类型 };
struct v4l2_pix_format { __u32 width;//宽度 __u32 height;//高度 }
struct v4l2_requestbuffers { __u32 count;//缓存数量 enum v4l2_buf_type type;//数据流类型 }
enum v4l2_memory{ };
int fd = open(Devicename,mode);
Devicename:/dev/video0、/dev/video1 ……
Mode:O_RDWR [| O_NONBLOCK]
如果使用非阻塞模式调用视频设备,则当没有可用的视频数据时,不会阻塞,而立刻返回。
(1)取得设备的capability
struct v4l2_capability capability;
int ret = ioctl(fd, VIDIOC_QUERYCAP, &capability);
看看设备具有什么功能,比如是否具有视频输入特性。
(2)选择视频输入
struct v4l2_input input;
……初始化input
int ret = ioctl(fd, VIDIOC_QUERYCAP, &input);
一个视频设备可以有多个视频输入。如果只有一路输入,这个功能可以没有。
(3)检测视频支持的制式
v4l2_std_id std;
do {
ret = ioctl(fd, VIDIOC_QUERYSTD, &std);
} while (ret == -1 && errno == EAGAIN);
switch (std) {
case V4L2_STD_NTSC:
//……
case V4L2_STD_PAL:
//……
}
(4)设置视频捕获格式
struct v4l2_format fmt;
fmt.type = V4L2_BUF_TYPE_VIDEO_OUTPUT;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_UYVY;
fmt.fmt.pix.height = height;
fmt.fmt.pix.width = width;
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
ret = ioctl(fd, VIDIOC_S_FMT, &fmt);
if(ret) {
perror("VIDIOC_S_FMT\n");
close(fd);
return -1;
}
(5)向驱动申请帧缓存
struct v4l2_requestbuffers req;
if (ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
return -1;
}
v4l2_requestbuffers结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。多个缓存可以用于建立FIFO,来提高视频采集的效率。
(6)获取每个缓存的信息,并mmap到用户空间
typedef struct VideoBuffer {
void *start;
size_t length;
} VideoBuffer;
VideoBuffer* buffers = calloc( req.count, sizeof(*buffers) );
struct v4l2_buffer buf;
for (numBufs = 0; numBufs < req.count; numBufs++) {//映射所有的缓存
memset( &buf, 0, sizeof(buf) );
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = numBufs;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {//获取到对应index的缓存信息,此处主要利用length信息及offset信息来完成后面的mmap操作。
return -1;
}
buffers[numBufs].length = buf.length;
// 转换成相对地址
buffers[numBufs].start = mmap(NULL, buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED,
fd, buf.m.offset);
if (buffers[numBufs].start == MAP_FAILED) {
return -1;
}
操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。
一共有三种视频采集方式:
1)使用read、write方式:直接使用read和write函数进行读写。这种方式最简单,但是这种方式会在用户空间和内核空间不断拷贝数据,同时在用户空间和内核空间占用了大量内存,效率不高。
2)内存映射方式(mmap):把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。上面的mmap函数就是使用这种方式。
3)用户指针模式:内存由用户空间的应用程序分配,并把地址传递到内核中的驱动程序,然后由v4l2驱动程序直接将数据填充到用户空间的内存中。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。
第一种方式效率是最低的,后面两种方法都能提高执行的效率,但是对于mmap方式,文档中有这样一句描述 --Remember the buffers are allocated in physical memory, as opposed to virtual memory which can be swapped out to disk. Applications should free the buffers as soon as possible with the munmap () function .(使用mmap方法的时候,buffers相当于是在内核空间中分配的,这种情况下,这些buffer是不能被交换到虚拟内存中,虽然这种方法不怎么影响读写效率,但是它一直占用着内核空间中的内存,当系统的内存有限的时候,如果同时运行有大量的进程,则对系统的整体性能会有一定的影响。)
所以,对于三种视频采集方式的选择,推荐的顺序是userptr、mmap、read-write。当使用 mmap或 userptr方式的时候,有一个环形缓冲队列的概念,这个队列中,有 n个 buffer,驱动程序采集到的视频帧数据,就是存储在每个 buffer中。在每次用VIDIOC_DQBUF取出一个buffer,并且处理完数据后,一定要用VIDIOC_QBUF将这个buffer再次放回到环形缓冲队列中。环形缓冲队列,也使得这两种视频采集方式的效率高于直接read/write。
(1)开始采集
int buf_type= V4L2_BUF_TYPE_VIDEO_CAPTURE;
int ret = ioctl(fd, VIDIOC_STREAMON, &buf_type);
(2)取出FIFO缓存中已经采样的帧缓存
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=0;//此值由下面的ioctl返回
if (ioctl(fd, VIDIOC_DQBUF, &buf) == -1)
{
return -1;
}
根据返回的buf.index找到对应的mmap映射好的缓存,取出视频数据。
(3)将刚刚处理完的缓冲重新入队列尾,这样可以循环采集
if (ioctl(fd, VIDIOC_QBUF, &buf) == -1) {
return -1;
}
int ret = ioctl(fd, VIDIOC_STREAMOFF, &buf_type);
关闭视频设备
close(fd);
/* * V4L2 video capture example * * This program can be used and distributed without restrictions. */ #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 <asm/types.h> /* for videodev2.h */ #include <linux/videodev2.h> #define CLEAR(x) memset (&(x), 0, sizeof (x)) typedef enum { IO_METHOD_READ, IO_METHOD_MMAP, IO_METHOD_USERPTR, } io_method; struct buffer { void * start; size_t length; }; static char * dev_name = NULL; static io_method io = IO_METHOD_MMAP; static int fd = -1; struct buffer * buffers = NULL; static unsigned int n_buffers = 0; 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 fd, int request, void * arg) { int r; do r = ioctl (fd, request, arg); while (-1 == r && EINTR == errno); return r; } static void process_image (const void * p) { fputc ('.', stdout); fflush (stdout); } static int read_frame (void) { struct v4l2_buffer 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); 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); 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); if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) errno_exit ("VIDIOC_QBUF"); break; } return 1; } static void mainloop (void) { unsigned int count; count = 100; 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 v4l2_buf_type 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 v4l2_buf_type type; switch (io) { case IO_METHOD_READ: /* Nothing to do. */ break; case IO_METHOD_MMAP: for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer 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 v4l2_buffer 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 v4l2_requestbuffers 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 v4l2_buffer 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 v4l2_requestbuffers 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 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) { 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; 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. */ /* 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" "Options:\n" "-d | --device name Video device name [/dev/video]\n" "-h | --help Print this message\n" "-m | --mmap Use memory mapped buffers\n" "-r | --read Use read() calls\n" "-u | --userp Use application allocated buffers\n" "", argv[0]); } static const char short_options [] = "d:hmru"; 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' }, { 0, 0, 0, 0 } }; int main (int argc, char ** argv) { dev_name = "/dev/video"; for (;;) { int index; int c; c = getopt_long (argc, argv, short_options, long_options, &index); 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; default: usage (stderr, argc, argv); exit (EXIT_FAILURE); } } open_device (); init_device (); start_capturing (); mainloop (); stop_capturing (); uninit_device (); close_device (); exit (EXIT_SUCCESS); return 0; }
others
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <assert.h> #include <getopt.h> #include <fcntl.h> #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 <asm/types.h> #include <linux/videodev2.h> #include <linux/fb.h> #define CLEAR(x) memset (&(x), 0, sizeof (x)) struct buffer { void * start; size_t length; }; static char * dev_name = NULL; static int fd = -1; struct buffer * buffers = NULL; static unsigned int n_buffers = 0; static int time_in_sec_capture=5; static int fbfd = -1; static struct fb_var_screeninfo vinfo; static struct fb_fix_screeninfo finfo; static char *fbp=NULL; static long screensize=0; 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 fd,int request,void * arg) { int r; do r = ioctl (fd, request, arg); while (-1 == r && EINTR == errno); return r; } inline int clip(int value, int min, int max) { return (value > max ? max : value < min ? min : value); } static void process_image (const void * p){ //ConvertYUVToRGB32 //1; unsigned char* in=(char*)p; int width=640; int height=480; int istride=1280; int x,y,j; int y0,u,y1,v,r,g,b; long location=0; for ( y = 100; y < height + 100; ++y) { for (j = 0, x=100; j < width * 2 ; j += 4,x +=2) { location = (x+vinfo.xoffset) * (vinfo.bits_per_pixel/8) + (y+vinfo.yoffset) * finfo.line_length; y0 = in[j]; u = in[j + 1] - 128; y1 = in[j + 2]; v = in[j + 3] - 128; r = (298 * y0 + 409 * v + 128) >> 8; g = (298 * y0 - 100 * u - 208 * v + 128) >> 8; b = (298 * y0 + 516 * u + 128) >> 8; fbp[ location + 0] = clip(b, 0, 255); fbp[ location + 1] = clip(g, 0, 255); fbp[ location + 2] = clip(r, 0, 255); fbp[ location + 3] = 255; r = (298 * y1 + 409 * v + 128) >> 8; g = (298 * y1 - 100 * u - 208 * v + 128) >> 8; b = (298 * y1 + 516 * u + 128) >> 8; fbp[ location + 4] = clip(b, 0, 255); fbp[ location + 5] = clip(g, 0, 255); fbp[ location + 6] = clip(r, 0, 255); fbp[ location + 7] = 255; } in +=istride; } } static int read_frame (void) { struct v4l2_buffer buf; unsigned int i; 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: default: errno_exit ("VIDIOC_DQBUF"); } } assert (buf.index < n_buffers); printf("v4l2_pix_format->field(%d)/n", buf.field); //assert (buf.field ==V4L2_FIELD_NONE); process_image (buffers[buf.index].start); if (-1 == xioctl (fd, VIDIOC_QBUF, &buf)) errno_exit ("VIDIOC_QBUF"); return 1; } static void run (void) { unsigned int count; int frames; frames = 30 * time_in_sec_capture; while (frames-- > 0) { for (;;) { fd_set fds; struct timeval tv; int r; FD_ZERO (&fds); FD_SET (fd, &fds); 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; } } } static void stop_capturing (void) { enum v4l2_buf_type type; type = V4L2_BUF_TYPE_VIDEO_CAPTURE; if (-1 == xioctl (fd, VIDIOC_STREAMOFF, &type)) errno_exit ("VIDIOC_STREAMOFF"); } static void start_capturing (void) { unsigned int i; enum v4l2_buf_type type; for (i = 0; i < n_buffers; ++i) { struct v4l2_buffer 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"); } static void uninit_device (void) { unsigned int i; for (i = 0; i < n_buffers; ++i) if (-1 == munmap (buffers[i].start, buffers[i].length)) errno_exit ("munmap"); if (-1 == munmap(fbp, screensize)) { printf(" Error: framebuffer device munmap() failed./n"); exit (EXIT_FAILURE) ; } free (buffers); } static void init_mmap (void) { struct v4l2_requestbuffers req; //mmap framebuffer fbp = (char *)mmap(NULL,screensize,PROT_READ | PROT_WRITE,MAP_SHARED ,fbfd, 0); if ((int)fbp == -1) { printf("Error: failed to map framebuffer device to memory./n"); exit (EXIT_FAILURE) ; } memset(fbp, 0, screensize); 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 < 4) { //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 v4l2_buffer 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,buf.length,PROT_READ | PROT_WRITE ,MAP_SHARED,fd, buf.m.offset); if (MAP_FAILED == buffers[n_buffers].start) errno_exit ("mmap"); } } static void init_device (void) { struct v4l2_capability cap; struct v4l2_cropcap cropcap; struct v4l2_crop crop; struct v4l2_format fmt; unsigned int min; // Get fixed screen information if (-1==xioctl(fbfd, FBIOGET_FSCREENINFO, &finfo)) { printf("Error reading fixed information./n"); exit (EXIT_FAILURE); } // Get variable screen information if (-1==xioctl(fbfd, FBIOGET_VSCREENINFO, &vinfo)) { printf("Error reading variable information./n"); exit (EXIT_FAILURE); } screensize = vinfo.xres * vinfo.yres * vinfo.bits_per_pixel / 8; if (-1 == xioctl (fd, VIDIOC_QUERYCAP, ∩)) { 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); } if (!(cap.capabilities & V4L2_CAP_STREAMING)) { fprintf (stderr, "%s does not support streaming i/o/n",dev_name); exit (EXIT_FAILURE); } 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; if (-1 == xioctl (fd, VIDIOC_S_CROP, &crop)) { switch (errno) { case EINVAL: break; default: break; } } }else { } CLEAR (fmt); fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 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"); init_mmap (); } static void close_device (void) { if (-1 == close (fd)) errno_exit ("close"); fd = -1; close(fbfd); } 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); } //open framebuffer fbfd = open("/dev/fb0", O_RDWR); if (fbfd==-1) { printf("Error: cannot open framebuffer device./n"); exit (EXIT_FAILURE); } //open camera fd = open (dev_name, O_RDWR| 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" "Options:/n" "-d | --device name Video device name [/dev/video]/n" "-h | --help Print this message/n" "-t | --how long will display in seconds/n" "", argv[0]); } static const char short_options [] = "d:ht:"; static const struct option long_options [] = { { "device", required_argument, NULL, 'd' }, { "help", no_argument, NULL, 'h' }, { "time", no_argument, NULL, 't' }, { 0, 0, 0, 0 } }; int main (int argc,char ** argv) { dev_name = "/dev/video0"; for (;;) { int index; int c; c = getopt_long (argc, argv,short_options, long_options,&index); if (-1 == c) break; switch (c) { case 0: break; case 'd': dev_name = optarg; break; case 'h': usage (stdout, argc, argv); exit (EXIT_SUCCESS); case 't': time_in_sec_capture = atoi(optarg); break; default: usage (stderr, argc, argv); exit (EXIT_FAILURE); } } open_device (); init_device (); start_capturing (); run (); stop_capturing (); uninit_device (); close_device (); exit (EXIT_SUCCESS); return 0; }
#ifndef _V4L_H #define _V4L_H #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <error.h> #include <assert.h> #include <fcntl.h> #include <sys/ioctl.h> #include <sys/types.h> #include <sys/mman.h> #include <linux/videodev.h> #include <sys/types.h> #include <string.h> /*采集的图像的最大长和宽*/ #define MAX_WIDTH 400 #define MAX_HEIGHT 300 /*设备文件*/ #define DEFAULT_DEVICE "/dev/video0" /*自定义数据结构,包含v4l 中用到的数据结构*/ typedef struct v4l_struct { int fd;/*设备号*/ struct video_capability capability; //包含设备的基本信息(设备名称、支持的最大最小分辨率、信号源信息等) struct video_channel channel[8];//信号源个数 struct video_picture picture;//设备采集的图象的各种属性 struct video_mmap mmap;//用于mmap struct video_mbuf mbuf;//利用mmap进行映射的帧的信息 unsigned char *buffer ;/*图像数据存放区*/ unsigned char *map;/*mmap方式获取数据时,数据的首地址*/ int frame_current; int frame_using[2]; /*这个帧的状态0 表示可用,1表示不可用*/ }v4l_device; /************************************************************** * 函数名:v4l_open * 功 能: 打开设备 * 输 入: dev,vd * 输 出: 无 * 返 回: -1----失败 0----成功 **************************************************************/ int v4l_open( char *dev, v4l_device *vd ) { if( !dev ) { dev=DEFAULT_DEVICE ; } if( ( vd->fd = open( dev, O_RDWR ) ) < 0 ) { perror( "v4l_open error" ); return -1; } return 0; } /************************************************************** * 函数名: v4l_get_capability * 功 能: 获取设备属性 * 输 入: vd * 输 出: 无 * 返 回: -1----失败 0----成功 **************************************************************/ int v4l_get_capability( v4l_device *vd ) { if( ioctl( vd->fd, VIDIOCGCAP, &( vd->capability ) ) <0 ) { perror( "v4l_get_capability" ); return -1 ; } return 0; } /*************************************************************** * 函数名:v4l_get_picture * 功 能:获取图片属性 * 输 入: vd * 输 出: 无 * 返 回: -1----失败 0----成功 ***************************************************************/ int v4l_get_picture( v4l_device *vd ) { if( ioctl( vd->fd,VIDIOCGPICT,&( vd->picture ) ) < 0 ) { return -1; } return 0; } /************************************************************** * 函数名: v4l_set_picture * 功 能: 设置图片属性 * 输 入: vd * 输 出: 无 * 返 回: -1----失败 0----成功 **************************************************************/ int v4l_set_picture( v4l_device *vd ) { if( ioctl( vd->fd, VIDIOCSPICT, &( vd->picture ) ) < 0 ) { return -1; } return 0; } /************************************************************* * 函数名:v4l_get_channels * 功 能:获取通道信息 * 输 入: vd * 输 出: 无 * 返 回: -1----失败 0----成功 *************************************************************/ int v4l_get_channels( v4l_device *vd ) { int i; for( i=0;i < vd->capability.channels ; i++ ) { vd->channel[i].channel = i; //确定通道 if( ioctl( vd->fd , VIDIOCGCHAN, &( vd->channel[i] ) ) <0 ) { perror( "v4l_get_channel" ); return -1; } } return 0; } /************************************************************* * 函数名: v4l_get_mbuf * 功 能: 获取内存映射信息 * 输 入: vd * 输 出: 无 * 返 回: -1----失败 0----成功 **************************************************************/ int v4l_get_mbuf( v4l_device *vd ) { if( ioctl ( vd->fd,VIDIOCGMBUF,&( vd->mbuf ) ) <0 ) { perror( "get_mbuf:" ); return -1; } if ( ( vd->map = ( unsigned char * )mmap( 0, vd->mbuf.size, PROT_READ | PROT_WRITE, MAP_SHARED, vd->fd, 0 ) ) < 0 ) { perror("v4l_mmap_init:mmap"); return -1; } return 0 ; } /************************************************************* * 函数名: v4l_init_mbuff * 功 能: 初始化内存映射信息 * 输 入: vd * 输 出: 无 * 返 回: 0----成功 **************************************************************/ int v4l_init_mbuf(v4l_device *vd) { //vd->mmap.frame = 10 ; //不懂双帧是怎样设置的这个frame 该是当前帧的可mbuf 以又没有设置怎么确定是双帧不是单帧还是更多 vd->mmap.width = MAX_WIDTH; vd->mmap.height = MAX_HEIGHT; vd->mmap.format = vd->picture.palette; vd->frame_current = 0; vd->frame_using[0] = 0; vd->frame_using[1] = 0; return 0; } /************************************************************** * 函数名: v4l_get_address * 功 能: 获取数据在图像的地址 ***************************************************************/ unsigned char *v4l_get_address(v4l_device *vd) { return (vd->map + vd->mbuf.offsets[vd->frame_current]); } /************************************************************* * 函数名: v4l_grab_frame * 功 能: 捕获帧 **************************************************************/ int v4l_grab_frame(v4l_device *vd, int frame) { if (vd->frame_using[frame]) { fprintf(stderr, "v4l_grab_frame: frame %d is already used./n", frame); return -1; } vd->mmap.frame = frame; if ( ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap ) ) < 0 ) { perror( "v4l_grab_frame" ); return -1; } vd->frame_using[frame] = 1; vd->frame_current = frame; return 0; } /************************************************************** * 函数名: v4l_grab_sync * 功 能:与内存映射捕获一致 **************************************************************/ int v4l_grab_sync(v4l_device *vd) { if (ioctl(vd->fd, VIDIOCSYNC, &(vd->frame_current)) < 0) { perror("v4l_grab_sync"); } vd->frame_using[vd->frame_current] = 0; return 0; } /*************************************************************** * 函数名: v4l_munmap * 功 能:停止内存映射 ***************************************************************/ int v4l_munmap( v4l_device *vd ) { if ( munmap( vd->map, vd->mbuf.size ) < 0 ) { perror( "v4lmunmap:munmap" ); return -1; } return 0; } /*************************************************************** * 函数名: v4l_close * 功 能:关闭设备 ***************************************************************/ int v4l_close(v4l_device *vd) { close(vd->fd); return 0; } #endif //简单的封装了关于SDL的相关操作"ScreenSurface.h" #ifndef SCREEN_SURFACE_H #define SCREEN_SURFACE_H #include <stdio.h> #include <stdlib.h> #include <SDL/SDL.h> class ScreenSurface { public: ScreenSurface(); bool screen_init(int w, int h, int b = 0, Uint32 f = 0);//初始化 ~ScreenSurface(); SDL_Surface* point() const; int screen_lock(); void screen_unlock(); void screen_quit(); void screen_set_caption( const char *str );//设置标题 bool flip( unsigned char * src) ;//显示 int startTV();//开始采集 private: static int screenNum; int width; int height; int bpp; Uint32 flags; SDL_Surface* pScreen; }; #endif //ScreenSurface.cpp #include "ScreenSurface.h" #include "qt_v4l.h" v4l_device v4l_dev; /************************************************************** * 函数名: v4l_grab_movie * 功 能:捕获连续图像 **************************************************************/ void v4l_grab_movie() { v4l_grab_frame(&v4l_dev, v4l_dev.frame_current);/*获取下一 帧*/ v4l_grab_sync(&v4l_dev);/*等待传完一 帧*/ v4l_dev.buffer = v4l_get_address(&v4l_dev);/*得到这一帧的地址*/ v4l_dev.frame_current = (v4l_dev.frame_current+1)%2; /* 下一帧的frame*/ } //构造函数。如果创建1个以上的screen surface,则会抛出异常 ScreenSurface::ScreenSurface():width(640), height(480), bpp(32), flags(0) { pScreen = 0; v4l_open(DEFAULT_DEVICE, &v4l_dev);/*打开设备*/ v4l_get_capability(&v4l_dev); v4l_get_picture(&v4l_dev); v4l_init_mbuf(&v4l_dev);/*初始化设备*/ v4l_get_mbuf(&v4l_dev);/*内存映射*/ } bool ScreenSurface::screen_init(int w, int h, int b, Uint32 f) { width = w ; height = h ; bpp = b ; flags = f ; if(SDL_Init(SDL_INIT_VIDEO) < 0) { printf("SDL_Init Failed!/n"); return false; } //设置图象模式(宽*高 位数 标志 SDL_SWSURFACE | SDL_DOUBLEBUF) pScreen = SDL_SetVideoMode(width, height, bpp, flags); if ( pScreen == 0 ) { printf("Could't set display mode /n"); SDL_Quit(); return false; } SDL_ShowCursor(SDL_DISABLE); return true; } //析构函数。在对象消亡时,退出SDL系统。 ScreenSurface::~ScreenSurface() { } //返回screen surface中SDL_Surface结构的指针,主要提供给SDL的函数调用 SDL_Surface* ScreenSurface::point() const { return pScreen; } int ScreenSurface::screen_lock() { if ( SDL_MUSTLOCK(pScreen)) return SDL_LockSurface(pScreen); return 0; } void ScreenSurface::screen_unlock() { if ( SDL_MUSTLOCK(pScreen)) SDL_UnlockSurface(pScreen); } void ScreenSurface::screen_quit() { SDL_Quit(); v4l_munmap(&v4l_dev) ; v4l_close(&v4l_dev); } void ScreenSurface::screen_set_caption( const char *str ) { SDL_WM_SetCaption( str, 0 ); } //显示(弹出flip)screen surface到屏幕上 bool ScreenSurface::flip( unsigned char * src ) { if ( screen_lock() < 0) return false; unsigned char *dest; dest = ( unsigned char * )pScreen->pixels; memcpy( dest , src , width * height * 4 ); screen_unlock(); if ( SDL_Flip(pScreen) < 0 ) return false; else return true; } int ScreenSurface::startTV() { bool bFlag = true; while(bFlag) { v4l_grab_movie(); unsigned char *buf= v4l_dev.buffer; if (buf != NULL) { flip(buf); } SDL_Event event; while(SDL_PollEvent(event)) { if (event.type == SDL_QUIT) { //bFlag = false; screen_quit(); } } } } //main.cpp #include "ScreenSurface.h" void main() { ScreenSurface *m_pScreen; m_pScreen = new ScreenSurface( ); m_pScreen->screen_init(400 , 300 , 32 , SDL_SWSURFACE | SDL_ANYFORMAT); m_pScreen->screen_set_caption("DemoTV"); m_pScreen->startTV(); }