毕设系列之Linux V4L2(图形图像采集篇)

PS:要转载请注明出处,本人版权所有。

PS: 这个只是基于《我自己》的理解,

如果和你的原则及想法相冲突,请谅解,勿喷。

前置说明

  本文作为本人csdn blog的主站的备份。(BlogID=034)
  本文发布于 2017-07-02 14:12:38,现用MarkDown+图床做备份更新。blog原图已丢失,使用csdn所存的图进行更新。(BlogID=034)

环境说明

  Ubuntu 16.04 LTS

前言


  无





V4L2




V4L2 介绍

  虽然介绍Linux V4L2的文章已经满大街了,但是这里我也还要讲一些基本的东西。

  1. v4l2 是Video for Linux 2的简称。
  2. v4l2 不仅仅支持图像类设备,还支持音频等设备类型。
  3. 能够使用v4l2的前提是Linux内核已经识别并注册了了相关的设备,常见的就是/dev/videox类似的设备文件存在。(如果是做图像类的采集,说白了你得有个摄像头,这个摄像头还得被Linux 内核识别)
  4. 从上文可知,一个设备要被内核识别必须要由驱动才行,而对于我们常用的图像采集来说,就是要有一个摄像头驱动才行。对于这个问题,linux内核工作者根据UVC这个标准开发了一个通用的驱动程序,并且整合到linux内核中。只要是支持UVC这个通用标准的摄像头芯片,就能够使用这个驱动。(非图像类也有类似的存在,具体这里不做多余阐述)
  5. 一般来说,常用的Linux平台是的内核是已经编译进去了UVC驱动的。如果是自己移植的嵌入式Linux 内核,请在配置内核选项时候打开UVC驱动的选项。如下图:
rep_img


V4L2框架简介

  V4L2这个框架的原理简述(这里我们只需要关注几个结构体就可以,要深入,去看内核源代码)

  1. struct v4l2_device v4l2框架中的根节点,主要是用来管理和遍历其它子节点。
  2. struct v4l2_subdev v4l2框架中的子节点,在v4l2_device下可以有多个v4l2_subdev存在,这里主要是区分设备类型,如图像或者音频等等。
  3. struct video_device 具体设备的结构体,并在/dev/下创建相关的设备文件。
  4. struct v4l2_buffer 为设备数据交换提供空间。

  原理(这里以USB摄像头为例):当一个设备接入内核,首先根据USB标准协议对USB进行初始化,最终完成USB设备信息探测。根据USB设备信息,内核给其分配相应的驱动。这里内核知道我们的USB设备是一个图像设备,这时内核开始初始化v4l2_subdev结构体,并且类型设置为图形图像设备。当初始化好后,内核继续初始化一个video_device结构体,并插入到v4l2_subdev的管理链表中。这里的初始化过程中,就要涉及摄像头驱动的加载,设备文件的创建等等。到这里,整个注册环节就结束了,意味着我们可以使用v4l2框架来操作我们的设备。操作的话,就是各种ops的调用就ok了。(这里涉及到usb设备驱动的初始化和字符型设备驱动等等相关知识,需要更多,自行查阅资料)



V4L2的使用

  V4L2的使用(没啥可讲的,各种资料烂大街了,我直接贴源代码)
ym_v4l2.c文件

