我一直试图以近乎最好的性能显示从覆盆子摄像头到屏幕的帧,经过研究,我发现最好的方法可能是使用V4L2库直接与司机通信.

在下一节中,我将介绍我的代码.它的特征可能是Stack overflow个代码更长,但我注释了每一段代码,所以它更容易理解.

#include <errno.h>
#include <fcntl.h>
#include <linux/videodev2.h>
#include <linux/fb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/ioctl.h>

#define CAM_WIDTH 1280
#define CAM_HEIGHT 720
#define CAM_FORMAT (v4l2_fourcc('B','G','R','A'))

#define CAM_BUFFERS 1

int main(void)
{
    int cam_fd; 
    int fb_fd;

    struct v4l2_capability cap;
    struct v4l2_requestbuffers reqbuf;
    struct v4l2_format fmt;
    struct v4l2_buffer buffinfo;
    enum v4l2_buf_type bufftype;

    char *cam_buffers[6];
    int cam_buffer_size;
    int cam_buffer_index = -1;

    char *fb_p;
    struct fb_var_screeninfo vinfo;
    struct fb_fix_screeninfo finfo;

    /* Setting framebuffer */   
    fb_fd = open("/dev/fb0", O_RDWR);
    if(!fb_fd)
        {
                fprintf(stderr, "%s:%i: Unable to open framebuffer\n", __FILE__, __LINE__);
                return -1;
        }
    ioctl(fb_fd, FBIOGET_FSCREENINFO, &finfo);
    if(ioctl(fb_fd, FBIOGET_VSCREENINFO, &vinfo) == -1)
    {
        fprintf(stderr, "%s:%i: Unable to get framebuffer info\n", __FILE__, __LINE__);
        return -1;
    }
    printf("Framebuffer: resolution %dx%d with %dbpp\n\r", vinfo.xres, vinfo.yres, vinfo.bits_per_pixel);

    fb_p = (char*)mmap(0, vinfo.xres*vinfo.yres*4, PROT_READ | PROT_WRITE, MAP_SHARED, fb_fd, 0);

    memset(fb_p, 0, vinfo.xres*vinfo.yres*4);

    /* Setting camera */
    cam_fd = open("/dev/video0", O_RDWR | O_NONBLOCK, 0);
    if(!cam_fd){
        fprintf(stderr, "%s:%i: Couldn't open device\n", __FILE__, __LINE__);
        return -1;
    }
    if(ioctl(cam_fd, VIDIOC_QUERYCAP, &cap))
    {
        fprintf(stderr, "%s:%i: Couldn't retreive device capabilities\n", __FILE__, __LINE__);
        return -1;
    }
    if(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE == 0)
    {
        fprintf(stderr, "%s:%i: Device is not a capture device\n", __FILE__, __LINE__);
        return -1;
    }
    if(cap.capabilities & V4L2_CAP_STREAMING == 0)
    {
        fprintf(stderr, "%s:%i: Device is not available for streaming", __FILE__, __LINE__);
        return -1;
    }
    
    /* Set image format */
    memset(&fmt, 0, sizeof(fmt));
    fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    fmt.fmt.pix.width = CAM_WIDTH;
    fmt.fmt.pix.height = CAM_HEIGHT;
    fmt.fmt.pix.pixelformat = CAM_FORMAT;
    fmt.fmt.pix.field = V4L2_FIELD_NONE;
    if(ioctl(cam_fd, VIDIOC_S_FMT, &fmt) == -1)
    {
        fprintf(stderr, "%s:%i: Unable to set image format\n", __FILE__, __LINE__);
        return -1;
    }
    cam_buffer_size = fmt.fmt.pix.sizeimage;

    /* Request buffers */
    memset(&reqbuf, 0, sizeof(reqbuf));
    reqbuf.count = CAM_BUFFERS;
    reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    reqbuf.memory = V4L2_MEMORY_MMAP;
    if(ioctl(cam_fd, VIDIOC_REQBUFS, &reqbuf) == -1)
    {
        fprintf(stderr, "%s:%i: Mmap streaming not supported\n", __FILE__, __LINE__);
        return -1;
    }
    if(reqbuf.count != CAM_BUFFERS)
    {
        fprintf(stderr, "%S:%i: Not all requared buffers are allocated\n", __FILE__, __LINE__);
        return -1;
    }
    
    /* Query and Mmap buffers */
    for (int i=0; i < CAM_BUFFERS; i++)
    {
        memset(&buffinfo, 0, sizeof(buffinfo));
        buffinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buffinfo.memory = V4L2_MEMORY_MMAP;
        buffinfo.index = i;
        
        if(ioctl(cam_fd, VIDIOC_QUERYBUF, &buffinfo) == -1)
        {
            fprintf(stderr, "%s:%i: Unable to query buffers\n", __FILE__, __LINE__);
            return -1;
        }
        
        cam_buffers[i] = mmap(NULL, buffinfo.length, PROT_READ | PROT_WRITE, MAP_SHARED, cam_fd, buffinfo.m.offset);
    
        if(cam_buffers[i] == MAP_FAILED)
        {
            fprintf(stderr, "%s:%i: Unable to enqueue buffers\n", __FILE__, __LINE__);
            return -1;
        }
    }   
    
    /* Enqueue buffers */
        for (int i=0; i < CAM_BUFFERS; i++)
        {
                memset(&buffinfo, 0, sizeof(buffinfo));
                buffinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buffinfo.memory = V4L2_MEMORY_MMAP;
                buffinfo.index = i;

                if(ioctl(cam_fd, VIDIOC_QBUF, &buffinfo) == -1)
                {
                        fprintf(stderr, "%s:%i: Unable to enqueue buffers\n", __FILE__, __LINE__);
                        return -1;
                }
        }    

    /* Start Streaming */
    bufftype = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    if(ioctl(cam_fd, VIDIOC_STREAMON, &bufftype) == -1)
    {
        fprintf(stderr, "%s:%i: Unable to start streaming\n", __FILE__, __LINE__);
        return -1;
    }

    while(1)
    {
        fd_set fds;
        struct timeval tv;
        int r;

        FD_ZERO(&fds);
        FD_SET(cam_fd, &fds);
        tv.tv_sec = 2;
        tv.tv_usec = 0;
        
        r = select(cam_fd+1, &fds, NULL, NULL, &tv);
        if (r == -1) {
            if (errno = EINTR)
                continue;
            fprintf(stderr, "%s:%i: Call to select() failed\n", __FILE__, __LINE__);
            return -1;
        }
        if (r == 0) {
            fprintf(stderr, "%s:%i: Call to select() timeout\n", __FILE__, __LINE__);
            continue;
        }

        if (!FD_ISSET(cam_fd, &fds))
            continue;
        
        memset(&buffinfo, 0, sizeof(buffinfo));
        buffinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buffinfo.memory = V4L2_MEMORY_MMAP;
        if(ioctl(cam_fd, VIDIOC_DQBUF, &buffinfo) == -1) {
            if(errno == EAGAIN)
                continue;
            fprintf(stderr, "%s:%i: Unable to dequeue buffer\n", __FILE__, __LINE__);
            return -1;
        }

        cam_buffer_index = buffinfo.index;

        memcpy(fb_p, cam_buffers[cam_buffer_index], vinfo.xres*vinfo.yres*4);

        memset(&buffinfo, 0, sizeof(buffinfo));
                buffinfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buffinfo.memory = V4L2_MEMORY_MMAP;
        buffinfo.index = cam_buffer_index;
                if(ioctl(cam_fd, VIDIOC_QBUF, &buffinfo) == -1) {
                        fprintf(stderr, "%s:%i: Unable to enqueue buffer\n", __FILE__, __LINE__);
                        return -1;
                }
    }
    return 0;
}

