直接贴代码:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <sys/ioctl.h>
#include <poll.h>
#include <linux/videodev2.h>
struct map_info {
int index;
void *addr;
int length;
int offset;
};
#define DEV_NAME "/dev/video0"
#define NUM_BUFS 4
static struct map_info minfo[NUM_BUFS];
int main(void) {
int fd,i,ret;
fd = open(DEV_NAME, O_RDWR | O_NONBLOCK, 0);
if (fd < 0) {
printf("open falied\n");
return -1;
}
printf("open %s fd = %d\n", DEV_NAME, fd);
//1.查询设备功能
struct v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0){
printf("VIDIOC_QUERYCAP failed\n");
return -1;
}
if(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
printf("dev support capture\n");
//2.枚举支持的像素格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmtdesc.index = 0;
while (!ioctl(fd, VIDIOC_ENUM_FMT, &fmtdesc))
{
printf("fmt:%s\n", fmtdesc.description);
fmtdesc.index++;
}
//3.设置像素格式
struct v4l2_format v4l2_fmt;
memset(&v4l2_fmt, 0, sizeof(struct v4l2_format));
v4l2_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_fmt.fmt.pix.width = 240; //宽度
v4l2_fmt.fmt.pix.height = 240; //高度
v4l2_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB565; //像素格式
v4l2_fmt.fmt.pix.field = V4L2_FIELD_ANY;
if (ioctl(fd, VIDIOC_S_FMT, &v4l2_fmt) < 0)
{
printf("VIDIOC_S_FMT failed\n");
return -1;
}
//4.申请缓存
struct v4l2_requestbuffers req;
req.count = NUM_BUFS;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0)
{
printf("VIDIOC_REQBUFS failed\n");
return -1;
}
//5.查询缓存信息
struct v4l2_buffer v4l2_buf;
for(i=0;i<NUM_BUFS;i++) {
memset(&v4l2_buf, 0, sizeof(struct v4l2_buffer));
v4l2_buf.index = i; //想要查询的缓存
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_QUERYBUF, &v4l2_buf);
if(ret < 0)
{
printf("Unable to query buffer.\n");
return -1;
}
//6.映射
void *addr;
addr = mmap(NULL,
v4l2_buf.length, PROT_READ | PROT_WRITE, MAP_SHARED,
fd, v4l2_buf.m.offset);
if(addr < 0) {
printf("map %d v4l2_buffer error!!!\n",i);
close(fd);
return -1;
}
minfo[i].index = i;
minfo[i].addr = addr;
minfo[i].length = v4l2_buf.length;
minfo[i].offset = v4l2_buf.m.offset;
}
//7.将所有的缓存放入队列
for(i = 0; i < NUM_BUFS; i++)
{
memset(&v4l2_buf, 0, sizeof(struct v4l2_buffer));
v4l2_buf.index = i; //想要放入队列的缓存
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_QBUF, &v4l2_buf);
if(ret < 0)
{
printf("Unable to queue buffer.\n");
close(fd);
return -1;
}
}
//8.开始捕获
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ret = ioctl(fd, VIDIOC_STREAMON, &type);
if (ret < 0){
printf("VIDIOC_STREAMON failed\n");
return -1;
}
//9.等待数据的到来
struct pollfd poll_fds[1];
poll_fds[0].fd = fd;
poll_fds[0].events = POLLIN; //等待可读
poll(poll_fds, 1, 10000);
//10.出队列
memset(&v4l2_buf, 0, sizeof(struct v4l2_buffer));
v4l2_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
v4l2_buf.memory = V4L2_MEMORY_MMAP;
ret = ioctl(fd, VIDIOC_DQBUF, &v4l2_buf);
if (ret < 0)
{
printf("VIDIOC_DQBUF failed, dropped frame\n");
return -1;
}
//11.处理数据
FILE *fp;
fp = fopen("test.data", "w");
if(!fp)
{
printf("failed to open picture\n");
} else {
fwrite(minfo[v4l2_buf.index].addr, 1, minfo[v4l2_buf.index].length, fp);
fclose(fp);
}
//12.入队列
if (ioctl(fd, VIDIOC_QBUF, &v4l2_buf) < 0) {
printf("VIDIOC_QBUF failed\n");
return -1;
}
//13.关闭设备
ret = ioctl(fd, VIDIOC_STREAMOFF, &type);
if (ret < 0){
printf("VIDIOC_STREAMOFF failed\n");
return -1;
}
//14.取消映射
for(i = 0; i < NUM_BUFS; i++)
munmap(minfo[i].addr, minfo[i].length);
close(fd);
return 0;
}
$ gcc test.c -o test
$./test
生成test.data,是RGB565的原始数据。
在ubuntu下可以用gimg看图像的原始数据。
$ sudo apt-get install gimp
$ gimp test.data
设置好格式和宽高,显示效果如下: