V4L2文档翻译(十二)

http://linuxtv.org/downloads/v4l-dvb-apis/io.html

第三节:输入和输出

V4L2 API定义了一些不同的方法来从设备读取或写入,所有需要与应用程序交换数据的驱动最少必须支持其中之一。

在打开一个V4L2设备后会自动选择使用经典的I/O方法read()和write(),当驱动不支持写或读时会失败。

其他的方法必须通过协商。应用程序通过VIDIOC_REQBUFS ioctl来选择内存映射或用户缓存的I/O流,I/O同步方法未定义。

尽管应用程序不直接接受图片数据,视频overlay也可被考虑做其他I/O方法。初始化视频overlay是通过VIDIOC_S_FMT ioctl,其他信息见“视频Overlay接口”章节。

通常一个I/O方法,包括overlay,是与每一个文件描述符关联的。只是以下情况是例外,若应用程序不与驱动进行数据交换(表盘程序,见“打开关闭设备”章节),以及驱动准许使用同一个文件描述符进行视频捕捉和overlay,这是为了对V4L和早期版本的V4L2兼容。

VIDIOC_S_FMT和VIDIOC_REQBUFS会在某方面允许这种情况,但是?对于关闭和重打开设备来说简单驱动不需要支持I/O方法切换(第一次切换read/write后)。之后的小节描述了各种I/O方法的细节。

读/写

若VIDIOC_QUERYCAP ioctl返回的struct v4l2_capability中capabilities包含了V4L2_CAP_READWRITE时,输入和输出设备需要分别支持read()和write()函数。

驱动可能需要CPU进行数据拷贝操作,但是他们也可以通过支持DMA来处理用户内存,所以此I/O方法并不一定比其他只是交换缓冲区指针的方法效率低。因为像帧计数器或时间戳传输的无元数据的一些类型,所以它不优先考虑,此信息对于识别掉帧和与其他数据流同步来说很有必要。无论怎样,这是个简单的IO方法,需要很少甚至不需要任何的设置来交换数据。它允许在命令行中使用(vidvtrl工具的虚构的):

> vidctrl /dev/video --input=0 --format=YUYV --size=352x288
> dd if=/dev/video of=myimage.422 bs=202752 count=1

应用程序使用read()函数从设备中读取,使用write()函数对设备进行写入,若驱动与应用程序交换数据则必须声明一个I/O方法,但是不一定像是这样。若读取或写入支持了,那么驱动还必须支持select()和poll()函数。

在驱动等级select()和poll()是一样的,select()是非常重要的选项。

I/O流 (内存映射)

若VIDIOC_QUERYCAP ioctl后返回的struct v4l2_capability中capabilities包含了V4L2_CAP_STREAMING标志则输入、输出设备要支持这个I/O方法。有两种流方法,应用程序通过VIDIOC_REQBUFS ioctl决定内存映射特性是否支持。

应用程序和驱动只交换缓存指针,而数据本身并不被拷贝的I/O方法叫做“流”。内存映射即是将设备内存中的缓存映射到应用程序的地址空间去,设备内存可以是比如做视频捕捉等时显卡上的视频内存。无论如何,作为长久以来效率最高的I/O方法,非常多的驱动支持流,他们将在可DMA操作的主内存中申请缓存。

一个驱动可支持许多套缓存,每一套通过一个独一无二的缓存类型值定义。他们都是互相独立的,而且每一套缓存处理不同的数据类型。要同时访问不同的缓存必须通过使用不同的文件描述符。

应用程序通过VIDIOC_REQBUFS ioctl申请设备缓存,同时要带入需求的缓存数量和缓存类型,比如V4L2_BUF_TYPE_VIDEO_CAPTURE。这个ioctl也可以用来改变缓存数量或释放已申请的内存,对已映射的缓存无效。

