Qt 入门实战:用 200 行代码写个“能看又能录”的轻量级监控客户端
适合:刚学完 C++/Qt Widgets、想第一次调用摄像头、再顺手把画面推流/存盘的同学
源码:文末 gitee 链接,MIT 协议可商用
环境:Win10 + Qt 5.15.2(MinGW)/ Ubuntu 20.04 + Qt 6.5,OpenCV 4.8
一、需求拆解:最小可玩 MVP
- 实时预览:1 路 USB 摄像头,1080p@30fps,延迟 < 200 ms
- 一键录制:点“开始”→生成带时间戳的 MP4,码率 2 Mbps
- 最小依赖:Qt 自带多媒体模块 + OpenCV(仅编码用,UI 层不耦合)
- 可扩展:后续加 RTSP 推流、AI 检测、Web 后台,只需换 DLL/插件
二、技术选型对比(为什么这样搭)
| 方案 | 优点 | 缺点 | 本例取舍 |
|---|---|---|---|
| QtMultimedia | Qt 官方、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);
五、编译 & 运行
-
Win:
- 安装 Qt 5.15.2 + CMake 3.24
- 下载 OpenCV-MinGW 版,把
bin/*.dll拷到build/Debug cmake -B build -G "MinGW Makefiles" && cmake --build build
-
Ubuntu:
sudo apt install qtbase5-dev qtmultimedia5-dev libopencv-dev
cmake -B build && cmake --build build
运行效果:
- 左侧 1080p 实时画面,右侧“开始/停止”两按钮;
- 录制结束在同目录生成
20250930_143022.mp4,VLC 播放正常。
六、可玩扩展(留给读者)
- 多路摄像头:
QCameraInfo::availableCameras()遍历,TabWidget 每页一个 ViewFinder - 运动检测:
在CvRecorder::write里加cv::absdiff(last, cur, mask),检测轮廓面积 > 阈值再写文件,可省 80% 硬盘 - RTSP 推流:
二期用 FFmpeg 的avformat_write_header替换cv::VideoWriter,地址rtsp://localhost:8554/live - Web 后台:
QtWebApp 轻量级 HTTP 服务,暴露/api/start/api/stop,前端 Vue+VideoJS 回放 - 国产化编译:
银河麒麟 + 兆芯,Qt 6.5 静态编 + 国产显卡 OpenCL,帧率几乎无损耗
七、常见坑清单
| 现象 | 原因 | 解决 |
|---|---|---|
| MinGW 版 OpenCV 打不开摄像头 | 未开 WITH_DSHOW | 用预编译 opencv_world480.dll 或自己 CMake 开 -DWITH_DSHOW=ON |
| Qt6 找不到 QCameraViewfinder | Qt6 改成 QVideoSink | 用 QMediaCaptureSession + QVideoSink 接管帧 |
| 录制文件 0 字节 | fourcc 不支持 | 换 'X','V','I','D' 或 'M','J','P','G' |
| 画面颠倒 | OpenCV 默认 BGR | cv::cvtColor(mat, mat, cv::COLOR_RGB2BGR) |
八、回顾:你学到了什么
- QtMultimedia 摄像头三步曲:枚举→QCamera→ViewFinder
- QVideoFrame 与 cv::Mat 零拷贝转换思路(map + clone)
- OpenCV VideoWriter 四行代码就能出 MP4,别被 FFmpeg 吓跑
- 信号槽跨线程传 Mat 必须深拷贝,否则帧数据被 Qt 回收
- 用 CMake 同时管理 Qt + OpenCV,跨平台编译脚本一次写完