Chinaunix首页 | 论坛 | 博客
  • 博客访问: 308097
  • 博文数量: 60
  • 博客积分: 1451
  • 博客等级: 上尉
  • 技术积分: 710
  • 用 户 组: 普通用户
  • 注册时间: 2009-09-23 23:55
文章分类

全部博文(60)

文章存档

2017年(9)

2014年(1)

2013年(1)

2011年(9)

2010年(35)

2009年(5)

我的朋友

分类: 嵌入式

2010-10-09 09:55:08

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) |
给主人留下些什么吧!~~