linux下使用QT v4l2接口连接usb摄像头,杜绝画面割裂
·
v4l2capture.cpp
#include "v4l2capture.h"
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <QDebug>
#include <sys/ioctl.h> // 必须包含的核心头文件
#include <QtGui/QRgb> // 基础类型定义
#include <QtGui/QImage> // 图像处理类
V4L2Capture::V4L2Capture(QObject *parent) : QObject(parent) {}
bool V4L2Capture::openDevice(const char* device, int w, int h) {
fd = open(device, O_RDWR);
if (fd < 0) {
qWarning() << "Open device failed";
return false;
}
qDebug() << "Open device success";
v4l2_capability cap;
if (ioctl(fd, VIDIOC_QUERYCAP, &cap) < 0) {
qWarning() << "Not a V4L2 device";
close(fd);
return false;
}
qDebug() << "V4L2 device";
v4l2_format fmt = {};
fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
fmt.fmt.pix.width = w;
fmt.fmt.pix.height = h;
fmt.fmt.pix.pixelformat = V4L2_PIX_FMT_YUYV;
fmt.fmt.pix.field = V4L2_FIELD_NONE;
if (ioctl(fd, VIDIOC_S_FMT, &fmt) < 0) {
qWarning() << "Set format failed";
close(fd);
return false;
}
qDebug() << "Set format success";
width = fmt.fmt.pix.width;
height = fmt.fmt.pix.height;
return initMMap();
}
int V4L2Capture::initMMap() {
v4l2_requestbuffers req = {};
req.count = 8;
req.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
req.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_REQBUFS, &req) < 0) {
qWarning() << "Request buffers failed";
return -1;
}
qDebug() << "Request buffers success";
buffers = new buffer[req.count];
for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
v4l2_buffer buf = {};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = n_buffers;
if (ioctl(fd, VIDIOC_QUERYBUF, &buf) < 0) {
qWarning() << "Query buffer failed";
return -1;
}
qDebug() << "Query buffer success";
buffers[n_buffers].length = buf.length;
buffers[n_buffers].start = mmap(NULL, buf.length,
PROT_READ | PROT_WRITE,
MAP_SHARED, fd, buf.m.offset);
if (buffers[n_buffers].start == MAP_FAILED) {
qWarning() << "MMap failed";
return -1;
}
qDebug() << "MMap success";
}
return 0;
}
void V4L2Capture::startCapture() {
for (unsigned int i = 0; i < n_buffers; ++i) {
v4l2_buffer buf = {};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
buf.index = i;
ioctl(fd, VIDIOC_QBUF, &buf);
}
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMON, &type);
}
void V4L2Capture::stopCapture() {
if (fd == -1) return;
v4l2_buf_type type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
ioctl(fd, VIDIOC_STREAMOFF, &type); // 停止视频流:ml-citation{ref="1" data="citationList"}
for (unsigned int i = 0; i < n_buffers; ++i) {
munmap(buffers[i].start, buffers[i].length); // 释放内存映射:ml-citation{ref="1,2" data="citationList"}
}
}
bool V4L2Capture::processFrame() {
v4l2_buffer buf = {};
buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
buf.memory = V4L2_MEMORY_MMAP;
if (ioctl(fd, VIDIOC_DQBUF, &buf) < 0) {
return false;
}
QImage image(width, height, QImage::Format_RGB16); // RGB565对应Qt的Format_RGB16
const uchar* yuyv = static_cast<uchar*>(buffers[buf.index].start);
uint16_t* rgb565 = reinterpret_cast<uint16_t*>(image.bits());
// // YUYV直接转RGB565 这个算法画面有割裂
// for (int i = 0, j = 0; i < width * height * 2; i += 4, j += 2) {
// int y0 = yuyv[i];
// int u = yuyv[i+1];
// int y1 = yuyv[i+2];
// int v = yuyv[i+3];
// // 像素1转换
// int r0 = qBound(0, static_cast<int>(y0 + 1.402*(v-128)), 255);
// int g0 = qBound(0, static_cast<int>(y0 - 0.34414*(u-128) - 0.71414*(v-128)), 255);
// int b0 = qBound(0, static_cast<int>(y0 + 1.772*(u-128)), 255);
// rgb565[j] = ((r0 & 0xF8) << 8) | ((g0 & 0xFC) << 3) | (b0 >> 3);
// // 像素2转换
// int r1 = qBound(0, y1 + static_cast<int>(1.402*(v-128)), 255);
// int g1 = qBound(0, y1 - static_cast<int>(0.34414*(u-128) - 0.71414*(v-128)), 255);
// int b1 = qBound(0, y1 + static_cast<int>(1.772*(u-128)), 255);
// rgb565[j+1] = ((r1 & 0xF8) << 8) | ((g1 & 0xFC) << 3) | (b1 >> 3);
// }
for (int i = 0, j = 0; i < width * height * 2; i += 4, j += 2) {
// 提取YUV分量(使用查表法优化减128操作)
const int y0 = yuyv[i];
const int u = yuyv[i+1] - 128;
const int y1 = yuyv[i+2];
const int v = yuyv[i+3] - 128;
// 使用整数运算替代浮点(参考BT.601标准系数缩放256倍)
const int v_1_402 = (358 * v) >> 8; // 1.402*256=359
const int u_0_344 = (88 * u) >> 8; // 0.344*256=88
const int v_0_714 = (183 * v) >> 8; // 0.714*256=183
const int u_1_772 = (454 * u) >> 8; // 1.772*256=454
// 像素1转换(使用qBound替代条件判断)
int r0 = y0 + v_1_402;
int g0 = y0 - u_0_344 - v_0_714;
int b0 = y0 + u_1_772;
rgb565[j] = ((qBound(0, r0, 255) & 0xF8) << 8) |
((qBound(0, g0, 255) & 0xFC) << 3) |
(qBound(0, b0, 255) >> 3);
// 像素2转换(复用已计算的UV系数)
int r1 = y1 + v_1_402;
int g1 = y1 - u_0_344 - v_0_714;
int b1 = y1 + u_1_772;
rgb565[j+1] = ((qBound(0, r1, 255) & 0xF8) << 8) |
((qBound(0, g1, 255) & 0xFC) << 3) |
(qBound(0, b1, 255) >> 3);
}
emit frameReady(image);
if (ioctl(fd, VIDIOC_QBUF, &buf) < 0) {
return false;
}
return true;
}
V4L2Capture::~V4L2Capture() {
stopCapture();
if (fd != -1) {
close(fd);
}
if (buffers) {
for (unsigned int i = 0; i < n_buffers; ++i) {
munmap(buffers[i].start, buffers[i].length);
}
delete[] buffers;
}
}
v4l2capture.h
#ifndef V4L2CAPTURE_H
#define V4L2CAPTURE_H
#include <QObject>
#include <linux/videodev2.h>
class V4L2Capture : public QObject
{
Q_OBJECT
public:
explicit V4L2Capture(QObject *parent = nullptr);
~V4L2Capture();
bool openDevice(const char* device, int width, int height);
void startCapture();
void stopCapture();
int initMMap();
bool processFrame();
int fd = -1;
struct buffer {
void *start;
size_t length;
} *buffers;
unsigned int n_buffers;
int width;
int height;
signals:
void frameReady(const QImage &frame);
};
#endif // V4L2CAPTURE_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDebug>
#include <QTimer>
#include <sys/ioctl.h> // 必须包含的核心头文件
#include <fcntl.h> // 文件控制选项
#include <unistd.h> // POSIX标准头文件
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
capture = new V4L2Capture(this);
int ret=capture->openDevice("/dev/video0", 400, 300);
if (ret<0) {
qWarning() << "Failed to open camera"<<ret;
return;
}
connect(capture, &V4L2Capture::frameReady, this, &MainWindow::updateFrame);
capture->startCapture();
QTimer *timer = new QTimer(this);
connect(timer, &QTimer::timeout, capture, &V4L2Capture::processFrame);
timer->start(33); // ~30fps
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::updateFrame(const QImage& image) {
ui->label->setPixmap(QPixmap::fromImage(image).scaled(
ui->label->size(), Qt::KeepAspectRatio));
}
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include "v4l2capture.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
V4L2Capture *capture;
private slots:
void updateFrame(const QImage& image);
private:
Ui::MainWindow *ui;
};
#endif // MAINWINDOW_H
更多推荐




所有评论(0)