应用程序在操作缓存前必须要通过mmap()函数将他们映射到应用程序地址空间中,而所映射的缓存在设备内存中具体哪个位置是由VIDIOC_QUERYBUF ioctl决定的。在单一平面API中,返回的struct v4l2_buffer中m.offset和length成员用作mmap()函数的第六个和第二个参数。多平面API中,struct v4l2_buffer结构体包含了struct v4l2_plane结构体集合,每一个都包含了各自的,m.offset和length。当使用多平面API时,每个缓存冲的每个平面都需要分别映射,所以调用mmap()的次数就等于缓存数乘以每个缓存内的平面数量。offset和length的值必须不能修改。切记,缓存被分配在物理内存中,不同于虚拟内存的是,可以与硬盘做交换。应用程序在执行了munmap()函数后要尽快释放缓存。

例3.1 单平面API中的缓存映

struct v4l2_requestbuffers reqbuf;
struct {
    void *start;
    size_t length;
} *buffers;
unsigned int i;

memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 20;

if (-1 == ioctl (fd, VIDIOC_REQBUFS, &reqbuf)) {
    if (errno == EINVAL)
        printf("Video capturing or mmap-streaming is not supported\n");
    else
        perror("VIDIOC_REQBUFS");

    exit(EXIT_FAILURE);
}

/* We want at least five buffers. */

if (reqbuf.count < 5) {
    /* You may need to free the buffers here. */
    printf("Not enough buffer memory\n");
    exit(EXIT_FAILURE);
}

buffers = calloc(reqbuf.count, sizeof(*buffers));
assert(buffers != NULL);

for (i = 0; i < reqbuf.count; i++) {
    struct v4l2_buffer buffer;

    memset(&buffer, 0, sizeof(buffer));
    buffer.type = reqbuf.type;
    buffer.memory = V4L2_MEMORY_MMAP;
    buffer.index = i;

    if (-1 == ioctl (fd, VIDIOC_QUERYBUF, &buffer)) {
        perror("VIDIOC_QUERYBUF");
        exit(EXIT_FAILURE);
    }

    buffers[i].length = buffer.length; /* remember for munmap() */

    buffers[i].start = mmap(NULL, buffer.length,
                PROT_READ | PROT_WRITE, /* recommended */
                MAP_SHARED,             /* recommended */
                fd, buffer.m.offset);

    if (MAP_FAILED == buffers[i].start) {
        /* If you do not exit here you should unmap() and free()
           the buffers mapped so far. */
        perror("mmap");
        exit(EXIT_FAILURE);
    }
}

/* Cleanup. */

for (i = 0; i < reqbuf.count; i++)
    munmap(buffers[i].start, buffers[i].length);

例3.2 多平面API中的缓存映射

struct v4l2_requestbuffers reqbuf;
/* Our current format uses 3 planes per buffer */
#define FMT_NUM_PLANES = 3

struct {
    void *start[FMT_NUM_PLANES];
    size_t length[FMT_NUM_PLANES];
} *buffers;
unsigned int i, j;

memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
reqbuf.memory = V4L2_MEMORY_MMAP;
reqbuf.count = 20;

if (ioctl(fd, VIDIOC_REQBUFS, &reqbuf) < 0) {
    if (errno == EINVAL)
        printf("Video capturing or mmap-streaming is not supported\n");
    else
        perror("VIDIOC_REQBUFS");

    exit(EXIT_FAILURE);
}

/* We want at least five buffers. */

if (reqbuf.count < 5) {
    /* You may need to free the buffers here. */
    printf("Not enough buffer memory\n");
    exit(EXIT_FAILURE);
}

buffers = calloc(reqbuf.count, sizeof(*buffers));
assert(buffers != NULL);

for (i = 0; i < reqbuf.count; i++) {
    struct v4l2_buffer buffer;
    struct v4l2_plane planes[FMT_NUM_PLANES];

    memset(&buffer, 0, sizeof(buffer));
    buffer.type = reqbuf.type;
    buffer.memory = V4L2_MEMORY_MMAP;
    buffer.index = i;
    /* length in struct v4l2_buffer in multi-planar API stores the size
     * of planes array. */
    buffer.length = FMT_NUM_PLANES;
    buffer.m.planes = planes;

    if (ioctl(fd, VIDIOC_QUERYBUF, &buffer) < 0) {
        perror("VIDIOC_QUERYBUF");
        exit(EXIT_FAILURE);
    }

    /* Every plane has to be mapped separately */
    for (j = 0; j < FMT_NUM_PLANES; j++) {
        buffers[i].length[j] = buffer.m.planes[j].length; /* remember for munmap() */

        buffers[i].start[j] = mmap(NULL, buffer.m.planes[j].length,
                 PROT_READ | PROT_WRITE, /* recommended */
                 MAP_SHARED,             /* recommended */
                 fd, buffer.m.planes[j].m.offset);

        if (MAP_FAILED == buffers[i].start[j]) {
            /* If you do not exit here you should unmap() and free()
               the buffers and planes mapped so far. */
            perror("mmap");
            exit(EXIT_FAILURE);
        }
    }
}

