Qt+FFmpeg 实现摄像头采集并录制 YUV 格式视频
一、前言本文主要记录通过 Qt 结合 FFmpeg 实现从摄像头采集视频数据并将采集到的数据保存为 YUV 格式文件的过程。涉及 FFmpeg 设备操作、Qt 多线程编程等核心知识点适合有一定 Qt 和 FFmpeg 基础的开发者学习参考。二、环境准备开发框架Qt 5/6本文以 Qt 为例核心逻辑不依赖具体版本FFmpeg 库需包含libavdevice、libavformat、libavutil、libavcodec等核心库系统环境本文以 Windows 系统为例使用 dshowDirectShow作为设备输入格式三、核心代码解析3.1 线程类头文件audiothread.h由于视频采集是耗时操作需放在子线程中执行避免阻塞主线程UI 线程。#ifndef AUDIOTHREAD_H #define AUDIOTHREAD_H #include QThread class audioThread : public QThread { Q_OBJECT private: void run(); // 线程执行函数重写QThread的run方法 public: explicit audioThread(QObject *parent nullptr); ~audioThread(); }; #endif // AUDIOTHREAD_H3.2 线程实现文件audiothread.cpp核心逻辑集中在该文件包含 FFmpeg 设备打开、数据采集、文件写入、资源释放等步骤。3.2.1 头文件与宏定义#include audiothread.h #include qdebug.h #include qfile.h // 引入FFmpeg的C语言接口 extern C{ #include libavdevice/avdevice.h #include libavformat/avformat.h #include libavutil/avutil.h #include libavutil/imgutils.h #include libavcodec/avcodec.h } // Windows系统下的设备配置 #ifdef Q_OS_WIN #define FMT_NAME dshow // 设备输入格式dshow #define DEVICE_NAME videoIntegrated Webcam // 摄像头设备名称 #define FILENAME D:/out.yuv // YUV文件保存路径 #endif // 错误信息格式化宏 #define ERROR_BUF(ret) \ char errbuf[1024]; \ av_strerror(ret,errbuf,sizeof(errbuf));3.2.2 构造与析构函数audioThread::audioThread(QObject *parent):QThread{parent} { // 线程结束时自动回收内存 connect(this,audioThread::finished, this,audioThread::deleteLater); } audioThread::~audioThread() { disconnect(); // 断开所有信号槽连接 requestInterruption(); // 请求线程中断 quit(); // 退出事件循环 wait(); // 等待线程结束 qDebug() this 析构内存被回收; }3.2.3 核心采集逻辑run 方法void audioThread::run() { qDebug() this 开始执行------; // 1. 获取输入格式对象dshow AVInputFormat *fmt av_find_input_format(FMT_NAME); if(!fmt){ qDebug() av_find_input_format error FMT_NAME; return; } // 2. 初始化格式上下文操作设备的核心上下文 AVFormatContext *ctx nullptr; AVDictionary *options nullptr; // 设置采集参数分辨率、像素格式、帧率 av_dict_set(options,video_size,1280x720,0); av_dict_set(options,pixel_format,yuyv422,0); av_dict_set(options,framerate,10,0); // 3. 打开摄像头设备 int ret avformat_open_input(ctx,DEVICE_NAME,fmt,options); if(ret 0){ ERROR_BUF(ret); qDebug() avformat_open_input error errbuf; return; } // 4. 打开YUV文件用于写入 QFile file(FILENAME); if(!file.open(QFile::WriteOnly)){ qDebug() file open error FILENAME; avformat_close_input(ctx); // 失败时关闭设备 return; } // 5. 计算一帧视频数据的大小 AVCodecParameters *params ctx-streams[0]-codecpar; AVPixelFormat pixFmt (AVPixelFormat)params-format; int imageSize av_image_get_buffer_size( pixFmt, params-width, params-height, 1); // 6. 循环采集视频数据 AVPacket *pkt av_packet_alloc(); // 分配数据包 while(!isInterruptionRequested()){ // 检查是否需要中断 ret av_read_frame(ctx,pkt); // 读取一帧数据 if(ret 0){ // 读取成功 // 将数据写入YUV文件 file.write((const char*)pkt-data,imageSize); av_packet_unref(pkt); // 释放数据包引用 }else if(ret AVERROR(EAGAIN)){ // 资源临时不可用继续循环 continue; }else{ // 其他错误退出循环 ERROR_BUF(ret); qDebug() av_read_frame error errbuf ret; break; } } // 7. 释放资源 av_packet_free(pkt); // 释放数据包 file.close(); // 关闭文件 avformat_close_input(ctx); // 关闭设备 qDebug() this 正常结束------; }3.3 主窗口逻辑mainwindow 相关3.3.1 头文件mainwindow.h#ifndef MAINWINDOW_H #define MAINWINDOW_H #include QMainWindow #include audiothread.h QT_BEGIN_NAMESPACE namespace Ui { class MainWindow; } QT_END_NAMESPACE class MainWindow : public QMainWindow { Q_OBJECT public: MainWindow(QWidget *parent nullptr); ~MainWindow(); private slots: void on_playBtn_clicked(); // 按钮点击槽函数 private: Ui::MainWindow *ui; audioThread *_audioThread nullptr; // 采集线程指针 }; #endif // MAINWINDOW_H3.3.2 实现文件mainwindow.cpp#include mainwindow.h #include ui_mainwindow.h MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) , ui(new Ui::MainWindow) { ui-setupUi(this); } MainWindow::~MainWindow() { delete ui; } void MainWindow::on_playBtn_clicked() { if(!_audioThread){ // 开始采集 _audioThread new audioThread(this); _audioThread-start(); // 启动线程 // 线程结束后重置指针并恢复按钮文字 connect(_audioThread,audioThread::finished, [this](){ _audioThread nullptr; ui-playBtn-setText(开始录视频); }); ui-playBtn-setText(结束录视频); }else{ // 停止采集 _audioThread-requestInterruption(); // 请求线程中断 _audioThread nullptr; ui-playBtn-setText(开始录视频); } }3.4 程序入口main.cpp#include mainwindow.h #include QApplication extern C{ #include libavdevice/avdevice.h } int main(int argc, char *argv[]) { avdevice_register_all(); // 注册FFmpeg所有设备 QApplication a(argc, argv); MainWindow w; w.show(); return a.exec(); }四、关键知识点总结4.1 FFmpeg 核心操作流程avdevice_register_all()注册所有 FFmpeg 设备必须在操作设备前调用av_find_input_format()根据格式名称如 dshow获取输入格式对象avformat_open_input()打开设备 / 文件初始化格式上下文av_read_frame()从设备读取一帧视频数据封装在 AVPacket 中avformat_close_input()关闭设备释放格式上下文资源av_image_get_buffer_size()计算指定分辨率、像素格式的单帧视频数据大小。4.2 Qt 多线程注意事项耗时操作如视频采集必须放在QThread::run()方法中避免阻塞 UI线程中断通过requestInterruption()isInterruptionRequested()配合实现优雅停止线程线程结束后通过finished信号关联deleteLater()自动回收线程内存避免内存泄漏主线程与子线程的交互尽量通过信号槽避免直接操作子线程对象。4.3 YUV 文件验证采集完成后可通过 FFmpeg 的 ffplay 工具验证 YUV 文件是否有效ffplay -f rawvideo -pixel_format yuyv422 -video_size 1280x720 -framerate 10 D:/out.yuv五、常见问题与解决avformat_open_input 失败检查设备名称是否正确Windows 下可通过ffmpeg -list_devices true -f dshow -i dummy查看摄像头名称确认 FFmpeg 编译时包含了 dshow 模块检查权限摄像头是否被其他程序占用。采集数据写入文件后无法播放确认像素格式、分辨率、帧率与 ffplay 播放参数一致检查单帧数据大小计算是否正确避免写入数据长度错误。线程退出时崩溃确保退出时释放所有 FFmpeg 资源AVPacket、AVFormatContext 等析构函数中先请求中断再调用 quit () 和 wait ()等待线程完全结束后再释放资源。六、总结本文通过 QtFFmpeg 实现了摄像头视频采集并保存为 YUV 格式文件核心是掌握 FFmpeg 设备操作流程和 Qt 多线程编程规范。YUV 作为原始视频格式是音视频开发的基础理解其采集过程有助于后续学习编码如 H.264/H.265、封装如 MP4等进阶知识点。

