VIDIOC_QBUF: устройство или ресурс занят V4L2 MEMORY USERPTR - PullRequest
2 голосов
/ 13 января 2020

Все нижеперечисленное использует

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

По сути, мне приходится передавать с компьютера камеры. ИСПОЛЬЗУЯ формат YUV, я грубо говорю new uint8_t [IMAGE_HEIGHT * IMAGE_WIDTH * 2] , который должен быть заполнен (поставлен в очередь).

Идея в том, что мне нужно сделать 5 кадров, каждый из которых указывает на uint8_t *.

std::vector<uint8_t*> v4l2_buffers;

Другой класс, называемый CameraStream, выделит буферы и вернет точку этому кадру, содержащему изображение.

Чтобы поставить в буфер приложения, задайте поле типа структуры v4l2_buffer к тому же типу буфера, который использовался ранее с типом struct v4l2_format и типом struct v4l2_requestbuffers. Приложения также должны установить поле индекса. Допустимые номера индексов варьируются от нуля до количества буферов, выделенных с помощью ioctl VIDIOC_REQBUFS (struct v4l2_requestbuffers count) минус один. Содержимое struct v4l2_buffer, возвращаемое ioctl VIDIOC_QUERYBUF ioctl, тоже подойдет. Когда буфер предназначен для вывода (типом является V4L2_BUF_TYPE_VIDEO_OUTPUT, V4L2_BUF_TYPE_VIDEO_OUTPUT_MPLANE или V4L2_BUF_TYPE_VBI_OUTPUT), приложения также должны инициализировать информацию об использованных байтах, поля и метки времени см. В разделах «Байт и метка времени»; Приложения также должны установить флаги на 0. Зарезервированные поля 2 и зарезервированные должны быть установлены на 0. При использовании многоплоскостного API поле m.planes должно содержать указатель пользовательского пространства на заполненный массив struct v4l2_plane и поле длины должен быть установлен на количество элементов в этом массиве. .Чтобы поставить в очередь приложения-указатели в буфере, установите для поля памяти значение V4L2_MEMORY_USERPTR, для поля m.userptr - адрес буфера, а для длины - его размер. Когда используется многоплоскостной API, вместо него должны использоваться члены m.userptr и length переданного массива struct v4l2_plane. Когда VIDIOC_QBUF вызывается с указателем на эту структуру, драйвер устанавливает флаг V4L2_BUF_FLAG_QUEUED и очищает флаги V4L2_BUF_FLAG_MAPPED и V4L2_BUF_FLAG_DONE в поле flags или возвращает код ошибки. Этот ioctl блокирует страницы памяти буфера в физической памяти, они не могут быть выгружены на диск. Буферы остаются заблокированными до тех пор, пока не будут сняты, до тех пор, пока ioctl VIDIOC_STREAMOFF или ioctl VIDIOC_REQBUFS не будет вызван, или пока устройство не будет закрыто.

/*
 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.

1 Ответ

1 голос
/ 14 января 2020

Перво-наперво НЕ ПРОЧИТАЙТЕ ДОКУМЕНТАЦИЮ , это всего лишь несколько вводящих в заблуждение слов

Чтобы использовать указатель 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");
     }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...