基于Streaming I/O的V4L2设备使用

来源:互联网 发布:文本转换语音软件 编辑:程序博客网 时间:2024/05/22 15:08

基于V4L2的应用,通常面临着大块数据的读取与拷贝等问题。尤其在嵌入式系统中,对于实时性能要求较高的应用,拷贝会花上几十个ms的时间,这通常轻则造成用户体验差,重则导致产品质量不达标。V4L2 Framework定义了几种不同的方式,用于从设备中读取数据,这篇文章简要介绍下在Streaming I/O模式下,如何使用这几种数据的获取与使用方法。Streaming I/O设计的目的就是为了减少在数据处理的各个环节中,拷贝的次数,从而实现各阶段硬件的无缝配合。
本文针对的是USB Camera (Capture)设备。

1. 设备支持

对于设备特性来说,需要设备支持Streaming能力,这个需要通过V4L2的Capability来判断,方式如下:

struct v4l2_capability _cap;ioctl( _fd, VIDIOC_QUERYCAP, &_cap );

其中,_fd是打开的V4L2设备的描述符,通过:

if( (_cap.capabilities & V4L2_CAP_STREAMING) != 0 ) {    printf( " V4L2_CAP_STREAMING" );}

判断是否支持Streaming方式访问。

2 Memory Map

通过Memory Map访问V4L2设备驱动中分配的内存。设备收到的数据存在驱动内的Buffer中,通过Map方式,将内存Map到用户空间。使用这种方式只有指向这段内存的用户空间指针在各个处理环节中传递,不会发生真实的数据拷贝。

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);

用户程序首先需要通过VIDIOC_REQBUFS,通知驱动进行驱动内内存分配;之后通过VIDIOC_QUERYBUF取得驱动中各内存块的基本信息,主要是Buffer长度和Offset;取得这些信息后,通过mmap,将内存map到User Space中使用。在驱动中,V4L2对于Buffer的使用是队列形式,Buffer出队后,再次入队之前,驱动无法再使用这个Buffer,因此申请的Buffer个数通常是多个,避免数据丢失。
在使用过程中,需要通过poll操作,判断是否有数据到达,然后通过VIDIOC_DQBUF,取得当前有数据的Buffer,通过Buffer的Index属性,找到对应的User Space指针,交由下个环节处理;处理完成后,通过VIDIOC_QBUF,将Buffer重新入队。

3 User Pointers

User Pointers方式会将用户空间分配的内存指针及长度传递给V4L2驱动(虽然是用户空间分配,但不一定是在堆空间上分配的内存,可以是通过其它方式映射出来的内存,比如从另一个设备驱动中),这样数据到达后,可以直接传递这个指针到下个环节中处理。初始化方式如下:

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);}

4 DMA buffer importing

DMA方式的初始化方式如下:

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);}

DMA方式的传输基于文件描述符进行,fd的传递是通过VIDIOC_QBUF中的描述符设置:

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;}

poll操作返回后,可以通过这个dmafd进行下一步处理。

0 0