#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#include
#include
#include
#include
#include
#include
#include //V4L2 的相关定义包含在头文件 中
int cam_fd;
struct v4l2_capability cam_cap;
struct v4l2_fmtdesc fmtdesc;
v4l2_std_id std;
struct v4l2_format fmt;
struct v4l2_requestbuffers
struct v4l2_buffer buf;
req;
struct VideoBuffer{
void *start;
size_t length;
};
struct VideoBuffer *buffers;
const int bpp = 24;
int main(int argc,char **argv)
{
int w = 176;
int h = 144;
int ret;
int numBufs = 0;
cam_fd = open("/dev/video2" ,O_RDWR,0);
if(cam_fd < 0){
printf("can not open driver!! \n");
exit(1);
}
printf("cam_fd = %d\n",cam_fd);
//
VIDIOC_QUERYCAP:查询驱动功能
if( ioctl(cam_fd,VIDIOC_QUERYCAP,&cam_cap )< 0){
printf("cam_cap is fail!! \n");
exit(2);
}
printf("Driver name:%s \ndevice name: %s \n",cam_cap.driver,cam_cap.card);
//检测视频支持的制式,连驱动代码都查了,还是不知道
do{
ret = ioctl(cam_fd, VIDIOC_QUERYSTD, &std);
}while(ret == -1 && errno == EAGAIN);
printf("v4l2_std_id is:%x\n",std);
switch(std){
case V4L2_STD_NTSC:
printf("format is V4L2_STD_NTSC");
break;
case V4L2_STD_PAL:
printf("format is V4L2_STD_PAL");
break;
default:
printf("i can not find!!\n");
}
//设置帧格式
memset(&fmt,0,sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
V4L2_BUF_TYPE_VIDEO_CAPTURE
// 数 据 流 类 型 , 必 须 永 远 是
fmt.fmt.pix.width = 176;
fmt.fmt.pix.height = 144;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV; //要与摄像头相对应, 视频数据存储
类型,例如是 YUV4:2:2 还是 RGB
fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
if(ioctl(cam_fd, VIDIOC_S_FMT,&fmt) < 0){
printf("set format is fail!! \n");
exit(4);
}
if(ioctl(cam_fd,VIDIOC_TRY_FMT,&fmt) <0){
printf("not support format V4L2_PIX_FMT_YUYV!\n");
}
//接下来可以为视频捕获分配内存(向驱动申请帧缓存,为 mmap 映射做准备。),使用
VIDIOC_REQBUFS,我们获取了 req.count 个缓存
//v4l2_requestbuffers 结构中定义了缓存的数量,驱动会据此申请对应数量的视频缓存。
多个缓存可以用于建立 FIFO,来提高视频采集的效率。
req.count = 2;
就是说在缓存队列里保持多少张照片
// 缓存数量,也
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// 数 据 流 类 型 , 必 须 永 远 是
// V4L2_MEMORY_MMAP 或
V4L2_BUF_TYPE_VIDEO_CAPTURE
req.memory = V4L2_MEMORY_MMAP;
V4L2_MEMORY_USERPTR,与后面的读取方式有关
if(ioctl(cam_fd,VIDIOC_REQBUFS,&req) ==-1){
printf("VIDIOC_REQUFS is fail!! \n");
exit(5);
}
//printf("req.count is %d\n",req.count);
//申请缓冲区个数:2
//获取并记录缓存的物理空间,调用 VIDIOC_QUERYBUF 命令来获取这些缓存的地址
buffers = (struct VideoBuffer *)calloc(req.count,sizeof(*buffers));
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;
//要获取内核视频缓冲区的信息编号
//目的只为了获取 buf.length 和 buf.m.offset 进行映射
if(ioctl(cam_fd, VIDIOC_QUERYBUF,&buf) < 0){
printf("VIDIOC_QUERYBUF is fail!! \n");
exit(6);
}
buffers[numBufs].length = buf.length;
buffers[numBufs].start=
mmap(NULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,cam_fd,buf.m.offset);
if(buffers[numBufs].start == MAP_FAILED){
printf("MMAP is fail!! \n");
exit(7);
}
printf("Buffer size:%d -------Offset:%d\n",buf.length,buf.m.offset);
//放入缓存队列
if(ioctl(cam_fd,VIDIOC_QBUF,&buf) < 0){
printf("VIDIOC_QBUF is fail!! \n");
exit(8);
}
}
/*以上步骤完成了视频采集的准备工作,但驱动还没有启动采集过程,
应用程序需要调用 VIDIOC_STREAMON ioctl 系统调用驱动才会开始采集数据。
enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl (fd, VIDIOC_STREAMON, &type);*/
//开始采集视频
int buf_type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if(ioctl(cam_fd,VIDIOC_STREAMON,&buf_type) < 0){
printf("VIDIOC_STREAMON is fail!! \n");
exit(9);
}
/*采集过程开始以后,驱动会不停地将数据写入分配的缓冲区内,
当一个缓冲区的数据准备就绪后,驱动就会将其放入输出队列,等待应用程序的处理。
当所有的缓冲区都进入输出队列后,驱动将停止采集,并等待缓冲区重新放入采集队列。
读取数据时,首先需要将一个缓冲区出队列:
struct v4l2_buffer buf;
ioctl (fd, VIDIOC_DQBUF, &buf);*/
//取出 FIFO 缓存中已经采样的帧缓存,
struct v4l2_buffer buf_2;
memset(&buf_2,0,sizeof(buf_2));
buf_2.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf_2.memory = V4L2_MEMORY_MMAP;
//buf.index = 0; 由 VIDIOC_DQBUF 返回
if(ioctl(cam_fd,VIDIOC_DQBUF,&buf_2) < 0){
printf("VIDIOC_DQBUF is fail!! \n");
exit(10);
}
printf("buf.index =%d \n",buf_2.index);
/*驱动会从输出队列取出一个缓冲区,并将其序号赋值给 buf.index,
应用程序可以通过 buffers[buf.index].start 来访问缓冲区的数据。
当处理完成后,需要将其重新放入采集队列。*/
//---根据返回的 buf.index 找到对应的 mmap 映射好的缓存,取出视频数据。
/*******
进行数据处理
*****/
//将刚刚处理完的缓冲重新入队列尾,这样可以循环采集
if(ioctl(cam_fd, VIDIOC_QBUF,&buf_2)<0){
printf("VIDIOC_QBUF is fail!! \n");
exit(11);
}
//停止视频的采集
if(ioctl(cam_fd,VIDIOC_STREAMOFF,&buf_type) < 0){
printf("VIDIOC_STREAMOFF is fail!! \n");
exit(12);
}
close(cam_fd);
return 0;
}