VIDIOC_QBUF:设备或资源繁忙 V4L2 内存用户

VIDIOC_QBUF: Device or resource busy V4L2 MEMORY USERPTR

本文关键字:V4L2 内存 用户 资源 QBUF VIDIOC      更新时间:2023-10-16

以下所有内容都使用

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

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

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

std::vector<uint8_t*> v4l2_buffers;

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

若要对缓冲区应用程序进行排队,请将结构v4l2_buffer的类型字段设置为以前与结构v4l2_format类型和结构v4l2_requestbuffers类型一起使用的相同缓冲区类型。应用程序还必须设置索引字段。有效索引号的范围从零到分配的缓冲区数VIDIOC_REQBUFS(结构v4l2_requestbuffers计数(减一。结构的内容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 字段必须包含指向结构v4l2_plane的填充数组的用户空间指针,并且长度字段必须设置为该数组中的元素数。 .若要对用户指针缓冲区应用程序进行排队,请将内存字段设置为 V4L2_MEMORY_USERPTR,将 m.userptr 字段设置为缓冲区的地址,并将长度设置为其大小。使用多平面 API 时,必须改用传递的结构v4l2_plane数组的 m.userptr 和长度成员。当使用指向此结构的指针调用 VIDIOC_QBUF 时,驱动程序将设置 V4L2_BUF_FLAG_QUEUED 标志并清除 flags 字段中的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;
};

相机流.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 pointern");
} 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 pointern");
}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

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

第一件事不要阅读文档,这只是一些误导性的话

为了将指针与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("%sn","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;

为了能够使用任何缓冲区,您必须设置大小图像(通常随格式一起提供(

接下来设置请求缓冲区:

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

激活流上

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