首页 > 解决方案 > v4l2 简单应用——性能问题

问题描述

我有一个非常简单的设置,连接到 Pi Zero 的相机由外部信号触发。

问题:我的代码使用 v4l2 可实现的帧速率 <0.4fps。适用于例如:raspivid。

使用 raspivid 1920x1080 @ 50fps 可以按预期工作(使用 -pts 测试以保存时间码)。相反,我的代码永远不会使 50Hz 的缓冲区出队。在每 ~600ms 接收帧之前,我必须将触发率降低到 5Hz!这看起来我仍然只接收每三帧。返回的帧缓冲区总是跳过一个索引:

new frame 0: 3110400
time diff 0.6006

new frame 2: 3110400
time diff 0.6006

new frame 4: 3110400
time diff 0.600601

new frame 6: 3110400
time diff 0.6006

您可以在下面找到我的代码(曝光设置,...已删除)

问题:你能告诉我可能是什么问题吗?该程序只对缓冲区进行出队和入队,因此性能不应该成为问题。

#define NB_BUFFER 10

int main(int argc, char *argv[])
{
    int exposure = 50;
    int rows = 1080;
    int cols = 1920;
    
    if(argc > 1){
        exposure= atoi(argv[1]);
    }
    if(argc > 3){
        cols=atoi(argv[2]);
        rows=atoi(argv[3]);
    }


    struct vdIn vdin;
    struct vdIn *vd = &vdin;
    
    if((vd->fd = open("/dev/video0", O_RDWR)) < 0){
        perror("open");
        exit(1);
    }

    if(ioctl(vd->fd, VIDIOC_QUERYCAP, &vd->cap) < 0){
        perror("VIDIOC_QUERYCAP");
        exit(1);
    }

    if(!(vd->cap.capabilities & V4L2_CAP_VIDEO_CAPTURE)){
        fprintf(stderr, "The device does not handle single-planar video capture.\n");
        exit(1);
    }

    vd->fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd->fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_SBGGR8;//V4L2_PIX_FMT_BGR24;
    vd->fmt.fmt.pix.width = cols; 
    vd->fmt.fmt.pix.height = rows;

    if(ioctl(vd->fd, VIDIOC_S_FMT, &vd->fmt) < 0){
        perror("VIDIOC_S_FMT");
        exit(1);
    }
    
    struct v4l2_control control;
    
    
    control.id = V4L2_CID_EXPOSURE_AUTO ;
    control.value = V4L2_EXPOSURE_MANUAL;
    if(ioctl(vd->fd, VIDIOC_S_CTRL, &control) < 0){
        perror("VIDIOC_S_CTRL EXPOSURE MANUAL");
        exit(1);
    }
    
    .... iso manual, line frequency off, set exposure, auto whitebalance off
     

    vd->rb.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    vd->rb.memory = V4L2_MEMORY_MMAP;
    vd->rb.count = NB_BUFFER;

    if(ioctl(vd->fd, VIDIOC_REQBUFS, &vd->rb) < 0){
        perror("VIDIOC_REQBUFS");
        exit(1);
    }
    

    
    
    int ret;
    /* map the buffers */
    struct v4l2_buffer buf;
    for (int i = 0; i < NB_BUFFER; i++) {
        memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
        vd->buf.index = i;
        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vd->buf.memory = V4L2_MEMORY_MMAP;
        ret = ioctl (vd->fd, VIDIOC_QUERYBUF, &vd->buf);
        if (ret < 0) {
            fprintf (stderr, "Unable to query buffer (%d).\n", errno);
            return -1;
        }
        std::cout << "buf len " << vd->buf.length<<std::endl;
        vd->mem[i] = mmap (0 /* start anywhere */ ,
                   vd->buf.length, PROT_READ, MAP_SHARED, vd->fd,
                   vd->buf.m.offset);
        if (vd->mem[i] == MAP_FAILED) {
            fprintf (stderr, "Unable to map buffer (%d)\n", errno);
            return -1;
        }

    }
  /* Queue the buffers. */
    for (int i = 0; i < NB_BUFFER; ++i) {
        memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
        vd->buf.index = i;
        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vd->buf.memory = V4L2_MEMORY_MMAP;
        ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf);
        if (ret < 0) {
            fprintf (stderr, "Unable to queue buffer (%d).\n", errno);
            return -1;
        }
    }

    // Activate streaming
    int type = vd->fmt.type;
    if(ioctl(vd->fd, VIDIOC_STREAMON, &type) < 0){
        perror("VIDIOC_STREAMON");
        exit(1);
    }

    bool capture_is_running = true;
    double lastTimestamp=0;
    
    while(capture_is_running){
        
            
        memset (&vd->buf, 0, sizeof (struct v4l2_buffer));
        vd->buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        vd->buf.memory = V4L2_MEMORY_MMAP;
        
        std::cout << "try to dequeue" << std::endl;
        
        ret = ioctl (vd->fd, VIDIOC_DQBUF, &vd->buf);
        if (ret < 0) {
            fprintf (stderr, "Unable to dequeue buffer (%d).\n", errno);
            return -1;
        }
        
        cout << "new frame "<<vd->buf.index<<": "<< vd->buf.bytesused << endl;
        
        double timestamp = vd->buf.timestamp.tv_sec + vd->buf.timestamp.tv_usec/1000000.;
        double timeDiff = timestamp - lastTimestamp;
        lastTimestamp = timestamp;
        std::cout << "time diff " << timeDiff << std::endl;
        
        
        ret = ioctl (vd->fd, VIDIOC_QBUF, &vd->buf);
            if (ret < 0) {
            fprintf (stderr, "Unable to requeue buffer (%d).\n", errno);
            return -1;
        }
    }

    // Deactivate streaming
    if(ioctl(vd->fd, VIDIOC_STREAMOFF, &type) < 0){
        perror("VIDIOC_STREAMOFF");
        exit(1);
    }

    close(vd->fd);
    return EXIT_SUCCESS;

    return 0;
}

标签: clinuxv4l2raspberry-pi-zero

解决方案


推荐阅读