/* Cleanup. */

for (i = 0; i < reqbuf.count; i++)
    for (j = 0; j < FMT_NUM_PLANES; j++)
        munmap(buffers[i].start[j], buffers[i].length[j]);

概念上讲,流驱动包含两个缓存序列,一个传入序列、一个传出序列。他们使同步捕捉或输出操作从应用程序分开来锁定到视频时钟上,因为应用程序可能受限于随机的硬盘或网络延迟,还有其他进程的抢占,因此减少了数据丢失的可能性。这些序列由FIFO管道组成,缓存可以在输入管道上进行输出,可以在输出管道上进行捕捉。

驱动可能会一直需要一个队列缓存的最小数,在没有缓存限制的情况下应用程序可以预先装配、解除和处理。他们还可以在缓存解除前以不同的顺序进行队列装配,驱动可以用任何顺序对空缓存进行填充。缓存的索引号并没有任何规定,只要是唯一的就好。

初始化所有映射缓存要在其没有进入队列的时候进行,驱动很难做到这一点。对于捕捉应用来说,通常是先装配所有已映射缓存,然后开始捕捉并进入读循环中。应用程序会一直等到一个被填充的缓存能被解除(移除队列),然后若不再需要其数据了就重新将其放入队列。输出程序填充缓存,然后将其放入队列中,当累计了足够的缓存后开始VIDIOC_STREAMON。在写循环中,若应用程序把空闲缓存用完了,那么必须等待有空的buffer可以移除队列和再利用。

应用程序通过VIDIOC_QBUF和VIDIOC_DQBUF来使一个缓存入列和出列。缓存的状态是已映射、已入列、满还是空可以在任何时候通过VIDIOC_QUERYBUF ioctl进行查询。有两个方法使应用程序的执行挂起来等待可出列的缓存。VIDIOC_DQBUF会在传出序列中没有缓存时自动阻塞,若打开设备的时候设置了O_NONBLOCK标志则VIDIOC_DQBUF会在没有可用缓存时返回EAGAIN。select()或poll()函数总是可用的。

应用程序通过调用VIDIOC_STREAMON和VIDIOC_STREAMOFF ioctl来开始、停止捕捉或输出。注意VIDIOC_STREAMOFF会移除掉所有的缓存。因为在多任务操作系统中并没有“当前”的概念,所以如果应用程序需要与其他项目进行同步,那么它应该检查struct v4l2_buffer中捕捉的或输出的缓存timestamp。

声明了内存映射I/O的驱动必须支持VIDIOC_REQBUFS, VIDIOC_QUERYBUF, VIDIOC_QBUF, VIDIOC_DQBUF, VIDIOC_STREAMON和VIDIOC_STREAMOFF ioctl,和mmap()、munmap()、select()及poll()函数。

I/O流 (用户指针)

若VIDIOC_QUERYCAP ioctl后返回的struct v4l2_capability中capabilities包含了V4L2_CAP_STREAMING标志则输入、输出设备要支持这个I/O方法。是否需要支持用户指针方法由VIDIOC_REQBUFS ioctl决定。

此I/O方法结合了高级读写以及内存映射方法。缓存(平面)通过应用程序本身进行申请,可以贮存在虚拟或共享内存中,需要交换的只是数据指针。这些指针和元数据在struct v4l2_buffer(对多平面API来说是struct v4l2_plane)中。若调用VIDIOC_REQBUFS且带有相应缓存类型,则驱动必须切换到用户指针I/O模式。不需要预先分配缓存(平面),因此他们没有索引,也不能像映射缓存那样通过VIDIOC_QUERYBUF ioctl进行查询。

