hkurj · 2020年05月11日

如何使用v4l2 API读取摄像头

注:在初始化时拔插摄像头可能会导致该usb口无法识别到video

1. v4l2 简介

V4L2是Video For Linux的第二个版本,它是Linux的视频捕获的API。在这里,您可以找到有关的文档。它提供了很方便的c,c++和python接口

2. 使用方法

按以下顺序即可成功读取图像

2.1 打开设备

首先,你应该先打开设备,在Linux下,通常是在/dev/video*

int fd;
fd = open("/dev/video0", O_RDWR);
if (fd == -1)
{
    // couldn't find capture device
    perror("Opening Video device");
    return 1;
}

2.2 查询设备信息

基本上需要先查询当前是否可用,有些camera V4l2可能不支持。我们需要使用v4l2_capability 结构和VIDIOC_QUERYCAP 来查询摄像头。在这里阅读更多相关内容

struct v4l2_capability caps = {0};
if (-1 == xioctl(fd, VIDIOC_QUERYCAP, &caps))
{
    perror("Querying Capabilites");
    return 1;
}

有可能你没有见过xioctl,xioctl是ioctl的封装,在这里阅读更多相关内容

2.3 设置图像格式

你可以使用v4l2_format 结构来设置图像格式

struct v4l2_format fmt = {0};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = 320;
fmt.fmt.pix.height = 240;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_MJPEG;
fmt.fmt.pix.field = V4L2_FIELD_NONE;

if (-1 == xioctl(fd, VIDIOC_S_FMT, &fmt))
{
    perror("Setting Pixel Format");
    return 1;
}

2.4 请求缓存

驱动缓存包含应用和驱动交互的io流,使用v4l2_requestbuffers 请求缓存,在这里阅读更多相关内容

struct v4l2_requestbuffers req = {0};
req.count = 1;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;

if (-1 == xioctl(fd, VIDIOC_REQBUFS, &req))
{
    perror("Requesting Buffer");
    return 1;
}

2.5 查询缓存

在请求缓存后,我们需要获取缓存地址来获得原始数据,在这里阅读更多相关内容

struct v4l2_buffer buf = {0};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = bufferindex;
if(-1 == xioctl(fd, VIDIOC_QUERYBUF, &buf))
{
    perror("Querying Buffer");
    return 1;
}

buffer = mmap (NULL, buf.length, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.m.offset);

mmap函数要求将以fd指定的设备内存中的偏移量开始的长度字节映射到应用程序地址空间,最好是在地址开始处。在这里阅读更多相关内容

2.6 捕捉图像

在上述准备都完成后,我们最后就可以捕捉到图像了

if(-1 == xioctl(fd, VIDIOC_STREAMON, &buf.type))
{
    perror("Start Capture");
    return 1;
}

fd_set fds;
FD_ZERO(&fds);
FD_SET(fd, &fds);
struct timeval tv = {0};
tv.tv_sec = 2;
int r = select(fd+1, &fds, NULL, NULL, &tv);
if(-1 == r)
{
    perror("Waiting for Frame");
    return 1;
}

if(-1 == xioctl(fd, VIDIOC_DQBUF, &buf))
{
    perror("Retrieving Frame");
    return 1;
}

2.7 如需使用opencv,转换为opencv数据类型

CvMat cvmat = cvMat(480, 640, CV_8UC3, (void*)buffer);
IplImage * img;
img = cvDecodeImage(&cvmat, 1);

3 完整代码

链接

推荐阅读
关注数
0
文章数
1
目录
极术微信服务号
关注极术微信号
实时接收点赞提醒和评论通知
安谋科技学堂公众号
关注安谋科技学堂
实时获取安谋科技及 Arm 教学资源
安谋科技招聘公众号
关注安谋科技招聘
实时获取安谋科技中国职位信息