我屏幕上的输出,您可以在下一张图片上看到:

enter image description here

我就是不明白为什么yields 是这样的.我输入了fbset -i命令来判断关于我的帧缓冲区的信息,它说:它是1280x720,有32 bpp,而在rgba部分它是8/16, 8/8, 8/0, 8/24(这应该意味着它是BGRA格式的).有没有人以前遇到过这种奇怪的行为,并知道发生了什么?我在代码中查找了逻辑错误,但找不到哪里出错了.

另外,如果有人对我现在正在研究的这个话题有建议,一些建议会真的很有帮助.

推荐答案

我搞清楚了问题出在哪里.我输入了v4l2-ctl --list-formats以判断我的相机支持哪些格式,但没有BGRA,所以它可能会 Select 一个可能的选项:YU12YUYVRGB3JPEGH264MJPGYVYUVYUYUYVYNV12BGR3YV12NV21RX24.我 Select 了BGR3,将它复制到帧缓冲区,在每个bgr分量后添加alpha值,并使用2个for循环,得到了正确的彩色图像!

以下是填充帧缓冲区的代码块:

int offset_data = 0;
for (int i = 0; i < vinfo.yres; i++)
{
    for (int j = 0; j < vinfo.xres*4; j+=4)
    {
        int idx = i*vinfo.xres*4 + j;
        fb_p[idx] = cam_buffers[cam_buffer_index][offset_data+0];
        fb_p[idx+1] = cam_buffers[cam_buffer_index][offset_data+1];
        fb_p[idx+2] = cam_buffers[cam_buffer_index][offset_data+2];
        fb_p[idx+3] = 0;
        offset_data += 3;
    }
}

结果如下:

enter image description here

C++相关问答推荐

自定义malloc实现上奇怪的操作系统依赖行为

仅在给定的大小和对齐方式下正确创建全局

C是否用0填充多维数组的其余部分?

拥有3x3二维数组并访问数组[1][3]等同于数组[2][0]?

以下声明和定义之间的区别

如何在C语言中正确打印图形

将 struct 传递给函数

是否需要包括<;errno.h>;才能使用perror?

为什么Linux无法映射这个PT_LOAD ELF段?

如何在C中用bool进行文件I/O?

我不知道为什么它不能正常工作,我用了get()和fget(),结果是一样的

某些EAX值的不同调用方的CPUID结果不一致

C中的char**v*char[]

如何在MSVC中使用intSafe.h函数?

x86-64平台上的int_fast8_t大小与int_fast16_t大小

从系统派生线程调用CRT

我正在使用 klib 库 我可以使用 (khash) KHASH_SET_INIT_INT64() 负值作为键.因为我在头文件中看到它使用 unsigned long int

为什么写入关闭管道会返回成功

Makefile - 将 .o 文件放入子文件夹中

我该如何处理这个 C 90 代码中的内存泄漏?