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

Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