7.46. ioctl VIDIOC_QBUF, VIDIOC_DQBUF

7.46.1. 名称

VIDIOC_QBUF - VIDIOC_DQBUF - 与驱动程序交换缓冲区

7.46.2. 概要

VIDIOC_QBUF

int ioctl(int fd, VIDIOC_QBUF, struct v4l2_buffer *argp)

VIDIOC_DQBUF

int ioctl(int fd, VIDIOC_DQBUF, struct v4l2_buffer *argp)

7.46.3. 参数

fd

open() 返回的文件描述符。

argp

指向结构体 v4l2_buffer 的指针。

7.46.4. 描述

应用程序调用 VIDIOC_QBUF ioctl 将一个空的(捕获)或已填充的(输出)缓冲区排入驱动程序的传入队列。语义取决于选择的 I/O 方法。

要排队一个缓冲区,应用程序将结构体 v4l2_buffertype 字段设置为与之前与结构体 v4l2_format type 和结构体 v4l2_requestbuffers type 一起使用的缓冲区类型相同。 应用程序还必须设置 index 字段。 有效的索引号范围从零到使用 ioctl VIDIOC_REQBUFS 分配的缓冲区数量(结构体 v4l2_requestbuffers count)减一。 由 ioctl VIDIOC_QUERYBUF ioctl 返回的结构体 v4l2_buffer 的内容也将起作用。 当缓冲区用于输出(typeV4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, 或 V4L2_BUF_TYPE_VBI_OUTPUT)时,应用程序还必须初始化 bytesused, fieldtimestamp 字段,参见 缓冲区 了解详细信息。 应用程序还必须将 flags 设置为 0。 reserved2reserved 字段必须设置为 0。 当使用 多平面 API 时,m.planes 字段必须包含一个指向已填充的结构体 v4l2_plane 数组的用户空间指针,并且 length 字段必须设置为该数组中元素的数量。

要排队一个 内存映射 缓冲区,应用程序将 memory 字段设置为 V4L2_MEMORY_MMAP。 当使用指向此结构的指针调用 VIDIOC_QBUF 时,驱动程序将 V4L2_BUF_FLAG_MAPPEDV4L2_BUF_FLAG_QUEUED 标志,并清除 flags 字段中的 V4L2_BUF_FLAG_DONE 标志,或者它返回一个 EINVAL 错误代码。

要排队一个 用户指针 缓冲区,应用程序将 memory 字段设置为 V4L2_MEMORY_USERPTRm.userptr 字段设置为缓冲区的地址,length 设置为其大小。 当使用多平面 API 时,必须使用传递的结构体 v4l2_plane 数组的 m.userptrlength 成员。 当使用指向此结构的指针调用 VIDIOC_QBUF 时,驱动程序将设置 V4L2_BUF_FLAG_QUEUED 标志,并清除 flags 字段中的 V4L2_BUF_FLAG_MAPPEDV4L2_BUF_FLAG_DONE 标志,或者它返回一个错误代码。 此 ioctl 会锁定物理内存中的缓冲区内存页,它们无法交换到磁盘。 缓冲区保持锁定状态,直到出队,直到调用 VIDIOC_STREAMOFFioctl VIDIOC_REQBUFS ioctl,或直到设备关闭。

要排队一个 DMABUF 缓冲区,应用程序将 memory 字段设置为 V4L2_MEMORY_DMABUF,并将 m.fd 字段设置为与 DMABUF 缓冲区关联的文件描述符。 当使用多平面 API 时,必须使用传递的结构体 v4l2_plane 数组的 m.fd 字段。 当使用指向此结构的指针调用 VIDIOC_QBUF 时,驱动程序将设置 V4L2_BUF_FLAG_QUEUED 标志,并清除 flags 字段中的 V4L2_BUF_FLAG_MAPPEDV4L2_BUF_FLAG_DONE 标志,或者它返回一个错误代码。 此 ioctl 会锁定缓冲区。 锁定缓冲区意味着将其传递给驱动程序以进行硬件访问(通常是 DMA)。 如果应用程序访问(读取/写入)锁定的缓冲区,则结果未定义。 缓冲区保持锁定状态,直到出队,直到调用 VIDIOC_STREAMOFFioctl VIDIOC_REQBUFS ioctl,或直到设备关闭。

