我是靠谱客的博主 美丽吐司,最近开发中收集的这篇文章主要介绍UVC摄像头开发概述UVC简介 IO接口      控制流程接口设计 后续,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

概述

        UVC摄像头由于其免驱特性目前在各种场合都有大量的应用,本文仅根据个人开发UVC摄像头过程进行总结记录。

有很多博主写的很好,大家可以都看一看。

UVC简介 

什么是UVC 

         UVC,全称为:USB video(device) class,是微软与另外几家设备厂商联合推出的为USB视频捕获设备定义的协议标准,目前已成为USB org标准之一。

        使用 UVC 的好处是USB 在 Video这块也成为一项标准了之后,硬件在各个程序之间彼此运行会更加顺利,而且也省略了驱动程序安装这一环节,操作系统只要是 Windows XP SP2 之后的版本都可以支持 UVC,Linux系统自2.4以后的内核都支持了大量的设备驱动,其中支持UVC设备。

UVC摄像头开发:V4L2协议框架

        V4L2 :video for linux version 2 ,是 Linux 里一套标准的视频驱动,是针对uvc免驱usb设备的编程框架,主要用于采集usb摄像头等。

        V4L2:https://baike.so.com/doc/497647-526878.html

IO接口      

        在Linux编程中,一般使用ioctl函数来对设备的I/O通道进行管理。Linux系统查询可知接口描述如下:

NAME
       ioctl - control device
SYNOPSIS
       #include <sys/ioctl.h>
       int ioctl(int d, unsigned long request, ...);
DESCRIPTION
       The  ioctl()  function  manipulates  the underlying device parameters of special files.  In particular, many operating characteristics of character special files (e.g., terminals) may be controlled with ioctl() requests.  The argument d must be an open file descriptor.
       The second argument is a device-dependent request code.  The third argument is an untyped pointer to memory.  It's traditionally char *argp (from the days before void * was valid C), and will be so named for this discussion.
       An  ioctl()  request  has encoded in it whether the argument is an in parameter or out parameter, and the size of the argument argp in bytes.  Macros and defines used in specifying an ioctl() request are located in the file <sys/ioctl.h>.
RETURN VALUE
       Usually, on success zero is returned.  A few ioctl() requests use the return value as an output parameter and return a nonnegative value on success.  On error, -1 is returned,  and  errno  is set appropriately.
ERRORS
       EBADF  d is not a valid descriptor.
       EFAULT argp references an inaccessible memory area.
       EINVAL request or argp is not valid.
       ENOTTY d is not associated with a character special device.
       ENOTTY The specified request does not apply to the kind of object that the descriptor d references.

         这里给出摄像头一般控制指令和参数,如下:

/****************************************************************************************************
   编号 |       描述                    |   使用命令cmd    |     主要参数结构体
    1   |  查询驱动程序和硬件信息       | VIDIOC_QUERYCAP  | struct v4l2_capability cap
    2   |  枚举设备所支持的image_format | VIDIOC_ENUM_FMT  | struct v4l2_fmtdesc
    3   |  获取当前image_format         | VIDIOC_G_FMT     | struct v4l2_format
    3.1 |  设置当前image_format         | VIDIOC_S_FMT     | struct v4l2_format
    4   |  获取当前stream信息           | VIDIOC_G_PARM    | struct v4l2_streamparm
    4.1 |  设置当前stream信息           | VIDIOC_S_PARM    | struct v4l2_streamparm  /此处可以设置帧率信息
    5   |  获取相机一般控制项           | VIDIOC_G_CTRL    | struct v4l2_control   
    6   |  查询当前关键参数具体信息     | VIDIOC_QUERYCTRL | struct v4l2_queryctrl
    7   |  设置相机一般控制项           | VIDIOC_S_CTRL    | struct v4l2_control     
struct v4l2_streamparm
{
	enum v4l2_buf_type type;
	union{
		struct v4l2_captureparm capture;
		struct v4l2_outputparm output;
		_u8 raw_data[200];
	}parm;
};
struct v4l2_captureparm
{
	_u32 capability;//是否可以被timeperframe控制帧数 如可,则设置为:V4L2_CAP_TIMEPERFRAME
	_u32 capturemode; //是否为高清模式。如是则设为:V4L2_MODE_HIGHQUALITY ,高清模式为牺牲其他信息,一般设置为0
	struct v4l2_fract timeperframe;//帧率
	_u32 extendedmode;//定制的,如不支持则设置为0
	_u32 readbuffers;
	_u32 reserved[4];
};

struct v4l2_fract
{
	_u32 numerator;//分子,如设置1
	_u32 denominator;// 分母,如30
};

struct v4l2_control 
{
	_u32 id;/一般参数枚举,如:V4L2_CID_EXPOSURE_AUTO4L2_CID_EXPOSURE_ABSOLUTE、4L2_CID_GAIN
	_s32 value;//参数值
};
struct v4l2_queryctrl
{
	_u32 id;//用户设置。指定查找的ID 
	enum v4l2_ctrl_type type;
	_u8 name[32];//ID对应的名字
	_u32 minimum;
	_u32 maximum;
	_s32 step;//步长
	_s32 default_value;
	_u32 flags;
	_u32 reserved[4];
};
*************************************************************************************************/

 这里有一个Video for Linux Two API Specification:https://www.linuxtv.org/downloads/legacy/video4linux/API/V4L2_API/spec-single/v4l2.html

