V4L2 Video 学习笔记
使用到的头文件
#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <malloc.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>
打开设备
int device_open(const char* device_name)
{
FUNCTION_NAME
int fd = -1;
fd = open(device_name,O_RDWR|O_NONBLOCK,0);
if (fd)
{
fprintf(stderr,"camera open success~ fd:(%d)\n",fd);
}else{
fprintf(stderr,"camera open faild~ fd:(%d)\n",fd);
}
return fd;
}
查询摄像头支持的功能和支持的格式
struct v4l2_capability cap;
device_ioctl(fd, VIDIOC_QUERYCAP, &cap);
fprintf(stderr, "version:%d device info:%s\n", cap.version,cap.driver);
if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
{
fprintf(stderr, "%s support capture!\n",dev_name);
}
if (cap.capabilities & V4L2_CAP_STREAMING)
{
fprintf(stderr, "%s support steaming capture!\n",dev_name);
}
查询摄像头支持的格式
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
int flag = -1;
while((flag=ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc))!=-1){
fprintf(stderr, "%s fmtdesc %s!\n",dev_name,fmtdesc.description);
fmtdesc.index++;
}
查询摄像头支持的分辨率
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
int flag = -1;
struct v4l2_frmsizeenum supportSizes;
while((flag=ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc))!=-1)
{
supportSizes.pixel_format = fmtdesc.pixelformat;
supportSizes.index = 0;
while((ioctl(fd,VIDIOC_ENUM_FRAMESIZES,&supportSizes))!=-1)
{
fprintf(stderr, "line:%d %sformat: %s!size with:%d height:%d\n",__LINE__,dev_name,fmtdesc.description,supportSizes.discrete.width,supportSizes.discrete.height);
supportSizes.index++;
}
fmtdesc.index++;
}
设置取景范围(可以设置取景范围,设置流类型)
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;
}
}
}
设置分辨率,格式等参数
struct v4l2_format fmt;
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 (device_ioctl(fd,VIDIOC_S_FMT,&fmt) == -1)
{
fprintf(stderr, "dev:%sErrors happens in set format!\n",dev_name);
}
内存映射
void device_buf_mmap(int fd)
{
struct v4l2_requestbuffers req;
CLEAR(req);
req.count = 4;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (device_ioctl(fd,VIDIOC_REQBUFS,&req) == -1)
{
fprintf(stderr, "request buf error!\n");
return;
}
if (buffers == NULL)
{
buffers = calloc(req.count, sizeof(*buffers));
}else{
free(buffers);
}
if(req.count < 2)
{
fprintf(stderr, "Insufficient buffer memory on %s/n", dev_name);
exit(EXIT_FAILURE);
}
fprintf(stderr, "sizeof buffers is:%d\n", sizeof(*buffers));
for (int i = 0; i < req.count; ++i)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (device_ioctl(fd,VIDIOC_QUERYBUF,&buf) != -1)
{
buffers[i].length = buf.length;
buffers[i].start = mmap(NULL,buf.length,PROT_READ | PROT_WRITE,
MAP_SHARED,fd,buf.m.offset);
}
if (buffers[i].start == MAP_FAILED)
{
error_exit("mmap");
}
}
}
视频采集
void startCapture(int fd)
{
FUNCTION_NAME;
for (int i = 0; i < buff_index; i++)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (device_ioctl(fd,VIDIOC_QBUF,&buf) == -1)
{
error_exit("queue buf~");
}
}
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (device_ioctl(fd,VIDIOC_STREAMON,&type) == -1)
{
error_exit("VIDIOC_STREAMON");
}
}
获取视频帧数据(注意这里需要 select fd设备设置超时)
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;
error_exit("select");
}
if(0 == r)
{
fprintf(stderr, "select timeout/n");
exit(EXIT_FAILURE);
}
if (getFrame(fd))
{
break;
}
void startCapture(int fd)
{
FUNCTION_NAME;
for (int i = 0; i < buff_index; i++)
{
struct v4l2_buffer buf;
CLEAR(buf);
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (device_ioctl(fd,VIDIOC_QBUF,&buf) == -1)
{
error_exit("queue buf~");
}
}
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (device_ioctl(fd,VIDIOC_STREAMON,&type) == -1)
{
error_exit("VIDIOC_STREAMON");
}
}
停止采集 释放映射
static void stopCapture(int fd)
{
FUNCTION_NAME;
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(-1 == device_ioctl(fd, VIDIOC_STREAMOFF, &type)){
error_exit("VIDIOC_STREAMOFF");
}
}
static void uninit_device(void)
{
FUNCTION_NAME;
unsigned int i;
for(i = 0; i < n_buffers; ++i)
{
if(-1 == munmap(buffers[i].start, buffers[i].length))
{
error_exit("munmap");
}
}
free(buffers);
}