Gent Binaku
Gent Binaku

Reputation: 39

VIDIOC_QBUF: Device or resource busy V4L2 MEMORY USERPTR

All of the following are using the

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

Basically I have to stream from my camera computer. USING the YUV format I have roughly speaking new uint8_t[IMAGE_HEIGHT*IMAGE_WIDTH*2] which should be filled(enqueued).

The idea is that I have to make 5 frames which each points to uint8_t*.

std::vector<uint8_t*> v4l2_buffers;

Another class callled CameraStream will allocate the buffers and return a points to this frame which contains the picture.

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. .To enqueue a user pointer buffer applications set the memory field to V4L2_MEMORY_USERPTR, the m.userptr field to the address of the buffer and length to its size. When the multi-planar API is used, m.userptr and length members of the passed array of struct v4l2_plane have to be used instead. When VIDIOC_QBUF is called with a pointer to this structure the driver sets the V4L2_BUF_FLAG_QUEUED flag and clears the V4L2_BUF_FLAG_MAPPED and V4L2_BUF_FLAG_DONE flags in the flags field, or it returns an error code. This ioctl locks the memory pages of the buffer in physical memory, they cannot be swapped out to disk. Buffers remain locked until dequeued, until the VIDIOC_STREAMOFF or ioctl VIDIOC_REQBUFS ioctl is called, or until the device is closed.

/*
 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 contain a class Camera which just a init class

#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;
}

The following error is displayed in my terminal output.

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

Caution It is not allowed to mix queuing requests with queuing buffers directly. EBUSY will be returned if the first buffer was queued directly and then the application tries to queue a request, or vice versa. After closing the file descriptor, calling VIDIOC_STREAMOFF or calling ioctl VIDIOC_REQBUFS the check for this will be reset. For memory-to-memory devices you can specify the request_fd only for output buffers, not for capture buffers. Attempting to specify this for a capture buffer will result in an EBADR error.

Upvotes: 2

Views: 3624

Answers (1)

Gent Binaku
Gent Binaku

Reputation: 39

First thing first DO NOT READ THE DOCUMENATION, it's just some misguiding words

In order to use MEMORY_USR pointer with VIDIOC_QBUF, first you have to do the following.

Query the capabilities of the camera with:

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

Next set the format with:

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;

To be able to use buffers of any you must set the sizeImage(usually comes with the format)

Next set up the request buffer:

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

Query the buffer with index 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");
    }

Activate StreamOn

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

Caputering a Frame with the size example here:

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

}

Upvotes: 1

Related Questions