控制流程

        本次需求是将UVC摄像头采集到的图像显示到FrameBuffer上面,所以控制流程图如下,涉及到相机或者摄像机控制协议都有控制流接口和流通道接口,同时图像数据交互有一块共用内存,在这里记为共享内存,不一定是指软件开发中的共享内存。

接口设计

打开关闭摄像头

        打开关闭采用Linux一般文件设备接口open/close对设备进行操作。在应用层,我们可以在 /dev 目录发现 video0 类似的设备节点,上层的摄像头程序打开设备节点进行数据捕获,显示视频画面。设备节点的名字很统一,video0 video1 video2…。这些设备节点在是核心层注册。

NAME
       open, openat, creat - open and possibly create a file

SYNOPSIS
       #include <sys/types.h>
       #include <sys/stat.h>
       #include <fcntl.h>

       int open(const char *pathname, int flags);
       int open(const char *pathname, int flags, mode_t mode);
       int creat(const char *pathname, mode_t mode);
       int openat(int dirfd, const char *pathname, int flags);
       int openat(int dirfd, const char *pathname, int flags, mode_t mode);
---------------------------------------------------------------------------------
NAME
       close - close a file descriptor
SYNOPSIS
       #include <unistd.h>

       int close(int fd);

所以摄像开关控制代码实现如下:

int camera_open(void)
{
	int iErrCode = OK;

	g_iFdCamera = open("/dev/video0"  ,O_RDWR);
	if (g_iFdCamera < 0) 
	{
		printf("Open /dev/video0 failedn");
		iErrCode = CAMERA_OPEN_ERROR;
    }
	
	return iErrCode;
}

int camera_close(void)
{
    int iErrCode = OK;
	iErrCode = close(g_iFdCamera);
	if(OK != iErrCode)
	{
		iErrCode = CAMERA_CLOSE_ERROR;
	}
    return iErrCode;
}

 查询相机相关信息

打开摄像头后,我们可以查询相关摄像头驱动程序和硬件相关信息,采用如下

VIDIOC_QUERYCAPstruct v4l2_capability cap

代码如下:

int camera_qurey_hardware_info(void)
{
	int iErrCode = OK;
	struct v4l2_capability cap;

	iErrCode = ioctl(g_iFdCamera, VIDIOC_QUERYCAP, &cap);
	if(OK != iErrCode)
	{
		iErrCode = CAMERA_QUERY_INFO_ERROR;
	}
	else
	{
		printf("driver:%s,Device:%sn",cap.driver,cap.card);
		printf("bus_info:%s,Version:%d.%d.%dn",cap.bus_info,
				cap.version>>16&0xFF,cap.version>>8&0xFF,cap.version&0xFF);
		if(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)
			printf("capability = V4L2_CAP_VIDEO_CAPTURE n");
		if(cap.capabilities & V4L2_CAP_STREAMING)
			printf("capability = V4L2_CAP_STREAMING n");
	}	
	return iErrCode;
}

 我这里放入开启相机接口,打开就行行查询,结果如下:


