这是不定期发布的关于写视频驱动程序的LWN系统文章的第四篇.没有看过介绍篇的,也许想从这里开始.本周的文章介绍的是应用程序如何确定在特定适配器上哪些输入和输出可用,并且它们之间做出选择。
在很多情况下,视频适配器并不能提供很多的输入输出选项.比如说摄像头控制器,可能只是提供摄像头,而没什么别的功能.然而,在一些其他的情况下,事情将变得很复杂.一个电视卡可能对应板上不用的接头有不同的输入.他甚至可能有可以独立发挥其功能的多路调谐器.有时,那些输入会有不同的特性;有些调谐器可以支持比其他的更广泛的视频标准.对于输出来说,也有同样的问题.
很明显,若想一个应用可以有效地利用视频适配器,它必须有能力找到可用的输入和输出,而且他必须能找到他想操作的那一个.为此,Video4Linux2 API提供三种不同的ioctl()调用来处理输入,相应地有三个来处理输出.
这三个(对于硬件支持的每一个功能)驱动都要支持.虽然如此,对于简单的硬件而言,代码还是很简单的.驱动也要提供一此启动时的默认值.然而,驱动不应该做的是,在应用退出时重置输入输出信息.对于其他视频参数,在多次打开之间,参数应维持不变.
视频标准在我们进入输入输出的细节之前,我们应该先了解一下视频标准 .这些标准描述的是视频为进行传输而做出的格式转换–分辨率,帧频率等.这些标准通常是由每一个国家的监管部门制定的。现在世界上使标准主要的有三个:NTSC(主要是北美使用),PAL(主要是欧洲,非洲和中国),和SECAM(法,俄和非洲部分地区).然而这在标准在国家之间都有变化,而且有些设备比其他设备能更加灵活,能与更多的标准变种协同工作.
V4L2使用v4l2_std_id来代表视频标准,它是一个64位的掩码。每个标准变种在掩码中就是一位。所以标准NTSC就是V4L2_STD_NTSC_M, 值为0x1000,而日本的变种就是V4L2_STD_NTSC_M_JP
(0x2000)。如果一个设备可以处理所以有NTSC变种,它就可以设为V4L2_STD_NTSC,它可以所有相关位置位。对PAL和SECAM标准,也存在一组类似的位集。Seethis pagefor a complete list.
对于用户空间而言,V4L2提供一个ioctl()命令(VIDIOC_ENUMSTD),它允许应用查询设备实现了哪些标准。驱动却无需直接回答查询,而是将video_device结构体的tvnorm字段设置为它所支持的所有标准。然后V4L2层会向应用输出所支持的标准。VIDIOC_G_STD命令,可以用来查询现在哪种标准是激活的,它也是在V4L2层通过返回video_device结构的current_norm字段来处理的,驱动程序应在启动时,初始化current_norm来反应现实情况。有些应用即使他并没有设置过标准,发现标准没有设置也会感到困惑。
当某个应用想要申请某个标准的时候,会发出一个VIDIOC_S_STD调用,该调用通过下面的函数传到驱动:
int (*vidioc_s_std) (struct file *file, void *private_data, v4l2_std_id std);驱动要对硬件编程,以使用给定的标准,并返回0(或是负的出错编码).V4L2层需要把current_norm设为新的值。
应用可能想要知道硬件所看到的是何种信号,答案可以通过VIDIOC_QUERYSTD找到,它到了驱动里面就是:
int (*vidioc_querystd) (struct file *file, void *private_data, v4l2_std_id *std);驱动要尽可能地在这个字段填写详细信息。如果硬件没有提供足够的信息,std字段就会暗示任何可能出现的标准。
这里还有一点值得一提:所以的视频设备必须支持(或是声明支持)至少一种视频标准。视频标准对于摄像头来说没什么意义,它不与任何监管制度绑定。但是也不存一个标准说“我是个摄像头,我什么都能做”,所以V4L2层有很多摄像头声明可以返回PAL或NTSC数据(实际只是如些声明而己)。
输入视频捕获的应用首先要通过VIDIOC_ENUMINPUT
命令来枚举所有可用的输入。在V4L2层,这个调用会转换成调用一个驱动中对应的回调函数:
在这个调用中,file 对就的是打开的视频设备。private_data是驱动的私有字段,input字段是真正的传递的信息,它有如下几个值得关注的字段:
通常驱动会设置上面所以的字段,并返回0。如果索引值超出支持的输入范围,应该返回-EINVAL.这个调用里可能出现的错误不多。
当应用想改变现行的输入时,驱动会收到一个对回调函数vidioc_s_input()的调用。
int (*vidioc_s_input) (struct file *file, void *private_data, unsigned int index);index的值与上面讲到的意义相同 – 它相来确定哪个输入是相要的.驱动要对硬件编辑,选择那个输入并返回0.也有可能要返回-EINVAL
(索引号不正确时) 或-EIO
(硬件有问题). 即使只有一路输入,驱动也要实现这个回调函数.
还有另一个回调函数,指示哪一个输入是激活状态的:
int (*vidioc_g_input) (struct file *file, void *private_data, unsigned int *index);这里驱动把index值设为对应的输入的索引号.
输出枚举和选择输出的过程与输入的是十分相似的。所以这里的描述就从简。输入枚举的回调函数是这样的:
int (*vidioc_enumoutput) (struct file *file, void *private_data struct v4l2_output *output);v4l2_output
结构的字段是:
也有用于获得和设定现行输入设置的回调函数;他们与输入的具体对称性:
int (*vidioc_g_output) (struct file *file, void *private_data, unsigned int *index); int (*vidioc_s_output) (struct file *file, void *private_data, unsigned int index);即便只有一个输出,设备驱动也要定义所有上述的三个回调函数.
有了这些函数之后,V4L2应用就可以知道有哪些输入和输入,并在它们间进行选择.然而选译这些输入输出中所传输的是什么视频数据流则是一件更加复杂的事情.本系列的下一期文章,我们将关注视频数据流的格式,以及如何与用户空间协定数据格式.