c++ - VIDIOC_QBUF : Device or resource busy V4L2 MEMORY USERPTR

标签 c++ linux kernel

以下所有都使用

#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的type字段设置为与以前使用struct v4l2_format类型和struct v4l2_requestbuffers类型相同的缓冲区类型。应用程序还必须设置索引字段。有效索引号的范围是零到使用ioctl VIDIOC_REQBUFS(结构v4l2_requestbuffers计数)减去1分配的缓冲区数。由ioctl VIDIOC_QUERYBUF ioctl返回的结构v4l2_buffer的内容也将执行。当缓冲区用于输出时(类型为V4L2_BUF_TYPE_VIDEO_OUTPUT,V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE或V4L2_BUF_TYPE_VBI_OUTPUT),应用程序还必须初始化字节使用的字段,字段和时间戳字段,有关详细信息,请参见缓冲区。应用程序还必须将标志设置为0。reserved2和reserved字段必须设置为0。使用多平面API时,m.planes字段必须包含指向struct v4l2_plane填充数组和length字段的用户空间指针必须设置为该数组中元素的数量。
要排队用户指针缓冲区应用程序,请将内存字段设置为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类,它只是一个初始化类
#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错误。

最佳答案

首先,不要读DOCUMENATION ,这只是一些误导性的单词

为了对VIDIOC_QBUF使用MEMORY_USR指针,首先必须执行以下操作。

使用以下命令查询摄像机的功能:

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

}

关于c++ - VIDIOC_QBUF : Device or resource busy V4L2 MEMORY USERPTR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59723314/

相关文章:

python - 从 Mac 和 Linux 机器获取本地用户

c++ - 在 ring0/kernel 中列出驱动程序?

windows - 驱动程序安装失败,因为交叉签名链不包含微软

c++ - 没有什么可发布的,八叉树是空的

linux - 如何为 imx6 交叉编译 QT 应用程序?

c - 函数 ‘mknod’ 的隐式声明,但我包含了 header

linux-kernel - 如何从给定的 inode 获取路径名、dentry 或 struct 文件?

c++ - 针对非类型参数特定值的模板代码优化。

C++ OpenCV 做人脸识别,直到相机被移除

c++ - 从函数 C++ 返回数组