简体   繁体   English

使用 v4l2 在 C++ 中捕获 YUYV

[英]Capturing YUYV in c++ using v4l2

I have a webcam connected to beaglebone via usb.我有一个通过 USB 连接到 beaglebone 的网络摄像头。 I am coding in c++ and my goal is to capture raw UNCOMPRESSED picture from the webcam.我正在用 C++ 编码,我的目标是从网络摄像头捕获原始的未压缩图片。 Firstly i checked what formats are supported via command v4l2-ctl --list-formats and the result was:首先,我通过命令v4l2-ctl --list-formats检查了支持哪些格式,结果是:

        Index       : 0
        Type        : Video Capture
        Pixel Format: 'MJPG' (compressed)
        Name        : Motion-JPEG

        Index       : 1
        Type        : Video Capture
        Pixel Format: 'YUYV'
        Name        : YUYV 4:2:2

So from this I assume it has to be possible to get an uncompressed picture if i try to use YUYV format.因此,我认为如果我尝试使用 YUYV 格式,则必须有可能获得未压缩的图片。

Knowing this I started writing a program in c++.知道这一点后,我开始用 C++ 编写程序。 I successfully written a program to capture a compressed picture, but when trying to capture using format YUYV it doesnt work and i really need some help to get this done.我成功编写了一个程序来捕获压缩图片,但是当尝试使用格式 YUYV 捕获时它不起作用,我真的需要一些帮助来完成这项工作。

Here is my code:这是我的代码:

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


template <typename typeXX>
void clear_memmory(typeXX* x) {
    memset(x, 0, sizeof(*x));
    }
    
void xioctl(int cd, int request, void *arg){
    int response;
    do{
        //ensures we get the correct response.
        response = v4l2_ioctl(cd, request, arg);
        }
    while (response == -1 && ((errno == EINTR) || (errno == EAGAIN)));

    if (response == -1) {
        fprintf(stderr, "error %d, %s\n", errno, strerror(errno));
        exit(EXIT_FAILURE);
        }
    }
    
    struct LMSBBB_buffer{
    void*  start;
    size_t length;
    };

