【问题标题】:VIDIOC_QBUF: Device or resource busy V4L2 MEMORY USERPTRVIDIOC_QBUF:设备或资源繁忙 V4L2 MEMORY USERPTR
【发布时间】:2020-01-13 19:56:27
【问题描述】:

以下都是使用的

#include <linux/videodev2.h>
#include <vector>

基本上,我必须从我的相机计算机进行流式传输。 使用 YUV 格式我粗略地说 新的 uint8_t[IMAGE_HEIGHT*IMAGE_WIDTH*2] 应该被填充(入队)。

我的想法是我必须制作 5 个帧,每个帧都指向 uint8_t*。

std::vector<uint8_t*> v4l2_buffers;

另一个名为 CameraStream 的类将分配缓冲区并返回一个指向包含图片的帧的点。

要将缓冲区应用程序排入队列,请将 struct v4l2_buffer 的类型字段设置为与之前用于 struct v4l2_format 类型和 struct v4l2_requestbuffers 类型的缓冲区类型相同。应用程序还必须设置索引字段。有效索引编号范围从零到使用 ioctl VIDIOC_REQBUFS (struct v4l2_requestbuffers count) 减一分配的缓冲区数。由 ioctl VIDIOC_QUERYBUF ioctl 返回的 struct v4l2_buffer 的内容也可以。当缓冲区用于输出时(类型为 V4L2_BUF_TYPE_VIDEO_OUTPUT、V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE 或 V4L2_BUF_TYPE_VBI_OUTPUT)应用程序还必须初始化所使用的字节、字段和时间戳字段,请参阅缓冲区了解详细信息。应用程序还必须将 flags 设置为 0。reserved2 和 reserved 字段必须设置为 0。使用多平面 API 时,m.planes 字段必须包含指向 struct v4l2_plane 填充数组的用户空间指针和长度字段必须设置为该数组中的元素数。 . 要将用户指针缓冲区应用程序排队,请将内存字段设置为 V4L2_MEMORY_USERPTR,将 m.userptr 字段设置为缓冲区的地址,并将长度设置为缓冲区的大小。当使用多平面 API 时,必须使用传递的 struct v4l2_plane 数组的 m.userptr 和 length 成员。当使用指向此结构的指针调用 VIDIOC_QBUF 时,驱动程序会设置 V4L2_BUF_FLAG_QUEUED 标志并清除标志字段中的 V4L2_BUF_FLAG_MAPPED 和 V4L2_BUF_FLAG_DONE 标志,否则会返回错误代码。此 ioctl 将缓冲区的内存页面锁定在物理内存中,它们不能被换出到磁盘。缓冲区保持锁定状态,直到出队,直到调用 VIDIOC_STREAMOFF 或 ioctl VIDIOC_REQBUFS ioctl,或直到设备关闭。

/*
 Allocate 5 buffers and form and abstraction to buffers with a continous loop of buffers.
 CameraChannel must require a buffer from CameraStream class.
 Pass that buffer to v4l2 to fill with frame data
*/
#include <cstdint>
#include <linux/videodev2.h>
#include <fcntl.h>
#include <iostream>
#include <sys/mman.h>
#include <string.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include <vector>

#define FRAME_NUM 5

class CameraStream{  
    public:
        CameraStream(int fd);
        void allocateBuffer();  
        uint8_t *returnBufferAddress(); 
    private:
        int sfd;
        unsigned int n_buffers;
        v4l2_requestbuffers requestBuffers{0};
        std::vector<uint8_t*> v4l2_buffers;
};

CameraStream.cpp

#include "CameraStream.h"
#include "Camera.h"