/*
	FileName:m_v4l2.c
	Version:1.5
	Description:
	Created On: 2017-2-21
	Modified date:2017-3-14
	Author:Sky
*/
#include <ym_v4l2.h>
int yInitMV4l2(const char * pathname, yMV4L2 * mv4l2){
	
	//mv4l2 = mvl;
	//request alloc IMG_BUFF_NUM DATA_BUF size mem
    if ( (mv4l2->img_buf = (IMG_BUF *)calloc(IMG_BUFF_NUM, sizeof(IMG_BUF))) == NULL){
        printf("calloc  failed\n");
        return -1;
    }
	if ( (mv4l2->camera_fd = open(pathname, O_RDWR | O_NONBLOCK)) < 0){//open video device
    
        perror("Open video device faild");
        return -1;
    }
	
	return 0;
}
int yIoctlV4l2(enum yV4l2Cmd cmd,...){

    va_list arg;
    va_start(arg,cmd);
    yMV4L2 *mv4l2;
    mv4l2 = va_arg(arg,yMV4L2 *);
    va_end(arg);
    switch(cmd){
    
        case yVIDIOC_QUERYCAP:
        {
    	    if ( ioctl(mv4l2->camera_fd, VIDIOC_QUERYCAP, &mv4l2->cap) < 0){
        
                 perror("QUERY VIDEO CAP FAILED");
                return -1;
            }
	        printf("DriverName:%s/nCard Name:%s/nBusinfo:%s/nDriverVersion:%u.%u.%u\n",mv4l2->cap.driver,mv4l2->cap.card,mv4l2->cap.bus_info,(mv4l2->cap.version>>16)&0XFF,(mv4l2->cap.version>>8)&0xFF,mv4l2->cap.version&0xFF);
            if ( !(mv4l2->cap.capabilities & V4L2_BUF_TYPE_VIDEO_CAPTURE) ){
    
                printf("The device is not a video capture\n");
                return -1;
            }
            if ( !(mv4l2->cap.capabilities & V4L2_CAP_STREAMING) ){
        
                 printf("The device can not support streaming i/o\n");
                return -1;
            }
            break;
        } 
        case yVIDIOC_ENUM_FMT:
        {
        	CLEAR_MEM(mv4l2->desc_fmt);
            mv4l2->desc_fmt.index = 0;
            mv4l2->desc_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
            while( ioctl(mv4l2->camera_fd, VIDIOC_ENUM_FMT,&mv4l2->desc_fmt) == 0 ){
    
                printf("index : %d, format:%s \n", mv4l2->desc_fmt.index,mv4l2->desc_fmt.description);
		        mv4l2->desc_fmt.index++;
            }
            break;
        } 
        case yVIDIOC_S_FMT:
        {
			// set data format for dev
    		mv4l2->stream_fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		mv4l2->stream_fmt.fmt.pix.width = IMAGE_WIDTH;
    		mv4l2->stream_fmt.fmt.pix.height = IMAGE_HEIGHT;
    		mv4l2->stream_fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
    		//mv4l2->stream_fmt.fmt.pix.field = V4L2_FIELD_INTERLACED;
			mv4l2->stream_fmt.fmt.pix.field = V4L2_FIELD_ANY;
			if ( ioctl(mv4l2->camera_fd, VIDIOC_S_FMT, &mv4l2->stream_fmt)  ){
    
       			perror("Set data format failed");
        		return -1;
    		}
           	break;
        } 
        case yVIDIOC_G_FMT:
        {
			CLEAR_MEM(mv4l2->stream_fmt);
			mv4l2->stream_fmt.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;  
			ioctl(mv4l2->camera_fd,VIDIOC_G_FMT,&mv4l2->stream_fmt);  
			printf("Currentdata format information: width:%d   height:%d\n",mv4l2->stream_fmt.fmt.pix.width,mv4l2->stream_fmt.fmt.pix.height); 
           	break;
        } 
        case yVIDIOC_REQBUFS:
        {
	    //bzero(&reqbuf, sizeof(reqbuf));
			CLEAR_MEM(mv4l2->reqbuf);
			mv4l2->reqbuf.count = IMG_BUFF_NUM;
    		mv4l2->reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		mv4l2->reqbuf.memory = V4L2_MEMORY_MMAP;
    
    		if ( ioctl(mv4l2->camera_fd, VIDIOC_REQBUFS, &mv4l2->reqbuf) < 0 ){
    
        		perror("ioctl REQBUFS failed");
        		return -1;
    		}
           	break;
        }    
        case yVIDIOC_STREAMON:
        {
			mv4l2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		if ( ioctl(mv4l2->camera_fd, VIDIOC_STREAMON,&mv4l2->type)< 0){
            
            	perror("Failed to ioctl:VIDIOC_STREAMON");
            	return -1;
    		}
           	break;
        }
        case yVIDIOC_STREAMOFF:
        {
    		mv4l2->type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		if ( ioctl(mv4l2->camera_fd, VIDIOC_STREAMOFF,&mv4l2->type)< 0){
            
            	perror("Failed to ioctl:VIDIOC_STREAMOFF");
            	return -1;
    		}
           	break;
        } 
        case yVIDIOC_S_PARM:
        {
			CLEAR_MEM(mv4l2->stream_parm);
			mv4l2->stream_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; 
	
			mv4l2->stream_parm.parm.capture.timeperframe.numerator = 1;
			mv4l2->stream_parm.parm.capture.timeperframe.denominator = 10;
			if ( ioctl(mv4l2->camera_fd, VIDIOC_S_PARM, &mv4l2->stream_parm) < 0){

				perror("Failed to ioctl:VIDIOC_S_PARM");
        		return -1;
			}
           	break;
        } 
        case yVIDIOC_G_PARM:
        {
			CLEAR_MEM(mv4l2->stream_parm);
			mv4l2->stream_parm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;

			if ( ioctl(mv4l2->camera_fd, VIDIOC_G_PARM, &mv4l2->stream_parm) < 0){

				perror("Failed to ioctl:VIDIOC_G_PARM");
        		return -1;
			}
			if (  mv4l2->stream_parm.parm.capture.capability == V4L2_CAP_TIMEPERFRAME ){
				printf("This Video Support Set Fps,Now-Fps is : %d\n",mv4l2->stream_parm.parm.capture.timeperframe.denominator);
			}
			else{
				printf("This Video Un-Support Set Fps\n");
			}
           	break;
        }
        case yVIDIOC_DQBUF:
        {
			//bzero(&normal_buf, sizeof(normal_buf));
			CLEAR_MEM(mv4l2->normal_buf);
    		mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    		mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
    		if ( ioctl(mv4l2->camera_fd, VIDIOC_DQBUF, &mv4l2->normal_buf) < 0){

        		perror("Failed to ioctl:VIDIOC_DQBUF");
        		return -1;
    
    		} 
           	break;
        } 
        case yVIDIOC_QBUF:
        {
			if ( ioctl(mv4l2->camera_fd, VIDIOC_QBUF, &mv4l2->normal_buf) < 0){

             	perror("Failed to ioctl:VIDIOC_QBUF");
            	return -1;
    		} 
           	break;
        }
		case yMMAPTOVEDIOBUF:
		{
			for ( int i=0; i < IMG_BUFF_NUM; i++ ){
    
        		//bzero(&normal_buf, sizeof(normal_buf));
				CLEAR_MEM(mv4l2->normal_buf);
        		mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        		mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
        		mv4l2->normal_buf.index = i;
        		//get kernel cache information
        
        		if ( ioctl(mv4l2->camera_fd, VIDIOC_QUERYBUF,&mv4l2->normal_buf) < 0){
            
            		perror("Failed to ioctl:VIDIOC_QUERYBUF");
            		return -1;
        		}
        		mv4l2->img_buf[i].len = mv4l2->normal_buf.length;
        		mv4l2->img_buf[i].start = mmap(NULL,mv4l2->normal_buf.length,
                	PROT_READ|PROT_WRITE,
                    MAP_SHARED,mv4l2->camera_fd,
                    mv4l2->normal_buf.m.offset);

        		if ( MAP_FAILED == mv4l2->img_buf[i].start){
            
            		perror("Failed to mmap");
            		return -1;
        		}
    		}
			break;
		}
		case yPUTVEDIOALLBUFTOQUEUE:
		{
			    //bzero(&normal_buf, sizeof(normal_buf));
			for ( int i = 0; i < IMG_BUFF_NUM;i++){
        
				CLEAR_MEM(mv4l2->normal_buf);
    			mv4l2->normal_buf.index = i;
    			mv4l2->normal_buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    			mv4l2->normal_buf.memory = V4L2_MEMORY_MMAP;
    			if ( ioctl(mv4l2->camera_fd, VIDIOC_QBUF,&mv4l2->normal_buf) < 0){
            
        			perror("Failed to ioctl:VIDIOC_QBUF");
        			return -1;
    			}
          
			}  
			break;
		}
		case yUNMMAPTOVEDIOBUF:
		{
			for (int i = 0; i < IMG_BUFF_NUM; i++){

    			if ( munmap(mv4l2->img_buf[i].start,mv4l2->img_buf[i].len) < 0){

            	perror("Failed to munmap");

            	return -1;

    			}

    		}  
			break;
		}
        default :
        {
            return -1;
            break;
        } 

        
    }

    return 0;
}

