【Linux开发】IO streaming DMA buffer importing

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

I/O流 (DMA缓存引用)

这是一个实验性接口,将来可能发生改变

DMABUF框架提供了在多设备见共享缓存的通用方法,支持DMABUF的设备驱动可以将一个DMA缓存以文件句柄的方式输出到用户空间(输出者规则),以文件句柄的方式从用户空间获取一个DMA缓存,这个文件句柄是之前其他或相同的设备所输出的(引入者规则),或都是。此章节描述在V4L2中DMABUF的引入者规则。

V4L2缓存以DMABUF文件句柄方式进行DMABUF输出。

支持I/O流方法的输入和输出设备在通过VIDIOC_QUERYCAP ioctl查询时,返回的struct v4l2_capability中capabilities成员会包含V4L2_CAP_STREAMING标签。是否支持通过DMABUF文件句柄引入DMABUF缓存决定了在调用VIDIOC_REQBUFS时内存类型是否要设定为V4L2_MEMORY_DMABUF。

这种I/O方法专用于在不同设备间共享DMA缓存,这些设备可以是V4L设备或是其他视频相关设备(如DRM)。缓存(面)通过应用程序控制驱动来申请。然后,这些缓存通过使用特殊API以文件句柄的方式输出给应用程序,交换的只有文件句柄。句柄和信息存在于struct v4l2_buffer(多平面API中是struct v4l2_plane)。在VIDIOC_REQBUFS ioctl执行后驱动必须切换到DMABUF I/O模式中。

例3.4 通过DMABUF文件句柄初始化I/O流

struct v4l2_requestbuffers reqbuf;

memset(&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_DMABUF;
reqbuf.count = 1;

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

    exit(EXIT_FAILURE);
}

缓存(面)文件描述符通过VIDIOC_QBUF ioctl传输。在多平面中,每个面都申请了一个不同的DMABUF描述符,尽管缓存通常是循环的,应用程序也要每次通过不同的DMABUF描述符进行VIDIOC_QBUF请求处理。

例3.5 单一平面API中入列DMABUF

int buffer_queue(int v4lfd, int index, int dmafd)
{
    struct v4l2_buffer buf;

    memset(&buf, 0, sizeof buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_DMABUF;
    buf.index = index;
    buf.m.fd = dmafd;

    if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
        perror("VIDIOC_QBUF");
        return -1;
    }

    return 0;
}

例3.6 多平面API中入列DMABUF

int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes)
{
    struct v4l2_buffer buf;
    struct v4l2_plane planes[VIDEO_MAX_PLANES];
    int i;

    memset(&buf, 0, sizeof buf);
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
    buf.memory = V4L2_MEMORY_DMABUF;
    buf.index = index;
    buf.m.planes = planes;
    buf.length = n_planes;

    memset(&planes, 0, sizeof(planes));

    for (i = 0; i < n_planes; ++i)
        buf.m.planes[i].m.fd = dmafd[i];

    if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
        perror("VIDIOC_QBUF");
        return -1;
    }

    return 0;
}

捕捉或显示缓存都通过VIDIOC_DQBUF ioctl进行出列操作。驱动在这个ioctl和DMA完成的期间可以在任何时候对缓存进行解锁。当VIDIOC_STREAMOFF或VIDIOC_REQBUFS调用,以及设备关闭的时候内存都会被解锁。

对于捕捉应用程序来说,在开始捕捉及进入读循环前通常要入列一定数量的空缓存。一旦已填充过的缓存可以出列,应用程序在其数据不再需要的时候会将此缓存重新入列。而输出程序对缓存进行填充和入列操作,当堆积了足够的缓存后输出就开始了,在写循环中,当程序没有空闲缓存可用了,它需要等待有可出列的空缓存然后对其重新利用。两种方法都会阻塞程序的执行直到有缓存可以出列为止。VIDIOC_DQBUF当输出序列中没有可用缓存时默认是阻塞的,若open()时带有O_NONBLOCK标签,在出现这种情况的时候VIDIOC_DQBUF会直接返回EAGAIN错误码。而select()和poll()函数一直都是有效的。

通过调用VIDIOC_STREAMON和VIDIOC_STREAMOFF ioctl可以开始及停止程序进行捕捉或输出,这里请注意,VIDIOC_STREAMOFF会将所有序列中的缓存都移除,并且解锁。因为在多任务系统中没有“当前”的概念,所以如果程序需要与其他事件进行同步,需要通过捕捉或输出缓存的struct v4l2_buffer中的timestamp成员进行。

声明了DMABUF流方法的驱动必须支持VIDIOC_REQBUFS,VIDIOC_QBUF,VIDIOC_DQBUF,VIDIOC_STREAMON,VIDIOC_STREAMOFF这些ioctl,以及select()和poll()函数。

同步I/O

此方法尚未定义。



Streaming I/O (DMA buffer importing)

