Qt开发实战:屏幕录制项目中学习到的知识与遇到的难题
一、音频采集:RtAudio库
1.1 RtAudio简介
RtAudio是一个强大的C++类库,旨在为实时音频输入/输出提供统一的API。无论你是在Linux、Macintosh OS X还是Windows平台上开发,RtAudio都能帮助你轻松地与计算机音频硬件进行交互。
通过支持多种音频API(如ALSA、JACK、PulseAudio、OSS、CoreAudio、DirectSound、ASIO和WASAPI),RtAudio确保了在不同操作系统上的无缝集成和高效性能。
项目地址:https://github.com/thestk/rtaudio
1.2 在Qt项目中的应用
在本次项目中,使用RtAudio来采集扬声器的声音。由于是在Windows环境下开发,需要在RtAudio.h中定义宏来启用Windows音频API:
cpp
#define __WINDOWS_WASAPI__
WASAPI(Windows Audio Session API)是Windows Vista及以上版本提供的音频接口,具有低延迟、高性能的特点,非常适合实时音频采集场景。
二、安装程序制作
2.1 NSIS + Qt方案
NSIS(Nullsoft Scriptable Install System)是一个专业的开源安装程序制作工具,结合Qt可以实现美观的安装界面。
参考资源:
- NSIS + Qt教程:https://blog.csdn.net/qq21497936/article/details/116445811
- 高仿安装程序模板:https://github.com/ms895/installer/
2.2 卸载脚本关键分析
卸载部分的NSIS脚本需要处理以下几个关键点:

核心代码解析:
nsis
# 定义卸载程序的用户界面页面顺序
UninstPage custom un.QtUninstConfirmPage
UninstPage instfiles
# 准备卸载程序的运行环境
Function un.onInit
InitPluginsDir
# 加载Qt相关DLL
File /oname=$PLUGINSDIR\Qt5Core${QT_DLL_SUFFIX}.dll "$%QTDIR%\bin\Qt5Core${QT_DLL_SUFFIX}.dll"
File /oname=$PLUGINSDIR\Qt5Gui${QT_DLL_SUFFIX}.dll "$%QTDIR%\bin\Qt5Gui${QT_DLL_SUFFIX}.dll"
# ... 更多DLL加载
FunctionEnd
# 调用Qt界面进行卸载确认
Function un.QtUninstConfirmPage
GetFunctionAddress $0 un.OnUIPrepared
${UI_PLUGIN_NAME}::BindUninstallEventToNsisFunc "UNINST_UI_PREPARED" $0
GetFunctionAddress $0 un.OnUserConfirmUninstall
${UI_PLUGIN_NAME}::BindUninstallEventToNsisFunc "UNINST_USER_CONFIRM" $0
FunctionEnd
2.3 Inno Setup方案
Inno Setup是另一个流行的安装程序制作工具,但相对NSIS而言,自定义界面的灵活性稍逊一筹。
教程资源:https://www.bilibili.com/video/BV15k4y1R7cL
三、程序更新机制
3.1 实现思路
程序更新采用独立的更新程序方案:
- 客户端检测到新版本,启动更新程序
- 客户端自行关闭
- 更新程序从服务端下载更新包
- 更新程序覆盖所有dll和exe文件
3.2 关键设计
后门机制 :当getVersion接口返回is_enforce为99时,不能通过程序自身更新,需要用户手动卸载、下载安装包、重新安装。
包格式规范:
- 安装包:
ZRecorder-setup-x.x.x.exe - 更新包:
ZRecorder-update-x.x.x.zip
3.3 注意事项
卸载程序删除的是安装目录下的所有文件,因此更新包覆盖不会影响卸载流程。
四、高DPI适配问题
4.1 问题现象
在设置了系统缩放比的电脑上,程序界面显示异常:

4.2 解决方案
方案一:禁用缩放
完全禁用程序的缩放,使程序不随系统进行缩放:
cpp
#include <QApplication>
int main(int argc, char *argv[]) {
// 禁用Qt的高DPI自动缩放
QApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
QApplication app(argc, argv);
return app.exec();
}
方案二:启用高DPI支持(推荐)
cpp
#include <QApplication>
int main(int argc, char *argv[]) {
// 必须在创建QApplication前调用
QApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
// 设置舍入策略为直接使用原始缩放值
QApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
QApplication app(argc, argv);
return app.exec();
}
Qt::HighDpiScaleFactorRoundingPolicy枚举值说明:
| 值 | 说明 |
|---|---|
| Round | 四舍五入到整数(默认) |
| Ceil | 向上取整 |
| Floor | 向下取整 |
| PassThrough | 直接使用原始缩放值(推荐) |
五、图标模糊问题
5.1 使用SVG格式
推荐使用SVG格式图片,可以在任意缩放比例下保持清晰。
注意事项:
- 如果在label上SVG显示不出来,可以在程序中使用一个
QSvgWidget - 将QWidget提升为QSvgWidget
- QLabel无法提升为QSvgWidget
5.2 获取屏幕缩放信息
cpp
// 获取所有屏幕的缩放比
QList<QScreen*> screens = QApplication::screens();
for (QScreen* screen : screens) {
qreal dpi = screen->logicalDotsPerInch() / 96.0;
qreal ratio = screen->devicePixelRatio();
qDebug() << "Screen:" << screen->name();
qDebug() << "DPI scaling:" << dpi;
qDebug() << "Pixel ratio:" << ratio;
}
// 获取当前屏幕的缩放比
QScreen* currentScreen = this->window()->screen();
if (currentScreen) {
qreal ratio = currentScreen->devicePixelRatio();
}
5.3 解决图片模糊的方法



六、ICO图标格式
6.1 格式特点
ICO格式是Windows操作系统专用的图标文件格式,一个ICO文件可以包含多个不同尺寸的图片(如16×16、32×32、48×48、256×256等),系统会根据需要自动选择合适的尺寸显示。
6.2 最佳实践
- 标准ICO最大尺寸为256×256
- 为了在不同系统上有良好显示效果,需要在ICO中存放多种尺寸
- 推荐使用在线转换工具:https://www.aconvert.com/cn/icon/png-to-ico/
七、全局快捷键
7.1 使用QHotkey库
即使在最小化、焦点不在程序上,快捷键也能生效。推荐使用第三方库QHotkey实现。
编译步骤:
bash
git clone https://gitee.com/bihj9919/QHotkey.git
# 配置CMake(32位示例)
cmake -B build -S . -A Win32 -DQT_DEFAULT_MAJOR_VERSION=5 \
-DCMAKE_PREFIX_PATH="G:\Qt\5.15.2\msvc2019"
# 编译Release版本
cmake --build build --config Release
# 安装
cmake --install build --prefix build
八、系统托盘菜单圆角
实现圆角托盘菜单需要自定义样式,参考资源:
- https://blog.csdn.net/weixin_39868379/article/details/123949195
- https://stackoverflow.com/questions/32491282/qframe-round-border-transparent-background
九、提高录制帧率
9.1 当前方案分析
BitBlt + FFmpeg软件编码:
- BitBlt截取1080p屏幕约30ms
- 软件编码约12ms
- 性能瓶颈在屏幕截取
- 多线程优化后最多实现30fps
9.2 优化方案
使用DirectX进行屏幕截取 + libyuv裁剪和颜色空间转换:
DirectX是微软开发的高性能多媒体接口,性能优于BitBlt。
9.3 帧率控制机制
双线程架构:
- 采集线程:屏幕采集、裁剪、颜色空间转换,按设置帧率固定间隔抓取
- 编码线程:编码、写入MP4文件,根据PTS值从队列取数据
PTS处理逻辑:
- 期望PTS > 队首PTS:丢弃旧帧,直到队首PTS >= 期望PTS
- 期望PTS < 队首PTS:直接使用队首帧
- 队列为空:重复上一帧

9.4 注意事项
使用优化方案可以达到60fps,但需要注意音视频同步问题。
十、窗口边框阴影
10.1 问题场景
当程序主体色为白色,移动到白色背景上时,程序边框不可见。
10.2 解决方案
cpp
setContentsMargins(8, 8, 8, 8); // 为阴影留出空间
QGraphicsDropShadowEffect *shadow = new QGraphicsDropShadowEffect(this);
shadow->setBlurRadius(8); // 阴影半径
shadow->setOffset(0, 0); // 阴影偏移
shadow->setColor(QColor(0, 0, 0, 60)); // 阴影颜色
shadow->setEnabled(true);
ui->centralwidget->setGraphicsEffect(shadow);
10.3 边距设置说明
setContentsMargins():设置窗口内所有内容距离窗口边框的距离- UI文件中的
layoutMargin:控件距离布局容器的距离 - 两者叠加:实际控件距离窗口边框 = 两者之和
十一、安装程序轮播图闪动问题
11.1 问题原因
为程序添加阴影后,设置显示图片的透明度失效,导致出现空白或闪动。
11.2 解决思路
需要调整阴影效果与透明度动画的配合方式,确保两者不冲突。