v4l2驱动编写篇第六A--基本的帧输入输出n
基本的帧输入输出
关于视频驱动的这一系列文章己经更新了好几期,但是我们还没有传输过一帧的视频数据。虽然在这一点上,我们己经了解了足够多的关于格式协定方面的细节,我们可以看一下视频帧是如何在应用和设备之间传输的了。
V4L2 API定义了三种不同的传输视频帧的方法,现在有两种是可以实现的:
read() 和write() 系统调用这种普通的方法. 根据硬件和驱动的不同,这种方法可能会非常慢 -但也不是一定会那样.
将帧直接以视频流的方法送到应用可以访问的缓冲区. 视频流这际上是传输视频数据的最有效的方法;这种接口还允许在图像帧中附带一些其他信息. 视频流的方法有两种变种,其分别在于缓冲的开辟是在用户空间还是内核空间。
Video4Linux2 API规范提供一种异频的输入输出机制用于帧的传输。然而这种模式还没有实现,因此不能使用。
这一篇将关注的是简单的read()和write()接口,视频流的方式将在下一期来讲解。
read() 和 write()
Video4Linux2 规范并没有规定要实现read()和write(),然而很多简单的应用希望这种系统调用可用,所以可能的话,驱动的作者应该使之工作。如果驱动没有实现这些系统调用,它应该在保证V4L2_CAP_READWRITE置位,来回应VIDIOC_QUERYCAP调用。然而以笔者的经验,多数的应用在使用调用之前,根本就不会是费心查看调用是否可用。
驱动的read()和/或write()方法必须存在相关的video_device结构中的fops字段里。注意:V4L2规范要求实现这些方法,从而也提供poll()操作。
在一下视频捕获设备上实现read()操作是非常直接的:驱动告诉硬件开始捕获帧,发送一帧到用户空间缓冲,然后关停硬件并返回。如果可能的话,驱动应该安排DMA操作直接将数据传送到目的缓冲区,但这种方式只有在控制器可以处理分散/聚集I/O的时候可能。否则,驱动应该在内核里启用帧缓冲区。同样,写操作也是尽可能直接传到设备,否则启用帧缓冲区。
不那么简单的操作也是可以的。例如笔者的”cafe”驱动会在read()操作后让摄像头控制器工作在一种投机的状态,在一秒钟的下一部分,摄像头中的后续帧将会存储在内核的缓冲区中,如果应用发出了另一个读的调用,它将会更快的反应,无续再次启动硬件。经过一定数目的帧都没有读的话,控制器就会被放回空闲的状态。同理,写操作时,也会廷时几十毫秒,意在帮助应用与硬件帧同步。
流参数
VIDIOC_G_PARM 和VIDIOC_S_PARM ioctl()系统调用会调整一些read(),write()专用的参数,其中一些更加普便。它看起来像是一个设置没有明显归属的杂项的调用。我们在这里就了解一下,虽然有些参数同时会影响流输入输出的参数。
支持这些调用的Video4Linux2驱动提供如下两个方法:
int (*vidioc_g_parm) (struct file *file, void *private_data,
struct v4l2_streamparm *parms);
int (*vidioc_s_parm) (struct file *file, void *private_data,
struct v4l2_streamparm *parms);
v4l2_streamparm结构包含下面的联合,这一系列文章的读者到现在应该对它们己经很熟悉了。
struct v4l2_streamparm
{
enum v4l2_buf_type type;
union
{
struct v4l2_captureparm capture;
struct v4l2_outputparm output;
__u8 raw_data[200];
} parm;
};
type字段描述的是在涉及的操作的类型。对于视频捕获设备,应该为V4L2_BUF_TYPE_VIDEO_CAPTURE。对于输出设备应该为V4L2_BUF_TYPE_VIDEO_OUTPUT。它的值也可以是V4L2_BUF_TYPE_PRIVATE,在这种情况下,raw_data字段用来传递一些私有的,不可移植的,甚至是不鼓励的数据给内核 。
对于捕获设备而言,parm.capture字段是要关注的内容,这个结构体如下:
struct v4l2_captureparm
{
__u32 capability;
__u32 capturemode;
struct v4l2_fract timeperframe;
__u32 extendedmode;
__u32 readbuffers;
__u32 reserved[4];
};
capability字段是一组功能标签。目前为止已经定义的只有一个V4L2_CAP_TIMEPERFRAME,它代表可以改变帧频率。capturemode也是一个只定义了一个标签的字段:V4L2_MODE_HIGHQUALITY,这个标签意在使硬件在高清模式下工作,实现单帧的捕获。这个模式可以做出任何的牺牲(包括支持的格式,曝光时间等),以达到设备可以处理的最佳图片质量。
timeperframe字段用于指定想要使用的帧频率,它又是一个结构体:
struct v4l2_fract {
__u32 numerator;
__u32 denominator;
};
numerator 和denominator所描述的系数给出的是成功的帧之间的时间间隔。另一个驱动相关的字段是:extendedmode,它在API中没有明确的意义。readbuffers字段是read()操作被调用时内核应为输入的帧准备的缓冲区数量。
对于输出设备,其结构体如下:
struct v4l2_outputparm
{
__u32 capability;
__u32 outputmode;
struct v4l2_fract timeperframe;
__u32 extendedmode;
__u32 writebuffers;
__u32 reserved[4];
};
capability, timeperframe, 和 extendedmode字段与捕获设备中的意义相同。outputmode 和writebuffers与capturemode 和readbuffers对应相同。
当应用想要查询现在的参数时,它会发出一个VIDIOC_G_PARM调用,因而调用驱动的vidioc_g_parm()方法。驱动应该提供现在的设置,不用的话确保将extendedmode设为0,并且把reserved字段永远设为0.
设置参数将调用vidioc_s_parm()。在这种情况下,驱动将参数设为与应用所提供的参数尽可能近的值,并调整v4l2_streamparm结构体以反应现行使用的值 。例如,应用可以会请求一个比硬件所能提供的更高的帧频率,在这种情况下,帧频率会设为最高,并把imeperframe设为这个最高的帧频率。
如果应用提供timeperframe为0,the driver should program the nominal frame rate associated with the current video norm.(这句不懂)。如果readbuffers 或writebuffers是0,驱动应返回现行设置而不是删除缓冲区。
到现在为止,我们已经可以写一个支持read()和write()方式帧传输的简单的驱动了。然而多数正式的应用要使用流输入输出方式:流的方式使高性能变得更简单;帧可以打包带上附加信息如帧序号,请继续关注本系列的下篇文章,我们将讨论如何在视频驱动中实现流API.
阅读(2156) | 评论(0) | 转发(0) |