The DMABUF framework provides a generic method for sharing buffers between multiple devices. Device drivers that support DMABUF can export a DMA buffer to userspace as a file descriptor (known as the exporter role), import a DMA buffer from userspace using a file descriptor previously exported for a different or the same device (known as the importer role), or both. This section describes the DMABUF importer role API in V4L2.

Refer to DMABUF exporting for details about exporting V4L2 buffers as DMABUF file descriptors.

Input and output devices support the streaming I/O method when the V4L2_CAP_STREAMING flag in the capabilities field of struct v4l2_capability returned by the VIDIOC_QUERYCAP ioctl is set. Whether importing DMA buffers through DMABUF file descriptors is supported is determined by calling the VIDIOC_REQBUFS ioctl with the memory type set to V4L2_MEMORY_DMABUF.

This I/O method is dedicated to sharing DMA buffers between different devices, which may be V4L devices or other video-related devices (e.g. DRM). Buffers (planes) are allocated by a driver on behalf of an application. Next, these buffers are exported to the application as file descriptors using an API which is specific for an allocator driver. Only such file descriptor are exchanged. The descriptors and meta-information are passed in struct v4l2_buffer (or in struct v4l2_plane in the multi-planar API case). The driver must be switched into DMABUF I/O mode by calling the VIDIOC_REQBUFS with the desired buffer type.

Example 3.4. Initiating streaming I/O with DMABUF file descriptors

struct v4l2_requestbuffers reqbuf;

memset(&reqbuf, 0, sizeof (reqbuf));
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_DMABUF;
reqbuf.count = 1;

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

	exit(EXIT_FAILURE);
}
      

The buffer (plane) file descriptor is passed on the fly with the VIDIOC_QBUF ioctl. In case of multiplanar buffers, every plane can be associated with a different DMABUF descriptor. Although buffers are commonly cycled, applications can pass a different DMABUF descriptor at each VIDIOC_QBUF call.

Example 3.5. Queueing DMABUF using single plane API

int buffer_queue(int v4lfd, int index, int dmafd)
{
	struct v4l2_buffer buf;

	memset(&buf, 0, sizeof buf);
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
	buf.memory = V4L2_MEMORY_DMABUF;
	buf.index = index;
	buf.m.fd = dmafd;

	if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
		perror("VIDIOC_QBUF");
		return -1;
	}

	return 0;
}
      

Example 3.6. Queueing DMABUF using multi plane API

int buffer_queue_mp(int v4lfd, int index, int dmafd[], int n_planes)
{
	struct v4l2_buffer buf;
	struct v4l2_plane planes[VIDEO_MAX_PLANES];
	int i;

	memset(&buf, 0, sizeof buf);
	buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
	buf.memory = V4L2_MEMORY_DMABUF;
	buf.index = index;
	buf.m.planes = planes;
	buf.length = n_planes;

	memset(&planes, 0, sizeof planes);

	for (i = 0; i < n_planes; ++i)
		buf.m.planes[i].m.fd = dmafd[i];

	if (ioctl(v4lfd, VIDIOC_QBUF, &buf) == -1) {
		perror("VIDIOC_QBUF");
		return -1;
	}

	return 0;
}
      

Captured or displayed buffers are dequeued with the VIDIOC_DQBUF ioctl. The driver can unlock the buffer at any time between the completion of the DMA and this ioctl. The memory is also unlocked when VIDIOC_STREAMOFF is called, VIDIOC_REQBUFS, or when the device is closed.

For capturing applications it is customary to enqueue a number of empty buffers, to start capturing and enter the read loop. Here the application waits until a filled buffer can be dequeued, and re-enqueues the buffer when the data is no longer needed. Output applications fill and enqueue buffers, when enough buffers are stacked up output is started. In the write loop, when the application runs out of free buffers it must wait until an empty buffer can be dequeued and reused. Two methods exist to suspend execution of the application until one or more buffers can be dequeued. By default VIDIOC_DQBUF blocks when no buffer is in the outgoing queue. When the O_NONBLOCK flag was given to the open() function, VIDIOC_DQBUF returns immediately with an EAGAIN error code when no buffer is available. The select() and poll() functions are always available.

To start and stop capturing or displaying applications call the VIDIOC_STREAMON and VIDIOC_STREAMOFF ioctls. Note that VIDIOC_STREAMOFF removes all buffers from both queues and unlocks all buffers as a side effect. Since there is no notion of doing anything "now" on a multitasking system, if an application needs to synchronize with another event it should examine the struct v4l2_buffer timestamp of captured or outputted buffers.

Drivers implementing DMABUF importing I/O must support the VIDIOC_REQBUFSVIDIOC_QBUFVIDIOC_DQBUFVIDIOC_STREAMON and VIDIOC_STREAMOFF ioctls, and the select() and poll() functions


posted @ 2016-09-01 14:44  ZhangPYi  阅读(1474)  评论(0编辑  收藏  举报