V4L2 中文 Made by:鱼在飞(467350479) 个人翻译,转载请申明!
Chapter 3. 输入/输出(Input/Output)
V4L2 API规定了若干个不同的读写设备的方法。驱动和应用程序间进行数据交换必须支持其中的一种。
传统的I/O方法是打开V4L2设备后会自动的调用read()和write()函数。如果驱动没有支持这种方法,任何读写设备的尝试都将失败。
用其他的方法必须事先协商好了。选用通过mmap或者用户空间缓冲区的流式I/O方法,应用程序可以调用VIDIOC_REQBUFS方法。目前,异步I/O方法还没定义呢(如果你有好的建议可以随时关注V4L2的邮件列表)。
video overlay也是一种值得考虑的I/O方法,尽管应用程序并不会直接接收图像数据。调用VIDIOC_S_FMT可以初始化videooverlay。查看4.2节获得更多信息。
通常,确切的说任何一种I/O方法,当然也包括overlay,都有他们对应的fd。唯一相同的是应用程序并不直接和驱动进行数据交换(面板应用,详见1.1节),并且驱动允许使用同一个fd进行连续不间断的视频采集和overlay,主要是为了和V4L以及V4L2的早期版本保持兼容。
VIDIOC_S_FMT和VIDIOC_REQBUFS的允许(切换方法)程度是有限的,但为了简洁,驱动除了通过关闭和重新打开设备来切换I/O方法外无需再支持其它的切换方式。
接下来的几小节会详细讨论各种I/O方法。
3.1. 读和写(Read/Write)
VIDIOC_QUERYCAP方法会返回结构v4l2_capability,而其capabilities域中有个V4L2_CAP_READWRITE标志,如果被设置了,那么输入和输出设备会分别支持read()和write()函数。
驱动可能会借助cpu来拷贝数据,当然他们也可能支持DMA方式,因此该I/0方法并不一定没有其他通过交换缓冲区指针交换数据的方法来的低效。但这仍然属于低级的,原因在于没有传递过像帧计数器或者时间戳这样的元信息(meta-information)。这些个信息是相当必要的,因为他们可以用来确定帧是否被丢弃了或者用来同其他应用程序进行数据流同步。尽管如此,这也是一种最简单的进行I/O的方法,只需要请求少量或者不用进行设置就可交换数据。可以使用如下的命令行技巧来操作(其中vidctrl工具是假想的):
> vidctrl /dev/video--input=0 --format=YUYV --size=352x288
> dd if=/dev/videoof=myimage.422 bs=202752 count=1
使用read()函数进行读设备,write()则进行写。假使驱动要和应用程序交换数据,它必须实现一种I/O方法,但这也不是必要的1。当之处读或者写时,驱动也必须支持select()和poll()2。
3.2. 流式I/O(内存映射)(Streaming I/O(MemoryMapping))
就是说当标志位V4L2_CAP_STREAMING置1时,就会支持这种I/O方法。有俩种streaming方法,应用程序可通过调用VIDIOC_REQBUFS来确定是不是支持内存映射。
streaming是一种I/O方法,它主要是通过和应用程序交换缓冲区指针来交换数据,即不用拷贝数据了。内存映射主要是将设备内存缓冲映射到应用程序的地址空间去。设备内存可以是,就像独立显卡它有自己的存储区吧,这存储区就是设备内存。当然了最要效的还属DMA了。
一个驱动可能支持多集合buffer。通过一独一无二的buffer类型值来辨识每一集合。它们是相互独立的并且可以拥有一个不同类型的数据。必须使用不用的fd来同时访问不同的集合3。
应用程序可以调用VIDIOC_REQBUFS来分配想要的设备内存,只要提供buffer数和buffer类型(例子:V4L2_BUF_TYPE_VIDEO_CAPTURE)。当然上面的ioctl方法也可以用来更改buffer数或者释放分配的内存,如果仍有处于maped状态,它可能不做任何改变。
应用程序可以访问这些buffer之前还必须通过mmap()(这驱动编程中很常见的哦)将这些buffer映射到应用程序的地址空间去。可以用VIDIOC_QUERYBUF来获得设备内存中的这些个buffer地址。mmap()第六和第二个参数是m.offset和length(由结构v4l2_buffer返回),这俩参数可别瞎改。谨记一点,这些个buffer地址是物理地址而不是虚拟地址(内核空间,用户空间;物理地址,虚拟地址,进程地址...不同架构范围是不同的)。资源有限,不用的就munmap()掉吧。