一、简介
- 一般采集过程:打开视频设备 → 设定属性(裁剪、缩放)→ 设定采集方式 → 开始采集,并处理采集数据(循环) → 关闭视频设备
- 头文件:<linux/videodev2.h>
二、查询设备属性:VIDIOC_QUERYCAP
- 相关结构体
struct v4l2_capability{
u8 driver[16]; //驱动名
u8 card[32]; //设备名
u8 bus_info[32]; //设备在系统中位置
u32 version; //驱动版本号
u32 capabilities; //设备支持操作
u32 reserved[4]; //保留字段
}
- 原型:int ioctl(int fd,int request,struct v4l2_capability *argp);
- ioctl(fd,VIDIOC_QUERYCAP,&CAP); //显示设备信息
- capabilities常用:V4L2_CAP_VIDEO_CAPTURE //是否支持图像获取
三、查询并显示所有支持格式:VIDIOC_ENUM_FMT
- 原型:int ioctl(int fd,int request,struct v4l2_fmtdesc* argp)
- 相关结构体
struct v4l2_fmtdesc{
u32 index; //要查询的格式序号,应用程序设置
enum v4l2_buf_type type; //帧类型,应用程序设置
u32 flags; //是否为压缩格式
u8 description[32]; //格式名称
u32 pixelformat; //格式
u32 reserved[4]; //保留
}
四、显示(设置)当前帧的相关信息
- 相关结构体
struct v4l2_format{
enum v4l2_buf_type type; //帧类型,应用程序设置,一般为V4L2_BUF_TYPE_VIDEO_CAPTURE
union fmt{
struct v4l2_pix_format pix; //视频设备使用
struct v4l2_window win;
struct v4l2_vbi_format vbi;
struct v4l2_sliced_vbi_format sliced;
u8 raw_data[200];
};
}
struct v4l2_pix_format{
u32 width; //帧宽,单位是像素
u32 height; //帧高,单位是像素
u32 pixelformat; //帧格式
enum v4l2_field field;
u32 bytesperline;
u32 sizeimage;
enum v4l2_colorspace colorspace;
u32 priv;
};
- 显示当前帧信息:ioctl(fd,VIDIOC_G_FMT,&fmt) 【struct v4l2_format fmt;】
- 设置当前帧信息:ioctl(fd,VIDIOC_S_FMT,&fmt)
五、视频输入输出
- request:VIDIOC_G_INPUT 和 VIDIOC_S_INPUT 。一个video设备节点可能对应多个视频源,通过S进行切换,通过G查询当前输入输出的index。
- 可通过VIDIOC_ENUMINPUT进行列举
- 相关结构体
struct v4l2_input{
_u32 index; //which input
_u8 name[32]; //label
_u32 type; //type of input
_u32 audioset; //associated audios
_u32 tuner; //associated tuner
v4l2_std_id std;
_u32 status;
_u32 reserved[4];
};
六、Video standards
- 存在如NTSC 和 PAL 等多种视频标准,查询支持或当前标准
- 相关结构体
typedef u64 v4l2_std_id; //64位数表示的标准
struct v4l2_standard{
u32 index;
v4l2_std_id id;
u8 name[24];
struct v4l2_fract frameperiod; //frame,not fields
u32 framelines;
u32 reserved[4];
};
七、申请、管理缓冲区
1、向设备申请缓冲区VIDIOC_REQBUFS
//相关结构体,该结构体在应用时候都要手动设置
struct v4l2_requsetbuffers{
U32 count; //缓冲区内缓冲帧数
enum v4l2_buf_type type; //缓冲帧格式
enum v4l2_memory memory; //区别内存映射还是用户指针方式
U32 reserved[2];
}
enum v4l2_memory{
V4L2_MEMORY_MMAP,V4L2_MEMORY_USERPTR
}
//例,申请4个缓冲帧的缓冲区
struct v4l2_requestbuffers req;
req.count=4;
req.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory=V4L2_MEMORY_MMAP;
ioctl(fd,VIDIOC_REQBUFS,&req);
2、获取缓冲帧地址、长度:VIDIOC_QUERYBUF
- 原型:int ioctl(int fd,int request,struct v4l2_buffer *argp);
- 相关结构体
struct v4l2_buffer{
U32 index; //buffer序号
enum v4l2_buf_type;
U32 byteused; //已用帧数
U32 flags; //MMAP or USERPTR
enum v4l2_field field;
struct timeval tiemstamp; //获取第一个字节的系统时间
struct v4l2_timecode timecode;
enum v4l2_memory memory;
union m{
U32 offset; //只对MMAP有效,缓冲帧地址
unsigned long userptr;
}
U32 length;//缓冲帧长度
U32 input;
U32 reserved;
}
3、内存映射(MMAP)和定义一个结构来映射每个缓冲帧
- 头文件:#include<sys/mman.h>中的一些函数
void * mmap(void* addr,size_t length,int prot, int flags, int fd,off_t offset)
/*
addr:映射起始地址,一般填NULL让内核自动选择
length:被映射的内存长度
prot:标志映射后能否被读写,值有:PROT_EXEC,PROT_READ,PROT_WRITE,PROT_NONE
flags:确定次内存映射是否能被其他进程共享,有:MAP_SHARED,MAP_PRIVATE
fd,offset:确定被映射的内存地址返回成功映射后的地址
*/
int munmap(void* addr,size_t length);
//断开映射,addr为地址,length为映射后内存长度
- 自己定义的结构体
struct buffer{
void* start;
unsigned int length;
}*buffers;
- 例:将4个已申请的缓冲帧映射到应用程序,用buffer指针记录
buffer=(buffer*)calloc(req.count,sizeof(*buffers));
for(unsigned int n_buffers=0;n_buffers<req.cout;n_buffers++){
struct v4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.index=n_buffers;
buf.memory=V4L2_MEMORY_MMAP;
//查询序号为n_buffers的缓冲区,得到起始物理地址和大小
if(ioctl(fd,VIDIOC_QUERYBUF,&buf))
exit(-1);
//映射内存
buffers[n_buffers].start=mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);
}
八、获取数据
1、启动/停止数据流
VIDIOC_STREAMON 和 VIDIOC_STREAMOFF
int ioctl(fd,~,&V4L2_BUF_VIDEO_CAPTURE)
2、把帧放入队列和从队列中取出帧
VIDIOC_QBUF VIDIOC_DQBUF
3、获取一帧并且处理
struct v4l2_buffer;
clear(buf);
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
while(1){
ioctl(fd,VIDIOC_DQBUF,&buf)
process(buffer[buf.index].start)
}