CameraStream::CameraStream(int fd):sfd(fd){

}
void CameraStream::allocateBuffer(){

    /* This has to be the number of buffers I want to allocate in the device*/
    /* Don't forget to change BUF_NUM or FRAME_NUM */
    requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    requestBuffers.memory = V4L2_MEMORY_USERPTR;
    if(-1 == xioctl(sfd,VIDIOC_REQBUFS,&requestBuffers)){
                if(EINVAL == errno) {
                        perror("Device does not support user pointer\n");
                } else {
                        perror("VIDIOC_REQBUFS");
                }
    }
    /*
     Applications call the VIDIOC_QBUF ioctl to enqueue an empty (capturing)
     or filled (output) buffer in the driver’s incoming queue. 
     The semantics depend on the selected I/O method.
     To enqueue a buffer applications set the type field of a struct v4l2_buffer 
     to the same buffer type as was previously used with struct v4l2_format 
     type and struct v4l2_requestbuffers type. Applications must also set 
     the index field. Valid index numbers range from zero to the number of 
     buffers allocated with ioctl VIDIOC_REQBUFS (struct v4l2_requestbuffers 
     count) minus one. The contents of the struct v4l2_buffer returned by a 
     ioctl VIDIOC_QUERYBUF ioctl will do as well. When the buffer is intended
     for output (type is V4L2_BUF_TYPE_VIDEO_OUTPUT, 
     V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE, or V4L2_BUF_TYPE_VBI_OUTPUT) 
     applications must also initialize the bytesused, field and 
     timestamp fields, see Buffers for details.
     Applications must also set flags to 0. 
     The reserved2 and reserved fields must be set to 0. 
     When using the multi-planar API, the m.planes field 
     must contain a userspace pointer to a filled-in array
     of struct v4l2_plane and the
     length field must be set to the number of elements in that array.
    */
    for(int i = 0;i < FRAME_NUM ; i++){
       // v4l2_buffers.push_back(uint8_t[IMAGE_HEIGHT*IMAGE_WIDTH*2]);
       v4l2_buffers.push_back(new uint8_t[IMAGE_HEIGHT*IMAGE_WIDTH*2]);
    }

    struct v4l2_buffer buf;
    buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    buf.memory = V4L2_MEMORY_USERPTR;
    buf.m.userptr = (unsigned long)&v4l2_buffers[0];
    buf.index = 0;
    buf.length = 1;
    if(xioctl(sfd,VIDIOC_QBUF,&buf) == -1){
                  perror("VIDIOC_QBUF");
    }


    /*
    This ioctl is part of the streaming I/O method. 
    It can be used to query the status of a buffer at any time 
    after buffers have been allocated with the ioctl VIDIOC_REQBUFS ioctl.
    */
    //struct v4l2_buffer buf;
    //for(int j = 0;j < IMAGE_HEIGHT*IMAGE_WIDTH*2;j++){
        //buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        //buf.index = j;
        //if(xioctl(sfd,VIDIOC_QUERYBUF,&buf) == -1){
        //           perror("VIDIOC_QUERYBUF");
       // }
    //}
    /*
    v4l2_buffers.resize(BUF_NUM,std::vector<uint8_t*>(IMAGE_WIDTH*IMAGE_HEIGHT*2));
    for(auto &frame:v4l2_buffers){
        int c = 0;
          for(auto& buffer:frame){ 
                struct v4l2_buffer buf;
                buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
                buf.memory = V4L2_MEMORY_USERPTR;
                buf.index = c++;
                buf.m.userptr = (unsigned long)&buffer;
                buf.length = sizeof(buffer);
                if(-1 == xioctl(sfd,VIDIOC_QBUF,&buf))
                        perror("VIDIOC_QBUF");
           }
    }
    */

    /*
    memset(&(requestBuffers),0,sizeof(requestBuffers));
    requestBuffers.count = BUF_NUM;
    requestBuffers.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    requestBuffers.memory = V4L2_MEMORY_USERPTR;
    if(-1 == xioctl(sfd,VIDIOC_REQBUFS,&requestBuffers)){
        if(EINVAL == errno){
            perror("Device does not support user pointer\n");
        }else{
            perror("VIDIOC_REQBUFS");
        }
    }
    struct v4l2_buffer buf;
    for(n_buffers = 0;n_buffers < BUF_NUM;++n_buffers){
        buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
        buf.index = n_buffers;
        if(xioctl(sfd, VIDIOC_QUERYBUF, &buf) == -1){
                perror("VIDIOC_QUERYBUF");
        }
        //*Create the buffer 
    }
    */
}

main.cpp 包含一个 Camera 类,它只是一个 init 类

#include <iostream>
#include "Camera.h"
#include "CameraStream.h"

int main(){
    char *device = "/dev/video0";
    Camera c(device);
    c.open();
    c.showCapabilities();
    c.config(V4L2_PIX_FMT_YUYV);
    CameraStream cam(c.getFd());
    cam.allocateBuffer();

    return 0;
}

我的终端输出中显示以下错误。

open
This device has capabilities
Device does  support this format, VIDIOC_S_FMT: Success
VIDIOC_QBUF: Device or resource busy

注意 不允许将排队请求与排队缓冲区直接混合。如果第一个缓冲区直接排队,然后应用程序尝试排队请求,则将返回 EBUSY,反之亦然。关闭文件描述符后,调用 VIDIOC_STREAMOFF 或调用 ioctl VIDIOC_REQBUFS 将重置对此的检查。 对于内存到内存设备,您可以仅为输出缓冲区指定 request_fd,而不能为捕获缓冲区指定。尝试为捕获缓冲区指定此项将导致 EBADR 错误。

