QT实战之监控系统

121 阅读3分钟

Qt 入门实战:用 200 行代码写个“能看又能录”的轻量级监控客户端

适合:刚学完 C++/Qt Widgets、想第一次调用摄像头、再顺手把画面推流/存盘的同学
源码:文末 gitee 链接,MIT 协议可商用
环境:Win10 + Qt 5.15.2(MinGW)/ Ubuntu 20.04 + Qt 6.5,OpenCV 4.8


一、需求拆解:最小可玩 MVP

  1. 实时预览:1 路 USB 摄像头,1080p@30fps,延迟 < 200 ms
  2. 一键录制:点“开始”→生成带时间戳的 MP4,码率 2 Mbps
  3. 最小依赖:Qt 自带多媒体模块 + OpenCV(仅编码用,UI 层不耦合)
  4. 可扩展:后续加 RTSP 推流、AI 检测、Web 后台,只需换 DLL/插件

二、技术选型对比(为什么这样搭)

方案优点缺点本例取舍
QtMultimediaQt 官方、API 简单Win 下后端依赖 WMF,低版本缺编码器预览层用它
QCamera + QAbstractVideoFilter可 GPU 滤镜文档少,录制格式受限仅预览
OpenCV VideoWriter跨平台、编码器丰富需额外装库录制用它
FFmpeg 直接 API最灵活学习曲线陡留作二期

结论:
预览 = QtMultimedia(最省事)
录制 = OpenCV(四行代码就能写 MP4)


三、项目骨架:文件树一览

LightMonitor/
 ├─ main.cpp
 ├─ mainwindow.ui
 ├─ mainwindow.cpp / .h
 ├─ cvrecorder.cpp / .h        // 封装 OpenCV 录制
 ├─ qcvvideoprobe.cpp / .h     // 把 QVideoFrame 转 cv::Mat
 ├─ CMakeLists.txt
 └─ README.md

.pro 文件(Qt5):

QT += core gui multimedia multimediawidgets
CONFIG += c++17
win32 {
    INCLUDEPATH += $$PWD/third_party/opencv/win/include
    LIBS += -L$$PWD/third_party/opencv/win/lib -lopencv_world480
}
unix {
    LIBS += `pkg-config --libs opencv4`
}

四、核心代码拆解

1. 摄像头初始化(Qt 侧)

// mainwindow.cpp
void MainWindow::initCamera()
{
    cameras = QCameraInfo::availableCameras();
    if (cameras.isEmpty()) { QMessageBox::critical(this, "Error", "No camera"); return; }

    camera = new QCamera(cameras.first());
    viewfinder = new QCameraViewfinder(this);
    camera->setViewfinder(viewfinder);
    ui->verticalLayout->addWidget(viewfinder);

    probe = new QCvVideoProbe(this);          // 自定义探头
    probe->setSource(camera);                 // 把帧抄一份给 OpenCV

    camera->start();
}

2. 帧探头:QVideoFrame → cv::Mat

// qcvvideoprobe.cpp
bool QCvVideoProbe::setSource(QCamera *camera)
{
    // Qt6 用 QMediaCaptureSession,Qt5 用 QCamera->setViewfinder(probe)
    return camera->setViewfinder(this) && QAbstractVideoFilter::isActive();
}

bool QCvVideoProbe::present(const QVideoFrame &frame)
{
    if (frame.isValid()) {
        QVideoFrame cloneFrame(frame);  // 必须 clone 再 map
        cloneFrame.map(QAbstractVideoBuffer::ReadOnly);
        cv::Mat mat(frame.height(), frame.width(),
                    CV_8UC3,                // 假设 RGB24
                    cloneFrame.bits(),
                    cloneFrame.bytesPerLine());
        emit matReady(mat.clone());       // 深拷贝后尽快 unmap
        cloneFrame.unmap();
    }
    return true;
}

3. 录制器:OpenCV 四行写 MP4

// cvrecorder.h
class CvRecorder : public QObject
{
    Q_OBJECT
public:
    void startRecording(const QString &fileName)
    {
        writer.open(fileName.toLocal8Bit().toStdString(),
                   cv::VideoWriter::fourcc('m','p','4','v'),
                   30, cv::Size(1920,1080), true);
        if (!writer.isOpened()) qWarning() << "Codec fail";
        recording = true;
    }
    void stop() { recording = false; writer.release(); }
public slots:
    void write(cv::Mat frame) { if (recording) writer.write(frame); }
private:
    cv::VideoWriter writer;
    bool recording = false;
};

4. 界面逻辑:两个按钮

void MainWindow::on_startBtn_clicked()
{
    QString file = QDateTime::currentDateTime().toString("yyyyMMdd_hhmmss") + ".mp4";
    recorder->startRecording(file);
}
void MainWindow::on_stopBtn_clicked() { recorder->stop(); }

连接信号:

connect(probe, &QCvVideoProbe::matReady,
        recorder, &CvRecorder::write, Qt::QueuedConnection);

五、编译 & 运行

  1. Win:

    • 安装 Qt 5.15.2 + CMake 3.24
    • 下载 OpenCV-MinGW 版,把 bin/*.dll 拷到 build/Debug
    • cmake -B build -G "MinGW Makefiles" && cmake --build build
  2. Ubuntu:
    sudo apt install qtbase5-dev qtmultimedia5-dev libopencv-dev
    cmake -B build && cmake --build build

运行效果:

  • 左侧 1080p 实时画面,右侧“开始/停止”两按钮;
  • 录制结束在同目录生成 20250930_143022.mp4,VLC 播放正常。

六、可玩扩展(留给读者)

  1. 多路摄像头:
    QCameraInfo::availableCameras() 遍历,TabWidget 每页一个 ViewFinder
  2. 运动检测:
    CvRecorder::write 里加 cv::absdiff(last, cur, mask),检测轮廓面积 > 阈值再写文件,可省 80% 硬盘
  3. RTSP 推流:
    二期用 FFmpeg 的 avformat_write_header 替换 cv::VideoWriter,地址 rtsp://localhost:8554/live
  4. Web 后台:
    QtWebApp 轻量级 HTTP 服务,暴露 /api/start /api/stop,前端 Vue+VideoJS 回放
  5. 国产化编译:
    银河麒麟 + 兆芯,Qt 6.5 静态编 + 国产显卡 OpenCL,帧率几乎无损耗

七、常见坑清单

现象原因解决
MinGW 版 OpenCV 打不开摄像头未开 WITH_DSHOW用预编译 opencv_world480.dll 或自己 CMake 开 -DWITH_DSHOW=ON
Qt6 找不到 QCameraViewfinderQt6 改成 QVideoSinkQMediaCaptureSession + QVideoSink 接管帧
录制文件 0 字节fourcc 不支持'X','V','I','D''M','J','P','G'
画面颠倒OpenCV 默认 BGRcv::cvtColor(mat, mat, cv::COLOR_RGB2BGR)

八、回顾:你学到了什么

  1. QtMultimedia 摄像头三步曲:枚举→QCamera→ViewFinder
  2. QVideoFrame 与 cv::Mat 零拷贝转换思路(map + clone)
  3. OpenCV VideoWriter 四行代码就能出 MP4,别被 FFmpeg 吓跑
  4. 信号槽跨线程传 Mat 必须深拷贝,否则帧数据被 Qt 回收
  5. 用 CMake 同时管理 Qt + OpenCV,跨平台编译脚本一次写完