相关新闻

从0到1:Android组件化架构搭建秘籍

从0到1:Android组件化架构搭建秘籍

从0到1:Android组件化架构搭建秘籍 组件化架构:为何如此重要? 在 Android 开发的漫漫征途中,我们常常会面临项目复杂度如滚雪球般增长的困境。当项目还处于 “幼年时期”,功能简单,代码量也相对较少&#x…

2026/7/3 8:01:18 阅读更多 →
Git merge 策略

Git merge 策略

Git Merge 策略:深入理解代码合并的艺术 引言 在团队协作开发中,Git 作为分布式版本控制系统扮演着至关重要的角色。而合并(Merge)操作作为 Git 中的核心功能,直接影响着团队的开发效率和代码质量。本文将深入探讨 Git…

2026/5/17 9:16:16 阅读更多 →
OpenClaw一键部署教程,接入qq、微信、飞书、钉钉

OpenClaw一键部署教程,接入qq、微信、飞书、钉钉

我是新购的云服务器,openclaw操作权限比较高,容易把本地电脑搞崩。 新购或者更换系统的时候直接选择openclaw, 链接直达 https://curl.qcloud.com/2vYV7GII可以接到飞书和qq里 详细教程直达 https://curl.qcloud.com/M4mqVieW 配置微信钉钉q…