int yDestroyMV4l2(yMV4L2 *mv4l2){
	
	//StopStream();
	yIoctlV4l2(yVIDIOC_STREAMOFF,mv4l2);
	//UnMMapToVedioBUf(mv4l2);
	yIoctlV4l2(yUNMMAPTOVEDIOBUF,mv4l2);
	close(mv4l2->camera_fd);
	free(mv4l2->img_buf);
	return 0;
}

ym_v4l2.h

/*
	FileName:ym_v4l2.h
	Version:1.5
	Description:
	Created On: 2017-2-21
	Modified date:2017-3-14
	Author:Sky
*/

#ifndef _YM_V4L2_H
#define _YM_V4L2_H

#ifdef __cplusplus
#if __cplusplus
extern "C"{
#endif
#endif /* __cplusplus */

#include <ym_v4l2_config.h>
//open
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
//memset
#include <string.h>
//v4l2
#include <linux/videodev2.h>
//errno
#include <errno.h>
//perror
#include <stdio.h>
//calloc,free
#include <stdlib.h>
//close
#include <unistd.h>
//ioctl
#include <sys/ioctl.h>
//mmap,munmap
#include <sys/mman.h>


#include <stdarg.h>

#define CLEAR_MEM(x) memset(&(x),0,sizeof(x))

enum yV4l2Cmd{

    yVIDIOC_QUERYCAP = 0,//Get Camera Capability
    yVIDIOC_ENUM_FMT = 1,//Get Camera all support-format
    yVIDIOC_S_FMT = 2,//Set Img Format
    yVIDIOC_G_FMT = 3,//Get Img Format
    yVIDIOC_REQBUFS = 4,//Req Video buf
    yVIDIOC_STREAMON = 5,//Start stream
    yVIDIOC_STREAMOFF = 6,//Stop stream
    yVIDIOC_S_PARM = 7,//Set Fps info
    yVIDIOC_G_PARM = 8,//Get Fps info
    yVIDIOC_DQBUF = 9,//delete buf from out queue
    yVIDIOC_QBUF = 10,//put buf to in queue
	yMMAPTOVEDIOBUF = 11,
	yUNMMAPTOVEDIOBUF = 12,
	yPUTVEDIOALLBUFTOQUEUE = 13,

};


typedef struct {
    
    void * start;
    long len;
} IMG_BUF;

typedef struct ymv4l2{
	
	int camera_fd;//camara file descriptor
	IMG_BUF *img_buf;//img buf head
	
	struct v4l2_buffer normal_buf;
	struct v4l2_fmtdesc desc_fmt;
    struct v4l2_capability cap;
    struct v4l2_format stream_fmt;
    struct v4l2_requestbuffers reqbuf;
	struct v4l2_streamparm stream_parm;
    enum v4l2_buf_type type;
	
}yMV4L2; 



int yInitMV4l2(const char * pathname, yMV4L2 * mvl);
int yDestroyMV4l2(yMV4L2 * mvl);
int yIoctlV4l2(enum yV4l2Cmd cmd,...);

//int OpenCamera(const char * pathname);
//int GetCapability(void);
//int GetAllSupportFormat(void);
//int SetFrameInfo(void);//to set fps
//int GetFrameInfo(void);
//int SetImgFormat(void);
//int GetImgFormat(void);
//int RequestVedioBuf(void);
//int MMapToVedioBuf(void);
//int PutVedioBufToQueue(void);
//int UnMMapToVedioBUf(void);
//int StartStream(void);
//int StopStream(void);
//int ConfigV4l2(void);
//int GetVedioBufFromQueue(void);
//int PutVedioBufToQueue(void);

#ifdef __cplusplus
#if __cplusplus
}
#endif
#endif /* __cplusplus */ 


#endif





后记


  无

参考文献




打赏、订阅、收藏、丢香蕉、硬币,请关注公众号(攻城狮的搬砖之路)
qrc_img

PS: 请尊重原创,不喜勿喷。

PS: 要转载请注明出处,本人版权所有。

PS: 有问题请留言,看到后我会第一时间回复。

posted on   SkyOnSky  阅读(146)  评论(0编辑  收藏  举报

相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了

导航

< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

统计

点击右上角即可分享
微信分享提示