Qt开发实战:屏幕录制项目中学习到的知识与遇到的难题

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可以实现美观的安装界面。

参考资源

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 实现思路

程序更新采用独立的更新程序方案:

  1. 客户端检测到新版本,启动更新程序
  2. 客户端自行关闭
  3. 更新程序从服务端下载更新包
  4. 更新程序覆盖所有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 最佳实践


七、全局快捷键

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

八、系统托盘菜单圆角

实现圆角托盘菜单需要自定义样式,参考资源:


九、提高录制帧率

9.1 当前方案分析

BitBlt + FFmpeg软件编码

  • BitBlt截取1080p屏幕约30ms
  • 软件编码约12ms
  • 性能瓶颈在屏幕截取
  • 多线程优化后最多实现30fps

9.2 优化方案

使用DirectX进行屏幕截取 + libyuv裁剪和颜色空间转换

DirectX是微软开发的高性能多媒体接口,性能优于BitBlt。

9.3 帧率控制机制

双线程架构

  • 采集线程:屏幕采集、裁剪、颜色空间转换,按设置帧率固定间隔抓取
  • 编码线程:编码、写入MP4文件,根据PTS值从队列取数据

PTS处理逻辑

  1. 期望PTS > 队首PTS:丢弃旧帧,直到队首PTS >= 期望PTS
  2. 期望PTS < 队首PTS:直接使用队首帧
  3. 队列为空:重复上一帧

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 解决思路

需要调整阴影效果与透明度动画的配合方式,确保两者不冲突。

相关推荐
热爱专研AI的学妹1 小时前
Seedance 2.0(即梦 2.0)深度解析:AI 视频正式迈入导演级精准可控时代
大数据·人工智能·阿里云·音视频
雾岛听蓝4 小时前
Qt操作指南:窗口组成与菜单栏
开发语言·经验分享·笔记·qt
(Charon)5 小时前
【C++/Qt】C++/Qt 实现 TCP Server:支持启动监听、消息收发、日志保存
c++·qt·tcp/ip
byte轻骑兵5 小时前
从收音机到蓝牙:LE Audio核心BASS服务解析与实战
人工智能·音视频·语音识别·le audio·低功耗音频
大猫会长7 小时前
AudioContext给音频提高音量
前端·javascript·音视频
开开心心就好7 小时前
无需安装的单机塔防游戏轻松畅玩
人工智能·游戏·pdf·音视频·智能家居·语音识别·媒体
开开心心就好8 小时前
这款工具批量卸载软件并清理残留文件
人工智能·游戏·音视频·语音识别·媒体·程序员创富·高考
半条-咸鱼8 小时前
基于安卓的 WAV 音频采集方案_含工具
android·音视频
while(1){yan}10 小时前
音视频流协议
音视频