如果正在使用请求,则可以将 request_fd 字段与 VIDIOC_QBUF ioctl 一起使用,以指定 请求 的文件描述符。 设置它意味着缓冲区在请求本身排队之前不会传递给驱动程序。 此外,驱动程序将为此缓冲区应用与请求关联的任何设置。 除非设置了 V4L2_BUF_FLAG_REQUEST_FD 标志,否则将忽略此字段。 如果设备不支持请求,则将返回 EBADR。 如果支持请求但给出了无效的请求文件描述符,则将返回 EINVAL

警告

不允许混合排队请求和直接排队缓冲区。 如果第一个缓冲区直接排队,然后应用程序尝试排队一个请求,或者反之亦然,将返回 EBUSY。 关闭文件描述符,调用 VIDIOC_STREAMOFF 或调用 ioctl VIDIOC_REQBUFS 后,对此的检查将重置。

对于 内存到内存设备,您只能为输出缓冲区指定 request_fd,而不能为捕获缓冲区指定。 尝试为捕获缓冲区指定此项将导致 EBADR 错误。

应用程序调用 VIDIOC_DQBUF ioctl 从驱动程序的传出队列中出队一个已填充的(捕获)或已显示的(输出)缓冲区。 当使用指向此结构的指针调用 VIDIOC_DQBUF 时,它们只需将结构体 v4l2_buffertype, memoryreserved 字段设置为如上所述,驱动程序将填充所有剩余字段或返回错误代码。 驱动程序还可以在 flags 字段中设置 V4L2_BUF_FLAG_ERROR。 它表示非关键的(可恢复的)流式传输错误。 在这种情况下,应用程序可以像往常一样继续,但应该意识到出队缓冲区中的数据可能已损坏。 当使用多平面 API 时,也必须传入平面数组。

如果应用程序将 memory 字段设置为 V4L2_MEMORY_DMABUF 以出队一个 DMABUF 缓冲区,则驱动程序将 m.fd 字段填充一个文件描述符,该文件描述符在数值上与缓冲区排队时给 VIDIOC_QBUF 的文件描述符相同。 在出队时不会创建新的文件描述符,该值仅为应用程序方便起见。 当使用多平面 API 时,将填充传递的结构体 v4l2_plane 数组的 m.fd 字段。

默认情况下,当传出队列中没有缓冲区时,VIDIOC_DQBUF 会阻塞。 当 open() 函数具有 O_NONBLOCK 标志时,当没有缓冲区可用时,VIDIOC_DQBUF 会立即返回一个 EAGAIN 错误代码。

结构体 v4l2_buffer 结构在 缓冲区 中指定。

7.46.5. 返回值

成功时返回 0,出错时返回 -1,并适当设置 errno 变量。 通用错误代码在 通用错误代码 章节中描述。

EAGAIN

已使用 O_NONBLOCK 选择了非阻塞 I/O,并且传出队列中没有缓冲区。

EINVAL

不支持缓冲区 type,或者 index 超出范围,或者尚未分配缓冲区,或者 userptrlength 无效,或者设置了 V4L2_BUF_FLAG_REQUEST_FD 标志,但给定的 request_fd 无效,或者 m.fd 是无效的 DMABUF 文件描述符。

EIO

由于内部错误,VIDIOC_DQBUF 失败。 也可以指示诸如信号丢失之类的临时问题。

注意

尽管返回错误,驱动程序也可能会出队一个(空)缓冲区,甚至停止捕获。 重用此类缓冲区可能不安全,并且其详细信息(例如 index)也可能不会返回。 建议驱动程序通过设置 V4L2_BUF_FLAG_ERROR 并返回 0 来指示可恢复的错误。 在这种情况下,应用程序应该能够安全地重用缓冲区并继续流式传输。

EPIPE

如果具有 V4L2_BUF_FLAG_LAST 的缓冲区已被出队并且预计不会有新的缓冲区可用,则 VIDIOC_DQBUF 会在 mem2mem 编解码器的空捕获队列上返回此错误。

EBADR

设置了 V4L2_BUF_FLAG_REQUEST_FD 标志,但设备不支持给定缓冲区类型的请求,或者未设置 V4L2_BUF_FLAG_REQUEST_FD 标志,但设备要求缓冲区是请求的一部分。

EBUSY

第一个缓冲区是通过请求排队的,但应用程序现在尝试直接排队它,或者反之亦然(不允许混合使用这两个 API)。