===================    SELECT FUNCTION     ======================
T_camera_open                                      - 1
T_camera_close                                     - 2
T_camera_query_config                              - 3
T_camera_set_resolution                            - 4
T_camera_set_focus                                 - 5
T_camera_get_frameRate                             - 6
T_camera_set_frameRate                             - 7
T_camera_get_exposure_value                        - 8
T_camera_set_exposure_value                        - 9
T_camera_set_params                                - 10
T_camera_get_params                                - 11
T_camera_map_buffer                                - 12
T_camera_unmap_buffer                              - 13
T_camera_start_grab                                - 14
T_camera_stop_grab                                 - 15
T_camera_get_image                                 - 16
T_camera_stress_test_1                             - 17
T_camera_stress_display                            - 18
UPPER MENU                                         - 0
===================    END PRINT          =======================

Please enter function number: 1

test_id = 1
START T_camera_open -- >>
x▒▒1024y 600, 32bpp  screensize is 2457600
driver:uvcvideo,Device:USB 2.0 Camera
bus_info:usb-nxp-ehci-1.3,Version:3.4.39
capability = V4L2_CAP_VIDEO_CAPTURE
capability = V4L2_CAP_STREAMING
END   test_camera_open -- << OK [ 0x0 ]
END   T_camera_open -- << OK [ 0x0 ]

查询到的驱动版本:driver:uvcvideo,bus_info:usb-nxp-ehci-1.3,Version:3.4.39
查询到的硬件信息:Device:USB 2.0 Camera

查询摄像机参数信息

查询当前设置的分辨率和支持的图像格式,指令如下:

VIDIOC_G_FMTstruct v4l2_format
VIDIOC_ENUM_FMTstruct v4l2_fmtdesc

代码如下:

int camera_query_config(void)
{
    int iErrCode = OK;
	struct v4l2_format fmt;
	struct v4l2_fmtdesc fmtdesc;

	fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	//获取当前摄像头的宽高
	iErrCode = ioctl(g_iFdCamera, VIDIOC_G_FMT, &fmt);
	if(OK == iErrCode)
	{
		g_image_format = fmt.fmt.pix.pixelformat;
		printf("Current data format information:ntwidth:%dntheight:%dn",fmt.fmt.pix.width,fmt.fmt.pix.height);
	}
	else{
		iErrCode = CAMERA_FORMAT_INFO_ERROR;
		printf("get camera width and height failed..n");
	}
	
	if(OK == iErrCode)
	{
		fmtdesc.index=0;
		fmtdesc.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
		//获取当前摄像头支持的格式
		while(ioctl(g_iFdCamera,VIDIOC_ENUM_FMT,&fmtdesc)!=-1)
		{
			//if(fmtdesc.pixelformat & fmt.fmt.pix.pixelformat)
			{
				printf("%dtformat:%sn",fmtdesc.index,fmtdesc.description);
				break;
			}
			fmtdesc.index++;
		}
	}

    return iErrCode;
}

使用查询指令和以获得全部的摄像头参数信息,如下指令:

VIDIOC_QUERYCTRLstruct v4l2_queryctrl 

实际查询结果如下:


UPPER MENU                                         - 0
===================    END PRINT          =======================

Please enter function number: pfb :0xff222831
sh: 1: pause: not found
3

test_id = 3
START T_camera_query_config -- >>
Current data format information:
        width:640
        height:480
0       format:MJPEG
1       format:YUV 4:2:2 (YUYV)
 control 0x00980900 Brightness min -64 max 64 step 1 default 0 current 0.
 control 0x00980901 Contrast min 0 max 64 step 1 default 40 current 40.
 control 0x00980902 Saturation min 0 max 128 step 1 default 64 current 64.
 control 0x00980903 Hue min -40 max 40 step 1 default 0 current 0.
 control 0x0098090c White Balance Temperature, Auto min 0 max 1 step 1 default 1 current 1.
 control 0x00980910 Gamma min 72 max 500 step 1 default 100 current 100.
 control 0x00980913 Gain min 0 max 100 step 1 default 0 current 0.
 control 0x00980918 Power Line Frequency min 0 max 2 step 1 default 1 current 1.
  0: Disabled
  1: 50 Hz
  2: 60 Hz
 control 0x0098091a White Balance Temperature min 2800 max 6500 step 1 default 4600 current 4600.
 control 0x0098091b Sharpness min 0 max 6 step 1 default 2 current 2.
 control 0x0098091c Backlight Compensation min 0 max 121 step 121 default 0 current 121.
 control 0x009a0901 Exposure, Auto min 0 max 3 step 1 default 3 current 3.
 control 0x009a0902 Exposure (Absolute) min 1 max 5000 step 1 default 157 current 157.
 control 0x009a0903 Exposure, Auto Priority min 0 max 1 step 1 default 0 current 1.
