Files
2026-08-24 10:03:24 +08:00

431 lines
11 KiB
C++

#include "v4l2video.h"
#include <QDebug>
#include <iostream>
#include <sys/time.h>
V4l2Video::V4l2Video()
{
this->pBuffers = NULL;
this->fd = -1;
this->uCount = 5;
this->uIndex = -1;
}
V4l2Video::~V4l2Video()
{
}
int V4l2Video::openDevice(const char* device_name)
{
qDebug() << "device_name: " << device_name << "\n";
int fd = open(device_name, O_RDWR, 0);
if (-1 == fd)
{
perror("open error");
return -1;
}
qDebug() << "Open device successfully" << "\n";
return fd;
}
int V4l2Video::closeDevice(int fd)
{
if (-1 == close(fd))
{
return -1;
}
return 0;
}
int V4l2Video::isValidDevice(int fd)
{
struct v4l2_capability cap;
memset(&cap, 0, sizeof(cap));
if (-1 == ioctl(fd, VIDIOC_QUERYCAP, &cap))
{
perror("ioctl VIDIOC_QUERYCAP error");
return -1;
}
if (!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE))
{
return -1;
}
if (! (cap.capabilities & V4L2_CAP_STREAMING))
{
return -1;
}
printf("Capability infrmations:\n");
printf("diver : %s \n", cap.driver);
printf("card : %s \n", cap.card);
printf("bus_info : %s \n", cap.bus_info);
printf("version : %08x \n", cap.version);
printf("capabilities : %08x\n", cap.capabilities);
printf("\n\n");
return 0;
}
int V4l2Video::setCaptureFormat(int fd, struct v4l2_format *fmt)
{
if (-1 == ioctl(fd, VIDIOC_S_FMT, fmt))
{
perror("ioctl VIDIOC_S_FMT error");
return -1;
}
return 0;
}
int V4l2Video::setCaptureParam(int fd, struct v4l2_streamparm *stream_param)
{
if (-1 == ioctl(fd, VIDIOC_S_PARM, stream_param))
{
perror("ioctl VIDIOC_S_PARAM error");
return -1;
}
return 0;
}
QString V4l2Video::getDefaultResolution()
{
QString default_resolution = QString::number(default_fmt.width) + "x" + QString::number(default_fmt.height);
return default_resolution;
}
int V4l2Video::getSupportedFormat(int fd, QString &format_text,
QMap<__u32, QMap<QString, QVector<QString> > > &camera_format)
{
// VIDIOC_ENUM_FMT
int ret = 0;
struct v4l2_fmtdesc fmt;
QString str;
QMap<QString, QVector<QString> > reso_fps;
QString internal_resolution;
QVector <QString> fps;
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
for (int i = 0; ; i++) // maybe more than one format
{
fmt.index = i;
ret = ioctl(fd, VIDIOC_ENUM_FMT, &fmt);
if (-1 == ret) // all kinds of format
{
break;
}
/* print current image format */
printf("========== Picture Format: %s ==========\n", fmt.description);
//str = QString("========== Picture Format: %1 ==========\n").arg(fmt.description);
str.sprintf("========== Picture Format: %s ==========\n", fmt.description);
format_text += str;
/* query related resolution(maybe more than one resolution) depends on current pixel format */
struct v4l2_frmsizeenum frmsize;
frmsize.pixel_format = fmt.pixelformat;
for (int j = 0; ; j++) // all kinds of resolution
{
frmsize.index = j;
ret = ioctl(fd, VIDIOC_ENUM_FRAMESIZES, &frmsize);
if (-1 == ret) // done
{
break;
}
/* print current resolution */
printf(">>>>>>>> resolution: %dx%d <<<<<<<<\n",
frmsize.discrete.width, frmsize.discrete.height);
str = QString(">>>>>>>> resolution: %1x%2 <<<<<<<<\n").arg(frmsize.discrete.width).arg(frmsize.discrete.height);
internal_resolution = QString("%1x%2").arg(frmsize.discrete.width).arg(frmsize.discrete.height);
format_text += str;
// index(0) set to be the current default fmt
if (V4L2_PIX_FMT_MJPEG == fmt.pixelformat && frmsize.index == 0) {
this->default_fmt.pixel_format = fmt.pixelformat;
this->default_fmt.width = frmsize.discrete.width;
this->default_fmt.height = frmsize.discrete.height;
}
struct v4l2_frmivalenum framival;
for (int k = 0; ; k++)
{
framival.index = k;
framival.pixel_format = fmt.pixelformat;
framival.width = frmsize.discrete.width;
framival.height = frmsize.discrete.height;
if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &framival) < 0)
{
printf("\n");
format_text += "\n";
break;
}
/* print current fps */
__u32 fps_tmp = framival.discrete.denominator / framival.discrete.numerator;
printf("fps:%d ", fps_tmp);
str = "fps:" + QString::number(fps_tmp);
format_text += str;
fps.append(QString::number(fps_tmp));
}
printf("fps.size: %d\n", fps.size());
reso_fps.insert(internal_resolution, fps);
fps.clear();
}
camera_format.insert(fmt.pixelformat, reso_fps);
reso_fps.clear();
printf("============== Current Format End ==============\n\n");
format_text += "============== Current Format End ==============\n\n";
}
return 0;
}
int V4l2Video::initDevice(int fd, QString &format_text,
QMap<__u32, QMap<QString, QVector<QString> > > &camera_format)
{
if (isValidDevice(fd) != 0)
{
qDebug() << "invalid device" << "\n";
return -1;
}
getSupportedFormat(fd, format_text, camera_format);
struct v4l2_format fmt;
memset(&fmt, 0, sizeof(fmt));
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
//fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.pixelformat = this->default_fmt.pixel_format;
fmt.fmt.pix.width = this->default_fmt.width;
fmt.fmt.pix.height = this->default_fmt.height;
fmt.fmt.pix.field = V4L2_FIELD_ANY;
printf("default format, pixelformat:%s, width:%d, height:%d\n",
"Motion-JPEG", this->default_fmt.width, this->default_fmt.height);
setCaptureFormat(fd, &fmt);
/*
NOTE(if needed):
可通过 struct v4l2_streamparm 结构体设置帧率
struct v4l2_streamparm stream_param;
stream_param.parm.capture.timeperframe.numerator; // 分子(1)
stream_param.parm.capture.timeperframe.denominator // 分母(30)
*/
struct v4l2_streamparm stream_param;
stream_param.type=V4L2_BUF_TYPE_VIDEO_CAPTURE;
stream_param.parm.capture.timeperframe.numerator = 1;
stream_param.parm.capture.timeperframe.denominator = 30;
if (-1 == ioctl(fd, VIDIOC_S_PARM, &stream_param)) {
perror("set fps: ioctl VIDIOC_S_PARM error");
return -1;
}
return 0;
}
int V4l2Video::initBuffer(int fd)
{
struct v4l2_requestbuffers reqbuf;
memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.count = this->uCount;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(fd, VIDIOC_REQBUFS, &reqbuf))
{
perror("ioctl VIDIOC_REQBUFS error");
return -1;
}
if ((reqbuf.count < 2) || (reqbuf.count > 5))
{
return -1;
}
this->pBuffers = (VideoBuffer *)calloc(reqbuf.count, sizeof(VideoBuffer));
if (NULL == this->pBuffers)
{
perror("calloc error");
return -1;
}
unsigned int numBufs = 0;
for (numBufs = 0; numBufs < reqbuf.count; numBufs++)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = numBufs;
if (-1 == ioctl(fd, VIDIOC_QUERYBUF, &buf))
{
perror("ioctl VIDIOC_QUERYBUF error");
return -1;
}
pBuffers[numBufs].uLength = buf.length;
pBuffers[numBufs].pStart = (unsigned char *)mmap(NULL, buf.length, PROT_READ |
PROT_WRITE, MAP_SHARED, fd, buf.m.offset);
if (NULL == pBuffers[numBufs].pStart)
{
perror("buffer error");
return -1;
}
}
qDebug() << "Init buffer successfully." << "\n";
return 0;
}
int V4l2Video::startCapturing(int fd)
{
unsigned int i = 0;
for (i = 0; i < this->uCount; i ++)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
if (-1 == ioctl(fd, VIDIOC_QBUF, &buf))
{
perror("ioctl VIDIOC_QBUF error");
return -1;
}
}
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == ioctl(fd, VIDIOC_STREAMON, &type))
{
perror("ioctl VIDIOC_STREAMON error");
return -1;
}
qDebug() << "Start capturing!!!!!!!!!!!!!" << "\n";
return 0;
}
int V4l2Video::stopCapturing(int fd)
{
enum v4l2_buf_type type;
type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
if (-1 == ioctl(fd, VIDIOC_STREAMOFF, &type))
{
perror("ioctl VIDIOC_STREAMOFF error");
return -1;
}
unsigned int numBufs = 0;
for (numBufs = 0; numBufs < this->uCount; numBufs++)
{
munmap(pBuffers[numBufs].pStart, pBuffers[numBufs].uLength);
pBuffers[numBufs].pStart = NULL;
}
struct v4l2_requestbuffers reqbuf;
memset(&reqbuf, 0, sizeof(reqbuf));
reqbuf.count = 0;
reqbuf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
reqbuf.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(fd, VIDIOC_REQBUFS, &reqbuf))
{
perror("ioctl VIDIOC_REQBUFS error");
return -1;
}
if (this->pBuffers) free(this->pBuffers);
return 0;
}
int V4l2Video::checkVideoFd(int fd)
{
// use select to check whether this fd is readable
fd_set rfds;
FD_ZERO(&rfds);
FD_SET(fd, &rfds);
int retval = select(fd + 1, &rfds, NULL, NULL, NULL);
if (retval <= 0) {
printf("select fail %d, %d %s\n", retval, errno, strerror(errno));
return -1;
}
if (fd > 0 && FD_ISSET(fd, &rfds))
{
return 0;
}
else
{
return -1;
}
}
int V4l2Video::dequeueFrame(int fd, void **out_frame, int *frame_len, int *index)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (-1 == ioctl(fd, VIDIOC_DQBUF, &buf))
{
perror("ioctl VIDIOC_DQBUF error");
return -1;
}
*out_frame = this->pBuffers[buf.index].pStart;
*frame_len = this->pBuffers[buf.index].uLength;
//this->uIndex = buf.index;
*index = buf.index;
struct timespec ts;
int fps;
clock_gettime(CLOCK_MONOTONIC, &ts);
int64_t last_frame_pts = this->pts;
this->pts = ts.tv_sec * 1000000ULL + ts.tv_nsec / 1000ULL;
if (last_frame_pts != 0) {
fps = 1000000ULL / (this->pts - last_frame_pts);
qDebug() << "pts: " << this->pts
<< "true fps: " << fps << "\n";
}
return 0;
}
int V4l2Video::queueFrame(int fd, int index)
{
if (-1 != index)
{
struct v4l2_buffer buf;
memset(&buf, 0, sizeof(buf));
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = index;
if (-1 == ioctl(fd, VIDIOC_QBUF, &buf))
{
return -1;
}
return 0;
}
return -1;
}