例3.3 初始化用户指针I/O流

struct v4l2_requestbuffers reqbuf;

memset (&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_USERPTR;

if (ioctl (fd, VIDIOC_REQBUFS, &reqbuf) == -1) {
    if (errno == EINVAL)
        printf ("Video capturing or user pointer streaming is not supported\n");
    else
        perror ("VIDIOC_REQBUFS");

    exit (EXIT_FAILURE);
}

缓存地址和大小通过VIDIOC_QBUF ioctl快速写入,尽管缓存通常是循环的,应用程序也可以在每个VIDIOC_QBUF调用中使用不同的地址和大小进行区分。如果硬件需要,驱动将在物理内存中进行内存页交换来创建一个连续的内存区域,这对处于内核虚拟内存子系统中的应用来说很显然就有必要。当缓存页被交换到了磁盘上,他们会被拿回来并锁定到物理内存中为了DMA使用。

填充的或显示的缓存通过VIDIOC_DQBUF ioctl出列。驱动可以在DMA完成和这个ioctl间的任何时候解锁内存页。当VIDIOC_STREAMOFF、VIDIOC_REQBUFS调用或设备关闭时内存也会被解锁。应用程序必须要注意,不要把未出列的缓存释放掉。那样的话,这些缓存一直是锁定的,浪费着物理内存。还有一点需要注意的是,当内存被应用程序释放然后利用为其他目的的时候,驱动并不会被通知,比如完成了请求的DMA以及更新了有效数据。

对于捕捉应用程序来说,将一定数量的空缓存入列是很正常的,他们是为了开启捕捉以及进入读循环。应用程序会等待一个填充后的缓存何时能被出列,然后当其数据不再需要的时候将此缓存重新入列。输出应用程序填充缓存,然后将缓存入列,当累积了足够的缓存输出动作就开始了。在写循环中,若应用程序用光了空闲缓存,那么他必须等到某个空缓存可以被出列,然后重新利用它。应用程序通过挂起的方式等待缓存有两种途径,默认的是当传出序列中没有缓存时VIDIOC_DQBUF会阻塞住。而如果在打开设备时候open()设定了O_NONBLOCK参数,那么VIDIOC_DQBUF在这种情况下会直接返回EAGAIN错误代码。这select()和poll()往往都是有效的。

可以通过VIDIOC_STREAMON和VIDIOC_STREAMOFF来控制捕捉或输出应用程序的开始和停止。需要注意的是,VIDIOC_STREAMOFF存在着一定的副作用,就是它会将所有序列冲的缓存移除,并将他们解锁。由于在多任务系统中并没有“当前”的概念,所以如果一个应用程序需要同其他事件进行同步,应该通过检查捕捉或输出的缓存struct v4l2_buffer中的timestamp成员来达成同步的目的。

实现用户指针I/O方法的驱动必须支持VIDIOC_REQBUFS,VIDIOC_QBUF,VIDIOC_DQBUF,VIDIOC_STREAMON及VIDIOC_STREAMOFF ioctl,还有select()和poll()函数。

V4L2文档翻译(十二)

时间: 2024-11-10 07:02:23

V4L2文档翻译(十二)的相关文章

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先

42. 蛤蟆的数据结构笔记之四十二图的遍历之广度优先 本篇名言:"生活真象这杯浓酒 ,不经三番五次的提炼呵 , 就不会这样一来可口 ! -- 郭小川" 继续看下广度优先的遍历,上篇我们看了深度遍历是每次一个节点的链表是走到底的. 欢迎转载,转载请标明出处:http://write.blog.csdn.net/postedit/47029275 1.  原理 首先,从图的某个顶点v0出发,访问了v0之后,依次访问与v0相邻的未被访问的顶点,然后分别从这些顶点出发,广度优先遍历,直至所有的

C和指针 (pointers on C)——第十二章:使用结构和指针

第十二章 使用结构和指针 这章就是链表.先单链表,后双向链表. 总结: 单链表是一种使用指针来存储值的数据结构.链表中的每个节点包含一个字段,用于指向链表的下一个节点. 有一个独立的根指针指向链表的第1个节点.单链表只能从一个方向遍历. 如何insert单链表:1.新节点的link字段必须设置为指向它的后面节点.2.前一个节点的link字段必须指向这个新节点. 为了防止可能会插入链表的起始位置这种情况,在C中,可以保存一个指向必须进行修改的link字段的指针,而不是保存一个指向前一个节点的指针.

微软云计算介绍与实践(实践之十二)

今天主要实践使用远程 SMB 存储共享保存 VHD.由于周末要看球等原因,内容不多,有料就行.(^_^) 接前面,公司私有云管理员小张已经执行了迁移,他还需要在最近已部署的 Virtual Machine Manager 环境中检查虚拟机设置.所以下一步小张需要确认 Guest01 的存储已经位于 HyperV02 上的 SMB 3.0 共享中,并且虚拟机通过 EvalCluster 群集运行. 1.打开 Virtual Machine Management 控制台,输入用户名 CONTOSO\

【管理心得之二十二】小人物 仰视 大授权

场景再现====================Boss:小王,来我办公室一下.小王: 嗯Boss:近期总公司有会,需要到外地出差几日.我不在的这段期间里,公司大小事务你帮忙处理一下.          如果有什么难决定的事,第一时间电话.邮件联系我商定即可.小王:  明白.放心吧领导,绝不会让你失望的Boss:嗯,那就好,没事了. {小王走出办公室} 心中暗喜,"难道这就是传说中的授权,Boss不在的时候,我岂不是最高权力的行使者." ==================== 从场景

QT开发(二十二)——QMainWindow主窗口

QT开发(二十二)--QMainWindow主窗口 一.主窗口简介 应用程序中的主窗口是与用户进行长时间交互的顶层窗口,提供了应用程序的大部分功能,通常是应用程序启动后的第一个窗口,应用程序一般由一个主窗口和多个对话框组成. QT中直接支持主窗口,QMainWindow是QT中主窗口的基类,是继承于QWidget类的容器型组件. QMainWindow内部封装了菜单栏.工具栏.中心组件.停靠组件.状态栏等. QMainWindow内置了布局管理器,基本的组件布局如下: 二.菜单栏 QT中提供了预

《构建之法》第十一、十二章学习总结

第十一章的内容是软件设计与实现. 在第一节中,讲的是关于分析和设计方法,向我们介绍在"需求分析"."设计与实现"阶段."测试""发布"阶段该搞清楚的问题. 在第二节中,讲的是关于图形建模和分析方法.在表达实体和实体之间的关系时,可以用到思维导图(Mind Map).实体关系图(ERD).UCD ;在表达数据的流动时,可以用到DFD工具:在表达控制流的时候可以用到FSM工具:前面提到的这些图形建模方法各有特点,UML却可以有一个

Python开发【第二十二篇】:Web框架之Django【进阶】

Python开发[第二十二篇]:Web框架之Django[进阶] 猛击这里:http://www.cnblogs.com/wupeiqi/articles/5246483.html 博客园 首页 新随笔 联系 订阅 管理 随笔-124  文章-127  评论-205 Python之路[第十七篇]:Django[进阶篇 ] Model 到目前为止,当我们的程序涉及到数据库相关操作时,我们一般都会这么搞: 创建数据库,设计表结构和字段 使用 MySQLdb 来连接数据库,并编写数据访问层代码 业务逻

QT开发(五十二)———QML语言

QT开发(五十二)---QML语言 QML是一种声明语言,用于描述程序界面.QML将用户界面分解成一块块小的元素,每一元素都由很多组件构成.QML定义了用户界面元素的外观和行为:更复杂的逻辑则可以结合JavaScript脚本实现. 一.QML基础语法 1.Import语句 QML代码中,import语句一般写在头几行,主要用途如下:     A.包含类型的全名空间     B.包含QML代码文件的目录     C.JavaScript代码文件 格式如下: import Namespace Ver

第十二周进度条

第十二周          日期  星期一   星期二   星期三   星期四   星期五   星期六   星期日  了解到的知识点 js获取当前时间 var d = new Date() var nowYear = +d.getFullYear() EF框架填充下拉菜单 var model = db.GYSAllFoods.Select(m => new{GYS = m.GYS}).Distinct();//去重很关键            foreach (var item in model