2026/5/17 9:16:16 阅读更多 →

最新新闻

OSXPhotos:macOS 照片库的命令行管理工具

OSXPhotos:macOS 照片库的命令行管理工具

文章目录OSXPhotos:macOS 照片库的命令行管理工具能做什么模板系统是亮点安装和使用适合谁用OSXPhotos:macOS 照片库的命令行管理工具 用 Mac 的人,照片库里多少都攒了几千上万张照片。时间一长,想找某张特定的照片,或…

2026/7/3 8:00:12 阅读更多 →
成年人必看!治愈一生的经典名著《小王子》

成年人必看!治愈一生的经典名著《小王子》

成年人必读的治愈经典,《小王子》从来不止是儿童童话,更是成年人的人生教科书。长大后才读懂,这本经典治愈书籍藏着我们所有的迷茫、遗憾与成长,也是当之无愧的人生必读名著。很多人年少读《小王子》,只记住了温柔的童…

2026/7/3 7:58:12 阅读更多 →
国际期货日内交易最佳交易时段

国际期货日内交易最佳交易时段

国际期货全天近 24 小时连续交易,不同时段资金量、波动幅度、流动性差异巨大,日内短线想要降低滑点、把握有效行情,优先选择欧美重叠盘,分三档时段区分优劣。最差时段为亚盘,北京时间 7:00 至 15:00,仅有亚…

2026/7/3 7:52:11 阅读更多 →
第19章:Celery 分布式任务队列深度解析

第19章:Celery 分布式任务队列深度解析

1. 项目背景 "我上传了一份 200 页的 PDF 到知识库,点击’保存并处理’后页面显示索引进度 0%。等了 30 分钟终于跳到 100%,中间我刷新了 5 次页面,每次都以为卡死了。"这是新手使用 Dify 知识库最常见的困惑。30 分钟里,Dify 的后台 Celery Worker 一直在拼命工…

2026/7/3 7:52:11 阅读更多 →
如何快速提升你的英雄联盟游戏体验:League Akari的完整指南

如何快速提升你的英雄联盟游戏体验:League Akari的完整指南

如何快速提升你的英雄联盟游戏体验:League Akari的完整指南 【免费下载链接】League-Toolkit An all-in-one toolkit for LeagueClient. Gathering power 🚀. 项目地址: https://gitcode.com/gh_mirrors/le/League-Toolkit 你是否曾在英雄选择阶段…

2026/7/3 7:52:11 阅读更多 →
解决Linux下802.11ac无线网卡驱动兼容性难题:rtl8812AU_8821AU内核模块深度解析

解决Linux下802.11ac无线网卡驱动兼容性难题:rtl8812AU_8821AU内核模块深度解析

解决Linux下802.11ac无线网卡驱动兼容性难题:rtl8812AU_8821AU内核模块深度解析 【免费下载链接】rtl8812AU_8821AU_linux rtl8812AU_8821AU linux kernel driver for AC1200 (801.11ac) Wireless Dual-Band USB Adapter 项目地址: https://gitcode.com/gh_mirror…

2026/7/3 7:50:10 阅读更多 →

日新闻

Nginx防御TLS重协商攻击实战:从原理到配置与监控

Nginx防御TLS重协商攻击实战:从原理到配置与监控

1. 项目概述:为什么TLS重协商攻击至今仍需警惕十多年前的CVE-2011-1473,一个关于TLS/SSL协议重协商机制的漏洞,现在提起来还有必要吗?很多运维和开发朋友可能会觉得,这都老掉牙了,现代服务器和客户端不都默…

2026/7/3 0:03:59 阅读更多 →
华为防火墙双通道远程管理实战:Web与SSH配置详解

华为防火墙双通道远程管理实战:Web与SSH配置详解

1. 项目概述:为什么需要双通道远程管理防火墙?在任何一个稍具规模的企业网络里,防火墙都是那个默默守护在边界的关键角色。作为网络工程师,我们不可能每次都跑到机房,插上console线去配置它。远程管理能力,…

2026/7/3 0:03:59 阅读更多 →
AD74413R与PIC18F65K40的高精度工业数据采集方案

AD74413R与PIC18F65K40的高精度工业数据采集方案

1. 项目概述:AD74413R与PIC18F65K40的协同工作在工业自动化和精密测量领域,同时实现高精度模数转换(ADC)和数模转换(DAC)功能是许多复杂系统的核心需求。AD74413R作为一款四通道可配置模拟输入/输出器件,与PIC18F65K40微控制器的组合&#xf…

2026/7/3 0:05:59 阅读更多 →

周新闻

月新闻