END   test_camera_query_config -- << OK [ 0x0 ]
END   T_camera_query_config -- << OK [ 0x0 ]


===================    SELECT FUNCTION     ======================

设置分辨率

分辨率就是宽高,之前查询指令改成设置指令即可,如下:

VIDIOC_S_FMTstruct v4l2_format
//cam_width/cam_hight作为全局变量进行使用
int camera_set_resolution(void)
{
    int iErrCode = OK;
	struct v4l2_format sFmt;
	if(!g_stress_test)
	{
		printf("please input width:");
		scanf("%d",&cam_width);
		printf("please input height:");
		scanf("%d",&cam_hight);
		printf("you input width:%d,height:%dn",cam_width,cam_hight);
	}
	
	sFmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	sFmt.fmt.pix.width       = cam_width;   //用户希望设置的宽
	sFmt.fmt.pix.height      = cam_hight;   //用户希望设置的高
	sFmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;//选择格式:V4L2_PIX_FMT_YUYV或V4L2_PIX_FMT_MJPEG
	sFmt.fmt.pix.field       = V4L2_FIELD_INTERLACED;
	iErrCode = ioctl(g_iFdCamera, VIDIOC_S_FMT, &sFmt);
	if (iErrCode < 0) {
		//printf("VIDIOC_S_FMT failed (%d)n", iErrCode);
		iErrCode = CAMERA_SET_PARAM_ERROR;
	}
	else{
		//如果用户传入超过了实际摄像头支持大小,摄像头会自动缩小成最大支持。这里把摄像头当前支持的宽高情况反馈给用户。
		cam_width = sFmt.fmt.pix.width;
		cam_hight = sFmt.fmt.pix.height;

		printf("------------VIDIOC_S_FMT---------------n");
		printf("Stream Format Informations:n");
		printf(" type: %dn", sFmt.type);
		printf(" width: %dn", sFmt.fmt.pix.width);
		printf(" height: %dn", sFmt.fmt.pix.height);
	}
    return iErrCode;
}

设置帧率

        帧率是每秒传送图片数量,这里的帧率是分母的大小,几每秒传送30帧则为1/30.在实际测试中,不同的摄像头帧率与设置值不一定相等,即便设置值与获取值一致,实际上的帧率值也不一定如此,需要实际测试验证。

指令如下:

VIDIOC_S_PARMstruct v4l2_streamparm sStreamParm;
int camera_set_frameRate(void)
{
	int iErrCode = OK;
	int iFrameRate = 0;
	struct v4l2_streamparm sStreamParm;

	memset(&sStreamParm,0x0,sizeof(struct v4l2_streamparm));
	printf("please input frameRate:");
	scanf("%d",&iFrameRate);
	printf("user input frameRate:%dn",iFrameRate);

	
	sStreamParm.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	sStreamParm.parm.capture.timeperframe.numerator = 1;
	sStreamParm.parm.capture.timeperframe.denominator = iFrameRate;
	iErrCode = ioctl(g_iFdCamera, VIDIOC_S_PARM, &sStreamParm);
	if (iErrCode < 0) {
		printf("set stream params failed (%d)n", iErrCode);
		iErrCode = CAMERA_SET_PARAM_ERROR;
	}
	return 0;
}

映射空间

