linux视频设备操作流程

因为工作中需要用到摄像头,了解了下视频设备的使用流程。

#include <stdio.h>
#include <stdlib.h>
//#include <string.h>
//#include <pthread.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/mman.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <linux/fb.h>
#include <linux/videodev.h>



int main()
{
	//1. 打开设备文件。
	int cameraFd;
	cameraFd = open("/dev/video0",O_RDWR | O_NONBLOCK);
	if(cameraFd <0)
	{
		printf("open failed. error code: %d \n", errno);
		return -1;
	}
	
	int ret;
	int img_width=352;
	int img_height=288;
	int i;
	int AppBufLength;
	void *AppBufStartAddr;
	struct v4l2_fmtdesc fmt;
	
    memset(&fmt, 0, sizeof(struct v4l2_fmtdesc));
   	fmt.index = 0;
   	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
   	while ((ret = ioctl(cameraFd, VIDIOC_ENUM_FMT, &fmt)) == 0) //获取当前视频设备支持的视频格式
   	{
          fmt.index++;
          printf("{ pixelformat = ''%c%c%c%c'', description = ''%s'' }\n",
                        fmt.pixelformat & 0xFF, 
                        (fmt.pixelformat >> 8) & 0xFF,
                        (fmt.pixelformat >> 16) & 0xFF, 
                        (fmt.pixelformat >> 24) & 0xFF,
                        fmt.description);
   	}
	
    //2. 取得设备的capability,看看设备具有什么功能,比如是否具有视频输入,或者音频输入输出等。VIDIOC_QUERYCAP,struct v4l2_capability
	struct v4l2_capability cap; 
	ret = ioctl(cameraFd, VIDIOC_QUERYCAP, &cap);// 查询视频设备的功能
	if(ret < 0)
	{
              printf("get video capability error,error code: %d \n", errno);
              return -1;
	}
	//3. 选择视频输入,一个视频设备可以有多个视频输入。VIDIOC_S_INPUT,struct v4l2_input(可不要)	

	//4. 设置视频的制式和帧格式,制式包括PAL,NTSC,帧的格式个包括宽度和高度等。
	struct v4l2_format tv4l2_format; 
	tv4l2_format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
	tv4l2_format.fmt.pix.width = img_width; 
	tv4l2_format.fmt.pix.height = img_height; 
	tv4l2_format.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;// 视频数据存储类型,例如是YUV 4 :2 :2 还是RGB
	tv4l2_format.fmt.pix.field = V4L2_FIELD_INTERLACED; 
	
	ret = ioctl(cameraFd, VIDIOC_S_FMT, &tv4l2_format);//设置当前驱动的频捕获格式
	if(ret <0)
	{
		printf("can't set format . error code: %d \n", errno);
		return -1;
	}
	
	//5. 向驱动申请帧缓冲,一般不超过5个。struct v4l2_requestbuffers
	struct v4l2_requestbuffers  tV4L2_reqbuf;
	memset(&tV4L2_reqbuf, 0, sizeof(struct v4l2_requestbuffers ));
	 
	tV4L2_reqbuf.count = 1;    //// 缓存数量,也就是说在缓存队列里保持多少张照片
	tV4L2_reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;// 数据流类型,必须永远是V4L2_BUF_TYPE_VIDEO_CAPTURE
	tV4L2_reqbuf.memory = V4L2_MEMORY_MMAP;//内核内存的方式   V4L2_MEMORY_MMAP 或 V4L2_MEMORY_USERPTR
	 
	ret = ioctl(cameraFd, VIDIOC_REQBUFS, &tV4L2_reqbuf);//分配内存
	
	if(ret < 0)
	{
		printf("can't malloc buffer . error code: %d \n", errno);
		return -1;
	}
	
	//6.申请物理内存
	//将申请到的帧缓冲映射到用户空间,这样就可以直接操作采集到的帧了,而不必去复制。将申请到的帧缓冲全部入队列,以便存放采集到的数据.VIDIOC_QBUF,struct v4l2_buffer
	struct v4l2_buffer tV4L2buf; 
	memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));
	
	tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
	tV4L2buf.memory = V4L2_MEMORY_MMAP; 
	tV4L2buf.index = 0;  // 要获取内核视频缓冲区的信息编号
	ret = ioctl(cameraFd, VIDIOC_QUERYBUF, &tV4L2buf);//把VIDIOC_REQBUFS中分配的数据缓存转换成物理地址
	
	AppBufLength  = tV4L2buf.length;
	// 把内核空间缓冲区映射到用户空间缓冲区(内存映射)
	AppBufStartAddr = mmap(NULL /* start anywhere */ ,
	       tV4L2buf.length, 
	       PROT_READ | PROT_WRITE ,  /* access privilege */
	       MAP_SHARED, /* recommended */
	       cameraFd, tV4L2buf.m.offset); 
	
	/*
	struct v4l2_buffer tV4L2buf; 
	memset(&tV4L2buf, 0, sizeof(struct v4l2_buffer));
	 
	tV4L2buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
	tV4L2buf.memory = V4L2_MEMORY_MMAP; 
	tV4L2buf.index = i; //指令要投放到视频输入队列中的内核空间视频缓冲区的编号;
	*/ 
	ret = ioctl(cameraFd, VIDIOC_QBUF, &tV4L2buf);//投放一个空的视频缓冲区到视频缓冲区输入队列中
	if(ret < 0)
	{
		printf("cant Qbuf!\n");
		return -1;
	}
	
	//7. 开始视频的采集。
	enum v4l2_buf_type v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
	ret = ioctl(cameraFd, VIDIOC_STREAMON, &v4l2type);//开始视频显示函数
	if(ret < 0)
	{
		printf("can't start on!\n");
		return -1;
	}
	
	fd_set    fds ; 
	struct timeval   tv; 
	tv.tv_sec = 2;       /* Timeout. */ 
	tv.tv_usec = 0; 
	struct v4l2_buffer buffer;
	int fail_cnt=0;
	FILE *fp =fopen("/sdcard/video.yuv","wb");
	while(1)
	{	
		FD_ZERO(&fds); 
		FD_SET(cameraFd,  &fds); 
		
		ret = select(cameraFd+ 1, &fds, NULL, NULL, &tv); 
		if(ret <0)
		{
			printf("select error!\n");
			sleep(1);
			continue;
		}
		else if(ret ==0)
		{
			usleep(10000);
			continue;
		}
		else
		{
			if(FD_ISSET(cameraFd,&fds))
			{
				memset(&buffer,0,sizeof(struct v4l2_buffer));
				buffer.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
				buffer.memory=V4L2_MEMORY_MMAP;
				//8. 出队列以取得已采集数据的帧缓冲,取得原始采集数据。VIDIOC_DQBUF, 将缓冲重新入队列尾,这样可以循环采集。VIDIOC_QBUF			
				if(ioctl(cameraFd,VIDIOC_DQBUF,&buffer)<0)// 从视频缓冲区的输出队列中取得一个已经保存有一帧视频数据的视频缓冲区
				{
					
				}
				else
				{
					fail_cnt =0;
					int n = fwrite(AppBufStartAddr,AppBufLength,1,fp);
			    	if( ioctl(cameraFd, VIDIOC_QBUF, &buffer) <0)//Reput buffer into queue
			        {
						printf("can't put VIDIOC_QBUF!!! \n");
						return -1;
			        }
				        
				}
			}
		}
	}
	fclose(fp);
	//9. 停止视频的采集。VIDIOC_STREAMOFF
	v4l2type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	if(ioctl(cameraFd,VIDIOC_STREAMOFF,&v4l2type)<0)
	{
		printf("ipanel_camera_stop failed!\n");
		return -1;
	}
	//11.解除内存映射
	munmap(AppBufStartAddr,AppBufLength);
	
	//12.10. 关闭视频设备
	if(cameraFd>0)
		close(cameraFd);
	return 0;
}


你可能感兴趣的:(linux视频设备操作流程)