简介:
Video4linux2(简称V4L2),是linux中关于视频设备的内核驱动。
在Linux中,视频设备是设备文件,可以像访问普通文件一样对其进行读写。
摄像头文件一般放在在/dev/video0下。
流程:
1、打开视频设备。
int g_videofd = open(“/dev/video0”, O_RDWR, 0); //选项可为 O_RDWR| O_NONBLOCK,非阻塞,即无数据依然返回。
2、取得设备的capability。(重要)//l inux-2.6.30/include/linux/videodev2.h定义
1 int VIPP_VIDEO_Queryinfo() 2 { 3 int ret; 4 struct v4l2_capability cap; 5 6 ret = ioctl(g_videofd, VIDIOC_QUERYCAP, &cap); 7 if (ret < 0) { 8 printf("VIDIOC_QUERYCAP failed (%d) ",ret); 9 return ret; 10 } 11 12 printf(“ Capability Informations: ”); // 13 printf(" driver: %s ", cap.driver); // 14 printf(“ card: %s ”, cap.card); // 15 printf(“ bus_info: %s ”, cap.bus_info); // 16 printf(“ version: %08X ”, cap.version); // 17 printf(“ version: %u.%u.%u ”, (cap.version>>16)&0xff, (cap.version>>8)&0xff, cap.version&0xff); // 18 printf(" capabilities: %08X ", cap.capabilities); 19 printf(" reserved: %08X.%08X.%08X.%08X ",cap.reserved[0],cap.reserved[1], 20 cap.reserved[2],cap.reserved[3]); // 21 22 return 0; 23 }
3、设置视频的制式和帧格式。
注意:如果该视频设备驱动不支持你所设定的图像格式,视频驱动会重新修改struct v4l2_format结构体变量的值为该视频设备所支持的 图像格式,所以在程序设计中,设定完所有的视频格式后,要获取实际的视频格式,要重新读取struct v4l2_format结构体变量。
1 int VIPP_VIDEO_Setparam(int width, int height, int format) 2 { 3 int ret; 4 memset(&gst_videofmt, 0, sizeof(gst_videofmt)); 5 gst_videofmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; //数据流类型,必须永远是 //V4L2_BUF_TYPE_VIDEO_CAPTURE 6 gst_videofmt.fmt.pix.width = width; 7 gst_videofmt.fmt.pix.height = height; 8 gst_videofmt.fmt.pix.pixelformat = format; 9 gst_videofmt.fmt.pix.field = V4L2_FIELD_INTERLACED; 10 ret = ioctl(g_videofd, VIDIOC_S_FMT, &gst_videofmt);//设置当前驱动的频捕获格式 11 if (ret < 0) { 12 printf("VIDIOC_S_FMT failed (%d) ", ret); 13 return ret; 14 } 15 16 gst_videofmt.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB32; 17 if (ioctl(g_videofd, VIDIOC_TRY_FMT, &gst_videofmt) == -1)//验证当前驱动的显示格式 18 { 19 if ( errno == EINVAL ){ 20 printf("not support format RGB32 "); 21 return (-1); 22 } 23 } 24 return 0; 25 }
4、向系统申请帧缓冲(一般不超过5个)。
struct v4l2_requestbuffers reqbuf;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
// 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
reqbuf.memory = V4L2_MEMORY_MMAP;
// V4L2_MEMORY_MMAP或 V4L2_MEMORY_USERPTR(内存片段由应用程序自己分配)
reqbuf.count = count; //缓存数量,也就是说在缓存队列里保持多少张照片,一般不超过5个
ret = ioctl(g_videofd , VIDIOC_REQBUFS, &reqbuf);
if(ret < 0) {
printf("VIDIOC_REQBUFS failed (%d)
", ret);
return ret;
}
5、将申请到的帧缓冲映射到用户空间。
6、将申请到的帧缓冲全部入队列。
for(i = 0; i< count; i++) { gst_videobuf.index = i; // 要获取内核视频缓冲区的信息编号 gst_videobuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; // 数据流类型 gst_videobuf.memory = V4L2_MEMORY_MMAP; //把内核空间缓冲区映射到用户空间缓冲区 ret = ioctl(g_videofd , VIDIOC_QUERYBUF, &gst_videobuf); if(ret < 0) { printf("VIDIOC_QUERYBUF (%d) failed (%d) ", i, ret); return ret; } frame[i].length = gst_videobuf.length; // 图像的大小 frame[i].start = (char *) mmap(0, gst_videobuf.length, PROT_READ|PROT_WRITE, MAP_SHARED, g_videofd, gst_videobuf.m.offset); if (frame[i].start == MAP_FAILED) { printf("mmap (%d) failed: %s ", i, strerror(errno)); return -1; } ret = ioctl(g_videofd , VIDIOC_QBUF, &gst_videobuf); if (ret < 0){ printf ("VIDIOC_QBUF (%d) failed (%d) ", i, ret); return -1; } }
7、开始视频的采集。
启动视频采集命令,应用程序调用VIDIOC_STREAMON启动视频采集命令后,视频设备驱动程序开始采集视频数据,并把采集到的视频 数据保存到视频驱动的视频缓冲区中
int VIPP_VIDEO_Streamon() { int ret; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(g_videofd, VIDIOC_STREAMON, &type); if (ret < 0) { printf("VIDIOC_STREAMON failed (%d) ", ret); return ret; } return 0; }
8、出队列以取得已采集数据的帧缓冲,取得原始采集数据。
int VIPP_VIDEO_ReleaseStream() { int ret; ret = ioctl(g_videofd, VIDIOC_QBUF, &gst_videobuf); if (ret < 0) { LOG("VIDIOC_QBUF failed (%d) ", ret); return ret; } return 0; }
9、把原始数据保存入文件中。
char filename[100] = {0};
FILE *fp = NULL;
sprintf(filename, "Image%d.%d_jpeg", i, j);
fp = fopen(filename, "w");
fwrite(frame[i].start, 1, frame[i].length, fp);
fclose(fp);
10、将缓冲重新入队列尾,这样可以循环采集。
int VIPP_VIDEO_ReleaseStream() { int ret; ret = ioctl(g_videofd, VIDIOC_QBUF, &gst_videobuf); if (ret < 0) { printf("VIDIOC_QBUF failed (%d) ", ret); return ret; } return 0; }
11、停止视频的采集。
int VIPP_VIDEO_Streamoff() { int ret; enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE; ret = ioctl(g_videofd, VIDIOC_STREAMOFF, &type); if (ret < 0) { printf("VIDIOC_STREAMON failed (%d) ", ret); return ret; } return 0; }
12、关闭视频设备文件。
int VIPP_VIDEO_End() { int i; for (i=0; i < COUNT; i++) { munmap(frame[i].start, frame[i].length); } close(g_videofd); return 0; }
相关结构体:
struct v4l2_requestbuffers //申请帧缓冲,对应命令VIDIOC_REQBUFS
struct v4l2_capability //视频设备的功能,对应命令VIDIOC_QUERYCAP
struct v4l2_input //视频输入信息,对应命令VIDIOC_ENUMINPUT
struct v4l2_standard //视频的制式,比如PAL,NTSC,对应命令VIDIOC_ENUMSTD
struct v4l2_format //帧的格式,对应命令VIDIOC_G_FMT、VIDIOC_S_FMT等
struct v4l2_buffer //驱动中的一帧图像缓存,对应命令VIDIOC_QUERYBUF
struct v4l2_crop //视频信号矩形边框
v4l2_std_id //视频制式
常用命令:
VIDIOC_REQBUFS:分配内存
VIDIOC_QUERYBUF:把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
VIDIOC_QUERYCAP:查询驱动功能
VIDIOC_ENUM_FMT:获取当前驱动支持的视频格式
VIDIOC_S_FMT:设置当前驱动的频捕获格式
VIDIOC_G_FMT:读取当前驱动的频捕获格式
VIDIOC_TRY_FMT:验证当前驱动的显示格式
VIDIOC_CROPCAP:查询驱动的修剪能力
VIDIOC_S_CROP:设置视频信号的边框
VIDIOC_G_CROP:读取视频信号的边框
VIDIOC_QBUF:把数据从缓存中读取出来
VIDIOC_DQBUF:把数据放回缓存队列
VIDIOC_STREAMON:开始视频显示函数
VIDIOC_STREAMOFF:结束视频显示函数
VIDIOC_QUERYSTD:检查当前视频设备支持的标准,例如PAL或NTSC。