int Camera::camera_map_buffer(void)
{
    int iErrCode = OK;
    int i = 0;
    struct v4l2_requestbuffers req;
    struct v4l2_buffer tmp_buf ;   //摄像头缓冲buf临时保存buf
    printf("Line:%d,%s,> ()n",__LINE__,__FUNCTION__);
    memset(&req, 0, sizeof (req));
    req.count = CAMERA_BUF_NUM;  //摄像头图片缓存buf个数,这里一般设置4个
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    iErrCode = ioctl(m_iFdCamera,VIDIOC_REQBUFS,&req);
    if (iErrCode <0)
    {
        printf("query image data failed =(%d)",iErrCode);
        iErrCode = CAMERA_QUERY_BUFFER_ERROR;
    }
    if(OK == iErrCode)
    {
        for (i = 0; i < CAMERA_BUF_NUM && OK == iErrCode; ++i)
        {
            memset( &tmp_buf, 0, sizeof(tmp_buf) );
            tmp_buf.type =  V4L2_BUF_TYPE_VIDEO_CAPTURE;
            tmp_buf.memory = V4L2_MEMORY_MMAP;
            tmp_buf.index = i;
            //获取内部buf信息到tmp_buf
            iErrCode = ioctl(m_iFdCamera, VIDIOC_QUERYBUF, &tmp_buf);
            printf("data size = %d n",tmp_buf.length);
            if (iErrCode < 0)
            {
                printf("VIDIOC_QUERYBUF (%d) errorn",i);
                iErrCode = CAMERA_QUERY_BUFFER_ERROR;
            }
            if(OK == iErrCode)
            {
                m_Image_Buffers[i].length = tmp_buf.length;
                m_Image_Buffers[i].offset = (size_t) tmp_buf.m.offset;
                //开始映射
                m_Image_Buffers[i].start = mmap (NULL, tmp_buf.length,PROT_READ | PROT_WRITE, MAP_SHARED, m_iFdCamera, tmp_buf.m.offset);
                if (m_Image_Buffers[i].start == MAP_FAILED)
                {
                    printf("map buffer (%d) errorn",i);
                    iErrCode = CAMERA_MAP_BUFFER_ERROR;
                }
            }
        }
    }
    printf("Line:%d,%s,< (= 0x%x)n",__LINE__,__FUNCTION__,iErrCode);

    return iErrCode;
}

int Camera::camera_unmap_buffer(void)
{
    int i = 0,iErrCode = OK;
    for(i = 0 ; i < CAMERA_BUF_NUM ; i++)
    {
        munmap(m_Image_Buffers[i].start , m_Image_Buffers[i].length);
    }

    return iErrCode ;
}

开始停止采集

VIDIOC_STREAMONV4L2_BUF_TYPE_VIDEO_CAPTURE
VIDIOC_STREAMOFFV4L2_BUF_TYPE_VIDEO_CAPTURE

为了保证进程不被阻塞,同时控制流通道和控制通道,这里开辟现成进行采集处理。代码如下:

int camera_start_grab(void)
{
    int iErrCode = OK;
	pthread_t iB;
	enum v4l2_buf_type type;
	
	if(OK == iErrCode)
	{
		type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
		iErrCode = ioctl (g_iFdCamera, VIDIOC_STREAMON, &type);
		if (iErrCode < 0)
		{
		    printf("start grab failedn");
			iErrCode = CAMERA_START_GRAB_ERROR;
		}
		else
		{
			g_display_thread_exit = false;
			pthread_create(&iB,NULL,func_display,(void*)2);
		}
	}
	return iErrCode;
}
int camera_stop_grab(void)
{
	int iErrCode = OK;
	g_display_thread_exit = true;
	while(g_display_thread_exit)
	{
		usleep(1000);
	}
	printf("stop camera stream off n");
    //停止摄像头
    enum v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    iErrCode = ioctl(g_iFdCamera , VIDIOC_STREAMOFF, &type);
    if(iErrCode != 0)
    {
        printf("stop grab failed");
        iErrCode = CAMERA_STOP_GRAB_ERROR;
    }	
	return iErrCode;
}

 摄像头次采集图像效果:

 

 后续

        后续使用中,对相机显示进行双缓冲显示处理,镜像变换,等等处理后续在补充。包括摄像头畸变校正,采集图像QML显示等。

最后

以上就是美丽吐司为你收集整理的UVC摄像头开发概述UVC简介 IO接口      控制流程接口设计 后续的全部内容,希望文章能够帮你解决UVC摄像头开发概述UVC简介 IO接口      控制流程接口设计 后续所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(64)

评论列表共有 0 条评论

立即
投稿
返回
顶部