int main(){
    
    const char* dev_name = "/dev/video0";
    int width=1920;
    int height=1080;
    
    int fd = v4l2_open(dev_name, O_RDWR | O_NONBLOCK, 0);
    
    struct v4l2_format format = {0};
    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.width = width;
    format.fmt.pix.height = height;
    format.fmt.pix.pixelformat = V4L2_PIX_FMT_RGB24;//V4L2_PIX_FMT_YUYV //V4L2_PIX_FMT_RGB24
    format.fmt.pix.field = V4L2_FIELD_NONE; //V4L2_FIELD_NONE
    xioctl(fd, VIDIOC_S_FMT, &format);

        
    printf("Device initialized.\n");
    
    
    ///request buffers  
    struct v4l2_requestbuffers req = {0};
    req.count = 2;
    req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    req.memory = V4L2_MEMORY_MMAP;
    xioctl(fd, VIDIOC_REQBUFS, &req);

    printf("Buffers requested.\n");
    

    ///mapping buffers  
    struct v4l2_buffer buf;
    LMSBBB_buffer* buffers;
    unsigned int i;
    buffers = (LMSBBB_buffer*) calloc(req.count, sizeof(*buffers));
    for (i = 0; i < req.count; i++) {
    clear_memmory(&(buf));

    (buf).type        = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    (buf).memory      = V4L2_MEMORY_MMAP;
    (buf).index       = i;

    xioctl(fd, VIDIOC_QUERYBUF, &buf);

    buffers[i].length = (buf).length;
    printf("A buff has a len of: %i\n",buffers[i].length);
    buffers[i].start = v4l2_mmap(NULL, (buf).length, PROT_READ | PROT_WRITE, MAP_SHARED,fd, (buf).m.offset);
    
    if (MAP_FAILED == buffers[i].start) {
        perror("Can not map the buffers.");
        exit(EXIT_FAILURE);
        }
    }
    printf("Buffers mapped.\n");    
    
    for (i = 0; i < req.count; i++) {
        clear_memmory(&(buf));
        (buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        (buf).memory = V4L2_MEMORY_MMAP;
        (buf).index = i;
        ioctl(fd,VIDIOC_QBUF, &(buf));
        }
    enum v4l2_buf_type type;
    type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    ioctl(fd,VIDIOC_STREAMON, &type);
    
    
    printf("buffers queued and streaming.\n");
    
    
    
    int pic_count=0;
    ///CAPTURE
    fd_set fds;
    struct timeval tv;
    int r;
    char out_name[256];
    FILE* fout;
        

        
    do {
        FD_ZERO(&fds);
        FD_SET(fd, &fds);

        // Timeout.
        tv.tv_sec = 2;
        tv.tv_usec = 0;

        r = select(fd + 1, &fds, NULL, NULL, &tv);
        } while ((r == -1 && (errno = EINTR)));
    if (r == -1) {
        perror("select");
        exit(EXIT_FAILURE);
        }

    clear_memmory(&(buf));
    (buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    (buf).memory = V4L2_MEMORY_MMAP;
    xioctl(fd,VIDIOC_DQBUF, &(buf));
    
    printf("Buff index: %i\n",(buf).index);
    sprintf(out_name, "image%03d.ppm",pic_count);
    fout = fopen(out_name, "w");
    if (!fout) {
        perror("Cannot open image");
        exit(EXIT_FAILURE);
        }
    fprintf(fout, "P6\n%d %d 255\n",width, height);
    fwrite(buffers[(buf).index].start, (buf).bytesused, 1, fout);
    fclose(fout);
    pic_count++;
    
    clear_memmory(&(buf));
    (buf).type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    (buf).memory = V4L2_MEMORY_MMAP;
    xioctl(fd,VIDIOC_DQBUF, &(buf));
    printf("Buff index: %i\n",(buf).index);
    sprintf(out_name, "image%03d.ppm",pic_count);
    fout = fopen(out_name, "w");
    if (!fout) {
        perror("Cannot open image");
        exit(EXIT_FAILURE);
        }
    fprintf(fout, "P6\n%d %d 255\n",width, height);
    fwrite(buffers[(buf).index].start, (buf).bytesused, 1, fout);
    fclose(fout);
    pic_count++;
    
    
    ///xioctl(fd,VIDIOC_QBUF, &(buf));
    
    
    return 0;
    }

in line 50, i can choose the format between V4L2_PIX_FMT_YUYV and V4L2_PIX_FMT_RGB24.在第 50 行,我可以选择 V4L2_PIX_FMT_YUYV 和 V4L2_PIX_FMT_RGB24 之间的格式。 for V4L2_PIX_FMT_RGB24 i get the picture, but when using V4L2_PIX_FMT_YUYV I get this error:对于 V4L2_PIX_FMT_RGB24 我得到了图片,但是当使用 V4L2_PIX_FMT_YUYV 时我得到这个错误:

libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable
libv4l2: error dequeuing buf: Resource temporarily unavailable

the error lines goes for ever until i end the program manually.错误行一直存在,直到我手动结束程序。

Does anyone have an idea what to do?有谁知道该怎么做? I spent over 2 weeks on this and i can't move anywhere from here.我在这上面花了 2 个多星期,我不能从这里搬到任何地方。 I would really appreciate any advice.我真的很感激任何建议。

From what I see you are requesting a FullHD (1920x1080) buffer in YUYV format from a camera.据我所知,您正在从相机请求 YUYV 格式的 FullHD (1920x1080) 缓冲区。 You did not mention the camera type/model/specs, but if it is a generic USB-attached hardware most likely you will not get a raw FullHD YUYV buffer as an output, only the MJPEG one (which you can decode to YUV, if you hack around with libjpeg) or the decoded RGB buffer (which is pretty much the decoded MJPEG with YUV->RGB conversion) which is not mmapped.你没有提到相机类型/型号/规格,但如果它是一个通用的 USB 连接硬件,你很可能不会得到一个原始的 FullHD YUYV 缓冲区作为输出,只有 MJPEG 一个(你可以解码为 YUV,如果您可以使用 libjpeg 进行修改)或未映射的解码 RGB 缓冲区(这几乎是带有 YUV->RGB 转换的解码 MJPEG)。

The exact list of formats with framerates can be requested by this command, which would probably tell you it does not provide a 1920x1080 YUYV, only something smaller, like 640x480:此命令可以请求具有帧速率的确切格式列表,这可能会告诉您它不提供 1920x1080 YUYV,仅提供较小的内容,例如 640x480:

v4l2-ctl --list-formats

If you need video processing with "true" zero-copy access to raw YUYV camera frames, you need direct access to hardware and that specific hardware in the first place.如果您需要对原始 YUYV 相机帧进行“真正”零拷贝访问的视频处理,则首先需要直接访问硬件和特定硬件。 Once you have the USB interface between your software and the camera, you get an extra indirection and that means the speed goes down.一旦你的软件和相机之间有了 USB 接口,你就会得到一个额外的间接访问,这意味着速度会下降。 Think for a moment, the YUYV frame at 1920x1080 takes up approximately 4 Megabytes of memory.想一想,1920x1080 的 YUYV 帧占用大约 4 兆字节的内存。 At 30 FPS this is 120 Megabytes (or 960 Megabits) per second of bus throughput.在 30 FPS 时,这是每秒 120 兆字节(或 960 兆位)的总线吞吐量。 If you have a USB2.0 camera, there is just no bandwidth to support this (thus the need for MJPEG).如果您有 USB2.0 相机,则没有带宽支持(因此需要 MJPEG)。 Even at 15FPS this is 480 Megabits, not counting the USB latency and protocol overhead.即使在 15FPS 时,这也是 480 兆位,不包括 USB 延迟和协议开销。

To provide some "actionable feedback" I would advice to first concentrate on the algorithms (probably, you just don't want to loose the processing speed at the very first step) which you want to apply to the image.为了提供一些“可操作的反馈”,我建议首先专注于要应用于图像的算法(可能,您只是不想在第一步失去处理速度)。 Don't hesitate to use OpenCV for camera input and basic image processing, later you can switch to some hardware interface and hand-written algorithms.不要犹豫,使用 OpenCV 进行相机输入和基本图像处理,稍后您可以切换到一些硬件接口和手写算法。

The easier way of getting raw frames would be to use Android's camera interface and try to process the incoming frames with GLSL shaders using the GL_TEXTURE_EXTERNAL_OES extension, about which there information and code samples available.获取原始帧的更简单方法是使用 Android 的相机界面,并尝试使用 GL_TEXTURE_EXTERNAL_OES 扩展使用 GLSL 着色器处理传入帧,有关该扩展的信息和代码示例可用。 There you can connect GL textures to AHardwareBuffer instances and then use AHardwareBuffer_lock function to get raw pointers.在那里您可以将 GL 纹理连接到 AHardwareBuffer 实例,然后使用 AHardwareBuffer_lock 函数来获取原始指针。 The exact supported formats also may vary across the hardware, so do not expect this to be super-easy.确切支持的格式也可能因硬件而异,因此不要指望这非常容易。

I've recently had a similar issue.我最近遇到了类似的问题。 In my case the camera driver needed the VIDIOC_S_PARM ioctl in order to set the frame rate and initialize the camera for the selected capture mode.在我的例子中,相机驱动程序需要 VIDIOC_S_PARM ioctl 来设置帧速率并为选定的捕获模式初始化相机。

You can try to add this code after the VIDIOC_S_FMT and see if it works for you as well:您可以尝试在 VIDIOC_S_FMT 之后添加此代码,看看它是否也适用于您:

struct v4l2_streamparm streamparam;
memset(&streamparam, 0, sizeof(streamparam));
streamparam.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
xioctl(fd, VIDIOC_G_PARM, &streamparam);
streamparam.parm.capture.timeperframe.numerator = 1;
streamparam.parm.capture.timeperframe.denominator = 5;
xioctl(fd, VIDIOC_S_PARM, &streamparam);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM