/**********************************************************************************************************/
//作者:zzhere2007
//时间:2013.08.27
/**********************************************************************************************************
V4L2 编程使用总结
说明:
V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。
流程: 内存映射方式
1.打开设备文件。 int fd=open(”/dev/video0″,O_RDWR);
2.取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
3.设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
VIDIOC_S_STD,VIDIOC_S_FMT,struct v4l2_std_id,struct v4l2_format
4.向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
5.将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。mmap
6.将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
7.开始视频的采集。VIDIOC_STREAMON
8.出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF
9.将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF
10.停止视频的采集。VIDIOC_STREAMOFF
11.关闭视频设备。close(fd);
头文件:V4L2 的相关定义包含在头文件<linux/videodev2.h> 中.
***********************************************************************************************************/
/*关于视频采集方式
操作系统一般把系统使用的内存划分成用户空间和内核空间,分别由应用程序管理和操作系统管理。应用程序可以直接访问内存的地址,而内核空间存放的是供内核访问的代码和数据,用户不能直接访问。v4l2捕获的数据,最初是存放在内核空间的,这意味着用户不能直接访问该段内存,必须通过某些手段来转换地址。
一共有三种视频采集方式:使用read、write方式;内存映射方式和用户指针模式。
read、write方式:在用户空间和内核空间不断拷贝数据,占用了大量用户内存空间,效率不高。
内存映射方式:把设备里的内存映射到应用程序中的内存控件,直接处理设备内存,这是一种有效的方式。本文就是。
首先让驱动申请一个缓存空间(使用命令),然后获取缓存空间的地址,之后再将这个缓存转换到用户按空间的地址和长度,记录在用户空间,直接读取即可
用户指针模式:内存片段由应用程序自己分配。这点需要在v4l2_requestbuffers里将memory字段设置成V4L2_MEMORY_USERPTR。
处理采集数据
V4L2有一个数据缓存,存放req.count数量的缓存数据。数据缓存采用FIFO的方式,当应用程序调用缓存数据时,缓存队列将最先采集到的 视频数据缓存送出,并重新采集一张视频数据。这个过程需要用到两个ioctl命令,VIDIOC_DQBUF和VIDIOC_QBUF:
structv4l2_buffer buf;
memset(&buf,0,sizeof(buf));
buf.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory=V4L2_MEMORY_MMAP;
buf.index=0;
//读取缓存
if(ioctl(cameraFd, VIDIOC_DQBUF, &buf) == -1)
{
return-1;
}
//…………视频处理算法
//重新放入缓存队列
if(ioctl(cameraFd, VIDIOC_QBUF, &buf) == -1) {
return-1;
}
关闭视频设备
使用close函数关闭一个视频设备
close(cameraFd)
还需要使用munmap方法。
*/
/******************************************************************************
**设备的打开和关闭:
*******************************************************************************/
#include <fcntl.h>
int open(const char *device_name, int flags);
#include <unistd.h>
int close(int fd);
//例:
int fd=open(“/dev/video0”,O_RDWR);// 打开设备
close(fd);// 关闭设备
/*****************************************************************************
**查询设备属性: VIDIOC_QUERYCAP 查询设备属性命令
************************************************************************************************************/
struct v4l2_capability
{
__u8 driver[16]; // 驱动名字
__u8 card[32]; // 设备名字
__u8 bus_info[32]; // 设备在系统中的位置
__u32 version; // 驱动版本号
__u32 capabilities; // 设备支持的操作 //常用值: V4L2_CAP_VIDEO_CAPTURE // 是否支持图像获取
__u32 reserved[4]; // 保留字段
};
int ioctl(int fd, int request, struct v4l2_capability *argp);
//例:获取并显示设备信息
struct v4l2_capability cap;
ioctl(fd,VIDIOC_QUERYCAP,&cap);
printf(“Driver
Name:%s/nCard
Name:%s/nBus
info:%s/nDriver
Version:%u.%u.%u/n”,
cap.driver,cap.card,cap.bus_info,(cap.version>>16)&0XFF, (cap.version>>8)&0XFF,cap.version&OXFF);
/****************************************************************************************************************
**查询或设置帧格式
** VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式命令
** VIDIOC_G_FMT: 读取当前驱动的频捕获格式命令
** VIDIOC_S_FMT: 设置当前驱动的频捕获格式命令
*****************************************************************************************************************/
// 显示所有支持的格式
struct v4l2_fmtdesc
{
__u32 index; // 要查询的格式序号,应用程序设置
enum v4l2_buf_type type; // 帧类型,应用程序设置
__u32 flags; // 是否为压缩格式
__u8 description[32]; // 格式名称
__u32 pixelformat; // 格式
__u32 reserved[4]; // 保留
};
int ioctl(int fd, int request, struct v4l2_fmtdesc *argp);
//例:
struct v4l2_fmtdesc fmtdesc;
fmtdesc.index=0;
fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
printf("Support format:/n");
while(ioctl(fd,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
{
printf("/t%d.%s/n",fmtdesc.index+1,fmtdesc.description);
fmtdesc.index++;
}
/**********************************************************************
** 查看或设置当前帧格式
** VIDIOC_G_FMT, VIDIOC_S_FMT
** 检查是否支持某种格式
** VIDIOC_TRY_FMT
************************************************************************/
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; // 帧格式 V4L2_PIX_FMT_MJPEG
enum v4l2_field field;
__u32 bytesperline;
__u32 sizeimage;
enum v4l2_colorspace colorspace;
__u32 priv;
};
int ioctl(int fd, int request, struct v4l2_format *argp);
//例:显示当前帧的相关信息
struct v4l2_format fmt;
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd,VIDIOC_G_FMT,&fmt);
printf(“Current data format information:/n/twidth:%d/n/theight:%d/n”,fmt.fmt.width,fmt.fmt.height);
//例:检查是否支持某种帧格式
struct v4l2_format fmt;
fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_RGB32;
if(ioctl(fd,VIDIOC_TRY_FMT,&fmt)==-1)
if(errno==EINVAL)
printf(“not support format RGB32!/n”);
/**************************************************************************************************************
** 为视频捕获分配内存:VIDIOC_REQBUFS
***************************************************************************************************************/
struct v4l2_request buffers
{
__u32 count; //缓存数量,也就是说在缓存队列里保持多少张照片
enum v4l2_buf_type type; //数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
enum v4l2_memory memory; // V4L2_MEMORY_MMAP或 V4L2_MEMORY_USERPTR
__u32 reserved[2];
};
//例:为视频捕获分配内存:
structv4l2_requestbuffers req;
if(ioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
return-1;
}
//获取并记录缓存的物理空间
//使用VIDIOC_REQBUFS,我们获取了req.count个缓存,下一步通过调用VIDIOC_QUERYBUF命令来获取这些缓存的地址,
//然后使用mmap函数转换成应用程序中的绝对地址,最后把这段缓存放入缓存队列:
/****************************************************************************************************************
** 使用mmap函数转换成应用程序中的绝对地址,视频数据
*** 获取分配的内存的地址命令:VIDIOC_QUERYBUF
*****************************************************************************************************************/
typedef struct VideoBuffer
{
void*start;
size_t length;
} VideoBuffer;
VideoBuffer* buffers= calloc( req.count, sizeof(*buffers) ); //1、定义用户的缓存空间
structv 4l2_buffer buf; //2、定义问询的数据格式
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; //3、填充问询数据
//读取缓存
if(ioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) //4、获取分配的内存的地址
{
return-1;
}
buffers[numBufs].length= buf.length; //5、将驱动的数据内存地址和长度,转换到用户空间
buffers[numBufs].start= mmap(NULL, buf.length,PROT_READ| PROT_WRITE,MAP_SHARED,fd, buf.m.offset);//转换成相对地址
/**********************************************************************************
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, 确定被映射的内存地址
***********************************************************************************/
if(buffers[numBufs].start== MAP_FAILED) { return-1; }
//放入缓存队列
if(ioctl(fd, VIDIOC_QBUF, &buf) == -1) { return-1; } //6、读取一帧放入缓冲队列
}
/****************************************************************************************************************************
** 启动或停止数据流
** 命令:VIDIOC_STREAMON, VIDIOC_STREAMOFF
*****************************************************************************************************************************/
int ioctl(int fd, int request, const int *argp);
//例:
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd, VIDIOC_STREAMON, &type);
V4L2总结(1)__使用说明经典_zzhere2007
最新推荐文章于 2023-02-15 00:37:34 发布