• (十一) UVC调节亮度



    title: UVC调节亮度
    date: 2019/4/23 20:30:00
    toc: true

    UVC调节亮度

    引入

    摄像头的参数比如亮度等是通过VC接口控制的,具体可以参考APP的调用流程,这里暂时不分析了

    xawtv.c:
        grabber_scan
            ng_vid_open
                v4l2_driver.open // v4l2_open
                    get_device_capabilities(h);
                        // 调用VIDIOC_QUERYCTRL ioctl确定是否支持某个属性
                        /* controls */
                        for (i = 0; i < MAX_CTRL; i++) {
                    	h->ctl[i].id = V4L2_CID_BASE+i;
                    	if (-1 == xioctl(h->fd, VIDIOC_QUERYCTRL, &h->ctl[i], EINVAL) ||
                    	    (h->ctl[i].flags & V4L2_CTRL_FLAG_DISABLED))
                    	    h->ctl[i].id = -1;
                        }
    怎么去获得/设置属性?
    看drv0-v4l2.c
    可见这2个函数:
    v4l2_read_attr  : VIDIOC_G_CTRL
    v4l2_write_attr : VIDIOC_S_CTRL
    

    直接说结论

    ioctl中的VIDIOC_QUERYCTRL来查询是支持的属性,VIDIOC_G_CTRL,VIDIOC_S_CTRL来读取设置具体的属性

    硬件协议速览

    亮度设置等属性是归属于PU的,我们找到uvc规范中的Processing Unit Descriptor

    mark

    这里有一个3字节的bmControls指示了所有的属性,1表示支持这个属性,0表示不支持.我们之前读取联合接口描述符可以分析出PU是支持亮度调节的

    mark

    代码框架

    对于硬件的描述,程序在uvc_ctrl.c > uvc_ctrls也描述了这个

    • SU,PU,CT,IT等都抽象为entity,这使用16个字节的GUID标识
    • selector标识了具体的属性,index则指出了在实际的数据位,在亮度中为bit0,这里index=0
    • size标识了数据的大小,单位为字节,亮度为两个字节,有些属性可能为11位,那么他也占据两个字节,具体有11位在下面另外一个结构体uvc_ctrl_mappings指出
    • flags表示可读可写等
    static struct uvc_control_info uvc_ctrls[] = {
    	{
    		.entity		= UVC_GUID_UVC_PROCESSING,
    		.selector	= UVC_PU_BRIGHTNESS_CONTROL,
    		.index		= 0,
    		.size		= 2,
    		.flags		= UVC_CTRL_FLAG_SET_CUR
    				| UVC_CTRL_FLAG_GET_RANGE
    				| UVC_CTRL_FLAG_RESTORE,
    	},
    	.....
        {
    		.entity		= UVC_GUID_UVC_PROCESSING,
    		.selector	= UVC_PU_ANALOG_LOCK_STATUS_CONTROL,
    		.index		= 17,
    		.size		= 1,
    		.flags		= UVC_CTRL_FLAG_GET_CUR,
    	},
        //===================================================================//
    	{
    		.entity		= UVC_GUID_UVC_CAMERA,
    		.selector	= UVC_CT_SCANNING_MODE_CONTROL,
    		.index		= 0,
    		.size		= 1,
    		.flags		= UVC_CTRL_FLAG_SET_CUR | UVC_CTRL_FLAG_GET_CUR
    				| UVC_CTRL_FLAG_RESTORE,
    	},
    
    

    这里是另外更详细的一些描述

    • size标识了使用了上述字节数中的多少位
    • offset 则是偏移量
    • v4l2_type提供给APP这是什么数据类型,比如菜单,滑动条等
    • data_type则是数据的类型
    static struct uvc_control_mapping uvc_ctrl_mappings[] = {
    	{
    		.id		= V4L2_CID_BRIGHTNESS,
    		.name		= "Brightness",
    		.entity		= UVC_GUID_UVC_PROCESSING,
    		.selector	= UVC_PU_BRIGHTNESS_CONTROL,
    		.size		= 16,
    		.offset		= 0,
    		.v4l2_type	= V4L2_CTRL_TYPE_INTEGER,
    		.data_type	= UVC_CTRL_DATA_TYPE_SIGNED,
    	},
    ....
        
    enum v4l2_ctrl_type {
    	V4L2_CTRL_TYPE_INTEGER	     = 1,
    	V4L2_CTRL_TYPE_BOOLEAN	     = 2,
    	V4L2_CTRL_TYPE_MENU	     = 3,
    	V4L2_CTRL_TYPE_BUTTON	     = 4,
    	V4L2_CTRL_TYPE_INTEGER64     = 5,
    	V4L2_CTRL_TYPE_CTRL_CLASS    = 6,
    	V4L2_CTRL_TYPE_STRING        = 7,
    	V4L2_CTRL_TYPE_BITMASK       = 8,
    };
       
    /* Data types for UVC control data */
    #define UVC_CTRL_DATA_TYPE_RAW		0
    #define UVC_CTRL_DATA_TYPE_SIGNED	1
    #define UVC_CTRL_DATA_TYPE_UNSIGNED	2
    #define UVC_CTRL_DATA_TYPE_BOOLEAN	3
    #define UVC_CTRL_DATA_TYPE_ENUM		4
    #define UVC_CTRL_DATA_TYPE_BITMASK	5
       
    

    属性初始化

    uvc_drvier.c > uvc_ctrl_init_device 中会通过USB读取到具体支持哪些属性,并构造一个结构去管理.

    最终目的就是实现

    1. 读取具体的属性支持,构造uvc_control

    2. 将之前统一设置的uvc_ctrls(包含了具体的属性是否可读的信息等),挂载到上述构造的结构中

      memcpy(&ctrl->info, info, sizeof(*info))
      //>ctrl->info = 某个uvc_control_info数组项
      

    详细的流程如下

    uvc_ctrl_init_device
    	// 遍历所有的 pu/eu/ct 
    	list_for_each_entry(entity, &dev->entities, list) {
    		struct uvc_control *ctrl;
    		// 取出具体的数据,数据size
    		bmControls = entity->processing.bmControls;
    		bControlSize = entity->processing.bControlSize;
    		
    		// 统计支持的属性个数
    		for (i = 0; i < bControlSize; ++i)
    			ncontrols += hweight8(bmControls[i]);
    			
    		// 一次性分配n个属性控制结构,到 uvc_control
            entity->controls = kcalloc(ncontrols, sizeof(*ctrl),GFP_KERNEL);
            ctrl = entity->controls;
            
            // 关联这个 uvc_control(这个刚构造的属性描述) 和 uvc_control_info类型的 uvc_ctrls (统一的描述)
            uvc_ctrl_init_ctrl
            	// 查询guid 和 index 一致 则关联
            	if (uvc_entity_match_guid(ctrl->entity, info->entity) &&ctrl->index == info->index) {
    				uvc_ctrl_add_info(dev, ctrl, info); 
               //这里有个关键函数 uvc_control_mapping.get/set 为这个  control 结构具体的数据转换函数   
                __uvc_ctrl_add_mapping(dev, ctrl, mapping);  
                    if (map->get == NULL)
                        map->get = uvc_get_le_value;
                    if (map->set == NULL)
                        map->set = uvc_set_le_value;
    
                	
    

    属性支持查询

    ioctl入手

    uvc_v4l2_do_ioctl
    	case VIDIOC_QUERYCTRL:
    		uvc_query_v4l2_ctrl(chain, arg)
                uvc_find_control(chain, v4l2_ctrl->id, &mapping)
                	//遍历 entity
                	list_for_each_entry(entity, &chain->entities, chain)
                		//寻找到一个详细的描述
                		// 这里有指示这个 unit 的详细信息,这个mapping就是
                		// static struct uvc_control_mapping uvc_ctrl_mappings[]
                		__uvc_find_control(entity, v4l2_id, mapping, &ctrl, next)
    

    也就是说存在这么个流程

    1. 设备插入后初始化属性管理结构,这个管理结构会挂载uvc_control_info uvc_ctrls,指示了可读可写等信息
    2. 查询属性的时候,可以根据这个找到更详细的uvc_control_mapping uvc_ctrl_mappings[],指示了具体的数据位宽和默认值等

    具体属性值获取

    VIDIOC_G_CTRL
    	uvc_ctrl_get(chain, &xctrl);
    		// 属性查询,了解具体的操作格式
    		uvc_find_control
    		// 发起usb传输 获取最大最小值,存到 ctrl->uvc_data  这个是在初始化后分配的内存
            	uvc_ctrl_populate_cache (UVC_GET_DEF,UVC_GET_MIN,UVC_GET_MAX)
            		uvc_query_ctrl
            			usb_control_msg
    		// 传递给usb
    		uvc_query_ctrl
    			__uvc_query_ctrl
    				usb_control_msg
    		// ...
    		uvc_ctrl_rollback(chain);
    

    具体属性值设置

    VIDIOC_S_CTRL
    	uvc_ctrl_set
    		// 属性查询
    		uvc_find_control
    			
    			// 发起usb传输 获取最大最小值,存到 ctrl->uvc_data  这个是在初始化后分配的内存
    			if (!ctrl->cached) {	//这里如果在读属性的时候获取过就不再发起usb传输获取了
    				ret = uvc_ctrl_populate_cache(chain, ctrl);
    			
    			//数据转换
    			min,max,step=get(uvc_ctrl_data..)
    			// 传输usb
    			uvc_query_ctrl
    				__uvc_query_ctrl
    					usb_control_msg
    	uvc_ctrl_commit
    

    代码实现

    这里代码的实现难点还是在usb_control_msg的参数确定了,摘录自这里

    • usb_control_msg()
      功能:发送一个简单的控制消息到指定的端点,并等待消息完成或超时;
      参数:
        dev:指向控制消息所发送的目标USB设备(usb_device)的指针; <这里是在probe()里获取的my_uvc_udev>
        pipe:控制消息所发送的目标USB设备的特定端点,调用usb_sndctrlpipe(把指定USB设备的指定端点设置为一个控制OUT端点)或usb_rcvctrlpipe(把指定USB设备的指定端点设置为一个控制IN端点)来创建的; <这里把my_uvc_udev设置为接收端点>
        request:控制消息的USB请求值; <这里分别是需要的GET_MIN、GET_MAX、GET_RES、GET_DEF>
        requesttype:控制消息的USB请求类型值; <这里为USB_TYPE_CLASS(1<<5)、usb_recip_interface(1<<0)、usb_dir_in(1<<7)>
          D7:数据的传输方向:0表示从主机到设备;1表示从设备到主机;
          D6~5:命令的类型:0表示标准命令;1表示类命令;2表示厂商提供的命令;3保留;
          D4~0:接收对象:0表示设备; 1表示接口;2表示端点;3表示其他;
        value:控制消息的USB消息值; <这里是PU亮度控制>
        index:控制消息的USB消息索引值;<这里是PU对应的ID和控制接口>
        data:指向要发送/接收的数据的指针; <这里是接收数据>
        size:data参数所指缓冲区的大小; <这里是两字节,bControlSize=2>
        timeout:以msecs为单位,期望等待的超时时间,如果为0,该函数将一直等待消息结束以的时间;<这里是5s>
      返回值:
        成功返回接收/发送的字节数,否则返回负的错误值;

    实例代码如下

    /* 查询/获得/设置属性 
    .vidioc_queryctrl     = myuvc_vidioc_queryctrl,
    .vidioc_g_ctrl        = myuvc_vidioc_g_ctrl,
    .vidioc_s_ctrl        = myuvc_vidioc_s_ctrl,
    */
    
    // uvc_get_le_value  uvc_set_le_value
    static void myuvc_set_le_value(__s32 value, __u8 *data);
    static __s32 myuvc_get_le_value(const __u8 *data);
    
    
    /* 参考:uvc_query_v4l2_ctrl */    
    int myuvc_vidioc_queryctrl (struct file *file, void *fh,
                    struct v4l2_queryctrl *ctrl)
    {
    	__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
    	unsigned int pipe;
        int ret;
        u8 data[2];
    
        if (ctrl->id != V4L2_CID_BRIGHTNESS)
            return -EINVAL;
        
    	memset(ctrl, 0, sizeof *ctrl);
    	ctrl->id   = V4L2_CID_BRIGHTNESS;
    	ctrl->type = V4L2_CTRL_TYPE_INTEGER;
    	strcpy(ctrl->name, "MyUVC_BRIGHTNESS");
    	ctrl->flags = 0;
    
    	pipe = usb_rcvctrlpipe(myuvc_udev, 0);
    	type |= USB_DIR_IN;
    
        /* 发起USB传输确定这些值 */
    	ret = usb_control_msg(myuvc_udev, pipe, GET_MIN, type, PU_BRIGHTNESS_CONTROL << 8,
    			ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
        if (ret != 2)
            return -EIO;
    	ctrl->minimum = myuvc_get_le_value(data);	/* Note signedness */
    
    
    	ret = usb_control_msg(myuvc_udev, pipe, GET_MAX, type,  PU_BRIGHTNESS_CONTROL << 8,
    			ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
        if (ret != 2)
            return -EIO;
    	ctrl->maximum = myuvc_get_le_value(data);	/* Note signedness */
    
    	ret = usb_control_msg(myuvc_udev, pipe, GET_RES, type, PU_BRIGHTNESS_CONTROL << 8,
    			 ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
        if (ret != 2)
            return -EIO;
    	ctrl->step = myuvc_get_le_value(data);	/* Note signedness */
    
    	ret = usb_control_msg(myuvc_udev, pipe, GET_DEF, type, PU_BRIGHTNESS_CONTROL << 8,
    			ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
        if (ret != 2)
            return -EIO;
    	ctrl->default_value = myuvc_get_le_value(data);	/* Note signedness */
    
        printk("Brightness: min =%d, max = %d, step = %d, default = %d
    ", ctrl->minimum, ctrl->maximum, ctrl->step, ctrl->default_value);
        
        return 0;
    }
    
    /* 参考 : uvc_ctrl_get */
    int myuvc_vidioc_g_ctrl (struct file *file, void *fh,
                    struct v4l2_control *ctrl)
    {
    	__u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
    	unsigned int pipe;
        int ret;
        u8 data[2];
        
        if (ctrl->id != V4L2_CID_BRIGHTNESS)
            return -EINVAL;
    
    	pipe = usb_rcvctrlpipe(myuvc_udev, 0);
    	type |= USB_DIR_IN;
    
    	ret = usb_control_msg(myuvc_udev, pipe, GET_CUR, type, PU_BRIGHTNESS_CONTROL << 8,
    			ProcessingUnitID << 8 | myuvc_control_intf, data, 2, 5000);
        if (ret != 2)
            return -EIO;
    	ctrl->value = myuvc_get_le_value(data);	/* Note signedness */
        
        return 0;
    }
    
    /* 参考: uvc_ctrl_set/uvc_ctrl_commit */
    int myuvc_vidioc_s_ctrl (struct file *file, void *fh,
                    struct v4l2_control *ctrl)
    {
        __u8 type = USB_TYPE_CLASS | USB_RECIP_INTERFACE;
        unsigned int pipe;
        int ret;
        u8 data[2];
        
        if (ctrl->id != V4L2_CID_BRIGHTNESS)
            return -EINVAL;
    
        myuvc_set_le_value(ctrl->value, data);
    
        pipe = usb_sndctrlpipe(myuvc_udev, 0);
        type |= USB_DIR_OUT;
    
        ret = usb_control_msg(myuvc_udev, pipe, SET_CUR, type, PU_BRIGHTNESS_CONTROL << 8,
                ProcessingUnitID  << 8 | myuvc_control_intf, data, 2, 5000);
        if (ret != 2)
            return -EIO;
        
        return 0;
    }
    
  • 相关阅读:
    dayjs:js时间插件
    IE:IE请求路径带中文报错
    layui:下载表格为excel文件
    VUE:导出表格为excel文件
    报错:unexpected trailing comma
    node.js报错address not available 192.168.1.4
    js防抖和节流
    mongodb的常用基础命令及操作
    mongodb下载安装及环境搭建
    vue页面缓存
  • 原文地址:https://www.cnblogs.com/zongzi10010/p/10764261.html
Copyright © 2020-2023  润新知