v4l2驱动编写篇第七--控制方法
刚刚完成了这一系列文章的第六部分,我们现在知道如何设置视频设备,并来回传输帧了。然而,有一个众所周知的事实,那就是用户永远也不会满意,不会满足于能从摄像头上看到视频,他们马上就会问我可不可以调参数啊?像亮度、对比度等等。这些参数可以在视频应用中调整,有时也的确会这样做,但是当硬件支持时,在 硬件中进行调整有其优势。比如说亮度调整,如果不这样做的话,可能会丢失动态范围,但是基于硬件的调整可以完整保持传感器可以传递的动态范围。很明显,基于硬件的调整也可以让主机的处理器压力轻些。
现代的硬件中通常都可以在运行的时候调整很多参数。然而现在,在不同设备之间这些参数差别很大。简单的亮 度调整可以直观地设置一个寄存器,也可以需要处理一个没有头绪的矩阵变换。最好是能尽可能多的把诸多细节对应用隐藏,但能隐藏到什么程序却受到很多限制。 一个过于抽象的接口会使硬件的控制无法发挥其极限。
V4L2的控制接口试图使事情尽可能地简单,同时还能完全发挥硬件的功能。它开始于定义一个标准控制名字 的集合,包括 V4L2_CID_BRIGHTNESS,V4L2_CID_CONTRAST,V4L2_CID_SATURATION,还有许多其他的。对于白平衡、 水平,垂直镜像等特性,还提供了一些布尔型的控制。定义的控制ID值的完整列表请见the V4L2 API spec 。还有一个驱动程序特定的控制,但这些,显然,一般只能由专用的应用程序使用。私有的控制从V4L2_CID_PRIVATE_BASE开始往后都是。
1、枚举可控制操作
一种典型的做法,V4L2 API提供一种机制可以让应用可以枚举可用的控制操作。为此,他们要发出最终要实现为驱动中的vidioc_queryctrl()方法的ioctl()调用。
int (*vidioc_queryctrl)(struct file *file, void *private_data, struct v4l2_queryctrl *qc);
驱动通常会用所关心的控制信息来添充qc结构体,或是当控制操作不支持时返回EINVAL,这个结构体有很多个字段:
struct v4l2_queryctrl
{
__u32 id;
enum v4l2_ctrl_type type;
__u8 name[32];
__s32 minimum;
__s32 maximum;
__s32 step;
__s32 default_value;
__u32 flags;
__u32 reserved[2];
};
被查询的控制操作将会通过id传送。作为一个特殊的情况,应用可以通过设定 V4L2_CTRL_FLAG_NEXT_CTRL位的方式传递控制id。当这种情况发生时,驱动会返回关于下一个所支持的控制id的信息,这比应用给出 的ID要高。无论在何种情况下,id都应设为实际上被描述的控制操作的id。
其他所有字段都由驱动设定,用来描述所选的控制操作。控制的数据类型在type字段中给定。这可以是 V4L2_CTRL_TYPE_INTEGER、 V4L2_CTRL_TYPE_BOOLEAN、V4L2_CTRL_TYPE_MENU (针对一组固定的择项) 或V4L2_CTRL_TYPE_BUTTON (针对一些设定时会忽略任何给出的值的控制操作)。
name字段用来描述控制操作;它可以在展现给用户的应用接口中使用。 对于整型的控制来说(仅针对这种控制),minimum和maximum 描述的是控制所实现的值的范围,step 给出的是此范围下的粒度大小。
default_value顾名思义就是默认值 – 仅管他只对整型,布尔型和菜单控制适用。驱动只应在初始化时将控制参数设为默认。至于其它设备参数,他们应该从open()到close()保持不变。结 果,default_value 很可能不是现在的控制参数值。
不可避免地,还有一组值进一步描述控制操作(flags)。V4L2_CTRL_FLAG_DISABLED 意为控制操作不可用,应用应该忽略它。V4L2_CTRL_FLAG_GRABBED 意为控制暂进不可变,可能会是因为另一个应用正在使用它。V4L2_CTRL_FLAG_READ_ONLY 意为可以查看,但不可以改变的控制操作。V4L2_CTRL_FLAG_UPDATE意为调整这个参数可以会对其他控制操作造成影响。 V4L2_CTRL_FLAG_INACTIVE 意为与当前设备配置无关的控制操作。V4L2_CTRL_FLAG_SLIDER 意在暗示应用在表现这个操作的时候可以使用类似于滚动条的接口。
应用可以只是查询几个特别编程过的控制操作,或者他们也想枚举整个集合。对后而来讲,他们会从开始 V4L2_CID_BASE 至V4L2_CID_LASTP1结束,过程中可能会用到V4L2_CTRL_FLAG_NEXT_CTRL标签.
2、枚举菜单操作
对于菜单型的诸多控制操作 (type=V4L2_CTRL_TYPE_MENU)而言, 应用很可能想要枚举可能的值,相关的回调函数是:
int (*vidioc_querymenu)(struct file *file, void *private_data, struct v4l2_querymenu *qm);
v4l2_querymenu 结构体如下:
struct v4l2_querymenu
{
__u32 id;
__u32 index;
__u8 name[32];
__u32 reserved;
};
在输入中,id 是相关的菜单控制操作的ID值,index 某特定菜单ID值的索引值.索引值从0开始,依次递增到vidioc_queryctrl()返回的最大值。驱动会填充菜单项的name字段。reserved字段恒设为0.
3、设置控制操作
一旦应用知道了可用的控制操作,它就很可以开始查询并改变其值。这种情况下相关的结构体是:
struct v4l2_control
{
__u32 id;
__s32 value;
};
要查询某一给定控制操作,应用应将id字段设为对应的控制的ID,并发出一个调用,这个调最终实现为:
int (*vidioc_g_ctrl)(struct file *file, void *private_data, struct v4l2_control *ctrl);
驱动应将值设为当前控制的设定,还要保证它知道这个特定的控制操作并在应用试图查询不存在的控制操作时返回EINVAL,试图访问按键控制时也应返回EINVAL.
一个试图改变控制操作的请求实现为:
int (*vidioc_s_ctrl)(struct file *file, void *private_data, struct v4l2_control *ctrl);
驱动应该验证id,保证其值在允许的区间。如果一切都没有问题的话,就将新值写入硬件。
最后, 值得注意的是,还有一个单独的扩展控制接口 也为V4L2所支持。这个API是一组相当复杂的控制操作。实际上,它的主要应用就是 MPEG编解码的参数。扩展控制可以分门归类, 而且支持64位整型值。其接口与常规的控制接口类似。详见API规范。