Linux系统v4l2编程详解之USB摄像头调试

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/suh666888/article/details/87343580

本文是我在查阅众多资料程序后,调试成功自己的摄像头后写下的。

1.定义

V4L2(Video For Linux Two) 是内核提供给应用程序访问音、视频驱动的统一接口。也就是Linux系统下视频设备驱动好后,应用程序怎样调用相关的视频设备的编程接口。

2.工作步骤

打开设备-> 检查和设置设备属性-> 设置帧格式-> 设置一种输入输出方法-> 循环获取数据-> 关闭设备。

3.检查设备是否支持UVC协议

插上USB摄像头,命令终端输入:lsusb 

然后输入:lsusb -d 0c45:6340 -v |grep "14 Video" // 0c45:6340设备ID见上图

 

输出如上则证明支持UVC协议

4.打开设备:

int fd = open(DEV_PATH,O_RDWR,0);//阻塞方式(活干不完不准回来)打开摄像头

if(fd<0)

{  printf("open device failed.\n"); }

5.查询和设置设备属性:VIDIOC_QUERYCAP

结构体:struct v4l2_capability

{

u8 driver[16]; // 驱动名字

u8 card[32]; // 设备名字

u8 bus_info[32]; // 设备在系统中的位置

u32 version; // 驱动版本号

u32 capabilities; // 设备支持的操作

u32 reserved[4]; // 保留字段

};//结构体 存放在/usr/include/linux/videodev2.h 路径下

代码段:

struct v4l2_capability cap;	
if(ioctl(fd,VIDIOC_QUERYCAP,&cap)==-1)
{		
    printf("VIDIOC_QUERYCAP failed.\n");
}	
printf("VIDIOC_QUERYCAP success.->DriverName:%s CardName:%s BusInfo:%s\n",cap.driver,cap.card,cap.bus_info);//查询驱动设备信息

6.显示并设置帧的格式

代码段:

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++;
}

 

struct v4l2_format fmt;
   
memset ( &fmt, 0, sizeof(fmt) );	
    
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//这里没有设置像素大小,由设备决定
    
if (ioctl(fd,VIDIOC_S_FMT,&fmt) == -1)
 
{	  
        printf("VIDIOC_S_FMT failed.\n");	 

        return -1;  
}	
    
printf("VIDIOC_S_FMT sucess.\n");	

  
if (ioctl(fd,VIDIOC_G_FMT,&fmt) == -1)

{	  
        printf("VIDIOC_G_FMT failed.\n");	
  
        return -1;
}  
printf("VIDIOC_G_FMT sucess.->fmt.fmt.width is %ld\nfmt.fmt.pix.height is %ld\n\
fmt.fmt.pix.colorspaceis%ld\n",fmt.fmt.pix.width,fmt.fmt.pix.height,fmt.fmt.pix.colorspace);

 

检查是否支持YUYV格式:

 memset ( &fmt, 0, sizeof(fmt) );
   
 fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;	
 
 fmt.fmt.pix.pixelformat=V4L2_PIX_FMT_YUYV;	
 
 if (ioctl(fd,VIDIOC_TRY_FMT,&fmt) == -1)
  
 {  
         printf("VIDIOC_TRY_FMT failed.\n");

	 return -1; 
 }   printf"VIDIOC_TRY_FMTsucess.->fmt.fmt.widthis%ld\nfmt.fmt.pix.heightis%ld\n\fmt.fmt.pix.colorspaceis%ld\n",fmt.fmt.pix.width,fmt.fmt.pix.height,fmt.fmt.pix.colorspace);

7.申请和管理缓冲区

应用程序和设备有三种交换数据的方法,直接 read/write、内存映射(memory mapping)

和用户指针。这里本人采用内存映射(memory mapping)。

 1.申请缓冲区

 2.管理缓冲区

 3.映射内存:内存映射MMAP 及定义一个结构体来映射每个缓冲帧。 相关结构体:

struct buffer{  
	void *start;  
	unsigned int length;  
}*buffers; 

 4.缓冲放入队列

struct v4l2_requestbuffers req;
  
req.count = 2;//frame count.帧的个数 	
  
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;	

req.memory = V4L2_MEMORY_MMAP;
if ( ioctl(fd,VIDIOC_REQBUFS,&req)==-1)

{  		
         
printf("VIDIOC_REQBUFS map failed.\n");
     
close(fd);
         
exit(-1);
} 	
    
printf("VIDIOC_REQBUFS map success.\n");
 
unsigned int n_buffers = 0;  
 
buffers = calloc (req.count, sizeof(*buffers));
 
for(n_buffers = 0; n_buffers < req.count; ++n_buffers)
   
{
   struct v4l2_buffer buf;
      
   memset(&buf,0,sizeof(buf));
        
   buf.index = n_buffers;
       
   buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
    
   buf.memory = V4L2_MEMORY_MMAP;
       
   if(ioctl(fd,VIDIOC_QUERYBUF,&buf) == -1)
     
   {  			
          
             printf("VIDIOC_QUERYBUF failed.\n");	
		
             close(fd); 
 			
             exit(-1);
   }    
     
    printf("VIDIOC_QUERYBUF success.\n");
     
    buffers[n_buffers].length = buf.length;
    buffers                                                                                  [n_buffers].start=mmapNULL,buf.length,PROT_READ|PROT_WRITE,MAP_SHARED,fd,buf.m.offset);  	
	
  if(MAP_FAILED == buffers[n_buffers].start)
       
  {  			
             printf("memory map failed.\n");	
		
             close(fd);  			
      
       exit(-1);  		
  } 		

         printf("memory map success.\n");
     
    if (ioctl(fd , VIDIOC_QBUF, &buf) ==-1)
     
    {		    
             printf("VIDIOC_QBUF failed.->n_buffers=%d\n", n_buffers);
		             return -1;		
     }		

        printf("VIDIOC_QBUF.->Frame buffer %d: address=0x%x, length=%d\n",n_buffers, (unsigned int)buffers[n_buffers].start, buffers[n_buffers].length);	


    }
   

8.使能设备输出数据流

 enum v4l2_buf_type type;

 type = V4L2_BUF_TYPE_VIDEO_CAPTURE;  

if (ioctl(fd,VIDIOC_STREAMON,&type) == -1)

{  printf("VIDIOC_STREAMON failed.\n");

  return -1;

 }

 printf("VIDIOC_STREAMON success.\n");

 

9.处理数据获得.YUV文件

用YUVplayer打开即可

获得图像


 

猜你喜欢

转载自blog.csdn.net/suh666888/article/details/87343580