【问题讨论】:

  • 不熟悉这个工具,所以我帮不上忙,但看起来你在这里接近minimal reproducible example。您应该完成它,以使可以回答问题的人和将来寻找类似问题答案的人更容易。再加上为minimal reproducible example 打包代码,您经常会自己发现并解决问题。如果您这样做,请转发并自行回答。
  • 感谢您的回复,也许我只是愚蠢
  • 嗯,您是否尝试将 requestBuffers.count 设置为 >0?在您的情况下,它看起来将是默认值 0 (即没有请求的缓冲区,然后没有排队)。只是一个想法
  • 我有,但根据 MEMORY_USERPTR 的文档,您不需要这样做,因为缓冲区将在用户空间中分配。这意味着我只需要 VIDIOC_QBUF 我想使用的所有缓冲区,但它不会工作。
  • 如果世界上任何人都可以提供帮助,我会很感激我被困住了。我希望我更聪明

标签: c++ linux kernel


【解决方案1】:

首先不要阅读文档,这只是一些误导性的话

为了将 MEMORY_USR 指针与 VIDIOC_QBUF 一起使用,首先您必须执行以下操作。

查询相机的功能:

if (xioctl(mFd, VIDIOC_QUERYCAP, &capability) < 0) {
            perror("Failed to get device capabilities");
        }
    if (!(capability.capabilities & V4L2_CAP_VIDEO_CAPTURE)
            || !(capability.capabilities & V4L2_CAP_STREAMING)) 
    {
            perror("This device cannot stream video");
            exit(1);
    }
    printf("%s\n","This device has capabilities");

接下来设置格式:

v4l2_format format;

    format.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    format.fmt.pix.pixelformat = pfmt;
    format.fmt.pix.width = 640;
    format.fmt.pix.height = 480;
    if(ioctl(mFd,VIDIOC_S_FMT,&format) == -1){
            perror("Unable to set format");
    }
    sizeImage = format.fmt.pix.sizeimage;
    std::cout<<"imgsize :\n"<<sizeImage<<std::endl;

为了能够使用任何缓冲区,您必须设置 sizeImage(通常带有格式)

接下来设置请求缓冲区:

v4l2_requestbuffers bufrequest;
    CLEAR(bufrequest);
    bufrequest.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    bufrequest.memory = V4L2_MEMORY_USERPTR;
    bufrequest.count = 1;
    if(-1 == xioctl(mFd,VIDIOC_REQBUFS,&bufrequest)){
                if(EINVAL == errno) {
                        perror("Device does not support user pointer\n");
                } else {
                        perror("VIDIOC_REQBUFS");
                }
    }

查询索引为 0 的缓冲区

CLEAR(mBuffferInfo);
    mBuffferInfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    mBuffferInfo.memory = V4L2_MEMORY_USERPTR;
    mBuffferInfo.index = 0;
    if(-1 == xioctl(mFd,VIDIOC_QUERYBUF,&mBuffferInfo)){
        perror("VIDIOC_QUERYBUF");
    }

激活 StreamOn

type = mBuffferInfo.type;
    if(-1 == xioctl(mFd,VIDIOC_STREAMON,&type)){
        perror("STREAMON");
    }
}

在此处使用大小示例捕获框架:

void Camera::captureFrame(uint8_t* frame){
     memset(frame,0,sizeImage);
     CLEAR(mBuffferInfo);
     mBuffferInfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
     mBuffferInfo.memory = V4L2_MEMORY_USERPTR;
     mBuffferInfo.index = 0;
     mBuffferInfo.m.userptr = (unsigned long)frame;
     mBuffferInfo.length = sizeImage;
     if(-1 == xioctl(mFd,VIDIOC_QBUF,&mBuffferInfo)){
                perror("VIDIOC_QBUF");
     }
    CLEAR(mBuffferInfo);
    mBuffferInfo.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
    mBuffferInfo.memory = V4L2_MEMORY_USERPTR;
     if(-1 == xioctl(mFd,VIDIOC_DQBUF,&mBuffferInfo)){
                perror("VIDIOC_DQBUF");
     }

}

【讨论】:

    猜你喜欢
    • 2017-09-02
    • 1970-01-01
    • 2016-08-29
    • 2015-09-24
    • 1970-01-01
    • 1970-01-01
    • 2023-03-06
    • 2022-12-03
    • 2020-11-04
    相关资源
    最近更新 更多