23. QT多媒体:QMediaPlayer、QMediaPlaylist、QVideoWidget、QAudioOutput、摄像头操作
多媒体开发,说白了就是让程序能「看」能「听」。我记得刚入行那会儿,Qt 的多媒体模块还叫 Phonon,API 设计得挺别扭。后来 Qt 5 时代全面重构成了 QMediaPlayer 这套体系,用起来顺手多了。今天我们就来聊聊这套东西怎么用,以及我在实际项目中踩过的那些坑。
核心知识点一览:
- QMediaPlayer —— 播放器的核心引擎
- QMediaPlaylist —— 管理播放列表
- QVideoWidget —— 视频渲染窗口
- QAudioOutput —— 音频输出控制
- QCamera —— 摄像头采集与预览
23.1 QMediaPlayer —— 播放器的灵魂
QMediaPlayer 是整个多媒体播放的「大脑」。它不负责界面渲染,只负责状态管理、媒体加载、播放控制。我个人习惯把它比作发动机——你得给它配上轮子和方向盘才能跑起来。
先看一个最基础的用法:
#include <QMediaPlayer>
#include <QVideoWidget>
#include <QAudioOutput>
// 创建播放器
QMediaPlayer *player = new QMediaPlayer;
// 创建视频输出窗口
QVideoWidget *videoWidget = new QVideoWidget;
// 创建音频输出
QAudioOutput *audioOutput = new QAudioOutput;
// 组装
player->setVideoOutput(videoWidget);
player->setAudioOutput(audioOutput);
// 加载并播放
player->setSource(QUrl::fromLocalFile("/path/to/video.mp4"));
player->play();
嗯,这里要注意:QMediaPlayer 本身不直接输出音频。Qt 6 之后把音频输出独立成了 QAudioOutput,你得显式地 setAudioOutput 才能听到声音。我在项目中遇到过有人折腾半天没声音,结果就是忘了这一步。
小技巧: 播放网络流媒体时,用 QNetworkRequest 包装 URL,可以设置超时和缓存策略:
QNetworkRequest request(QUrl("https://example.com/stream.mp4"));
request.setTransferTimeout(10000); // 10秒超时
player->setSource(request);
23.2 QMediaPlaylist —— 让播放列表转起来
单曲循环太单调了。QMediaPlaylist 就是用来管理多媒体的「歌单」。它支持顺序播放、随机播放、循环播放等模式。
QMediaPlaylist *playlist = new QMediaPlaylist;
playlist->addMedia(QUrl::fromLocalFile("song1.mp3"));
playlist->addMedia(QUrl::fromLocalFile("song2.mp3"));
playlist->addMedia(QUrl::fromLocalFile("song3.mp3"));
// 设置播放模式
playlist->setPlaybackMode(QMediaPlaylist::Loop); // 列表循环
// 关联到播放器
player->setPlaylist(playlist);
player->play();
播放模式有四种,我整理了一张表:
| 枚举值 | 行为 | 适用场景 |
|---|---|---|
| CurrentItemOnce | 当前项播放一次即停止 | 预览单个文件 |
| CurrentItemInLoop | 当前项无限循环 | 单曲循环 |
| Sequential | 顺序播放,播完停止 | 播放列表一次 |
| Loop | 列表循环 | 背景音乐、视频轮播 |
| Random | 随机播放 | 音乐播放器「随机模式」 |
曾经踩过的坑: 我在做一个广告机项目时,用了 Sequential 模式,结果播完最后一个视频就停了,客户投诉说屏幕黑了。后来改成 Loop 模式,并在播放列表末尾添加一个「空闲画面」视频,完美解决。
23.3 QVideoWidget —— 视频渲染的「画布」
QVideoWidget 是专门用来显示视频画面的控件。它继承自 QWidget,所以你可以像操作普通控件一样调整大小、嵌入布局、设置样式表。
但有个关键点:QVideoWidget 需要硬件加速支持。如果你在虚拟机里跑,或者显卡驱动没装好,很可能看到黑屏。我建议在 main.cpp 里加上这句:
QApplication::setAttribute(Qt::AA_UseOpenGLES); // 强制使用 OpenGL ES
另外,QVideoWidget 支持全屏显示:
videoWidget->setFullScreen(true); // 一键全屏
// 退出全屏用 Esc 键,或者调用
videoWidget->setFullScreen(false);
你想想看,如果要做个监控软件,全屏显示摄像头画面是不是很实用?
23.4 QAudioOutput —— 音量与声道控制
QAudioOutput 是 Qt 6 新增的类,专门负责音频输出。它把音量控制和声道选择从 QMediaPlayer 里剥离了出来,设计上更清晰。
QAudioOutput *audio = new QAudioOutput;
audio->setVolume(0.8); // 音量 0.0 ~ 1.0
player->setAudioOutput(audio);
// 静音切换
audio->setMuted(!audio->isMuted());
说实话,这个设计我挺喜欢的。以前在 Qt 5 里,音量控制混在 QMediaPlayer 里,想单独控制左右声道还得自己算。现在直接操作 QAudioOutput 就行了。
实用技巧: 如果你需要做音量渐变效果(比如淡入淡出),可以用 QPropertyAnimation 对 volume 属性做动画:
QPropertyAnimation *anim = new QPropertyAnimation(audio, "volume");
anim->setDuration(2000);
anim->setStartValue(0.0);
anim->setEndValue(1.0);
anim->start();
23.5 摄像头操作 —— 让程序「看见」世界
QCamera 是 Qt 多媒体模块里操作摄像头的核心类。它和 QMediaPlayer 配合,可以做到「摄像头预览」和「拍照录像」。
先看一个最简单的摄像头预览:
// 获取可用摄像头列表
const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
if (cameras.isEmpty()) {
qWarning() << "没有检测到摄像头";
return;
}
// 创建摄像头对象
QCamera *camera = new QCamera(cameras.first());
// 创建预览窗口
QVideoWidget *viewfinder = new QVideoWidget;
camera->setVideoOutput(viewfinder);
// 启动预览
camera->start();
拍照功能需要用到 QCameraImageCapture:
QCameraImageCapture *imageCapture = new QCameraImageCapture(camera);
camera->setCameraFormat(QCameraFormat::fromCamera(camera, QVideoFrameFormat::Format_Jpeg));
// 拍照并保存
imageCapture->captureToFile("/path/to/photo.jpg");
// 拍照完成信号
connect(imageCapture, &QCameraImageCapture::imageSaved, [](int id, const QString &fileName) {
qDebug() << "照片已保存:" << fileName;
});
曾经踩过的坑: 我在做扫码枪项目时,发现某些 USB 摄像头在 Qt 里无法正常启动。排查了半天,发现是摄像头格式不兼容。QCamera 默认使用最高分辨率,但有些老摄像头只支持特定格式。解决办法是手动设置 QCameraFormat:
QCameraFormat format = camera->cameraDevice().photoFormats().first();
camera->setCameraFormat(format);
说白了,就是别让 Qt 自动选格式,自己指定一个兼容的。
23.6 综合示例:简易媒体播放器
把上面这些知识点串起来,就是一个简易的媒体播放器。我贴一个核心代码片段:
class MediaPlayer : public QMainWindow {
Q_OBJECT
public:
MediaPlayer() {
// 初始化组件
player = new QMediaPlayer(this);
playlist = new QMediaPlaylist(this);
videoWidget = new QVideoWidget(this);
audioOutput = new QAudioOutput(this);
// 组装
player->setPlaylist(playlist);
player->setVideoOutput(videoWidget);
player->setAudioOutput(audioOutput);
// 设置中心窗口
setCentralWidget(videoWidget);
// 控制按钮
QPushButton *playBtn = new QPushButton("播放/暂停");
connect(playBtn, &QPushButton::clicked, this, [this]() {
if (player->playbackState() == QMediaPlayer::PlayingState)
player->pause();
else
player->play();
});
// 音量滑块
QSlider *volumeSlider = new QSlider(Qt::Horizontal);
volumeSlider->setRange(0, 100);
volumeSlider->setValue(80);
connect(volumeSlider, &QSlider::valueChanged, this, [this](int value) {
audioOutput->setVolume(value / 100.0);
});
// 布局
QToolBar *toolbar = addToolBar("控制");
toolbar->addWidget(playBtn);
toolbar->addWidget(volumeSlider);
}
private:
QMediaPlayer *player;
QMediaPlaylist *playlist;
QVideoWidget *videoWidget;
QAudioOutput *audioOutput;
};
这个例子虽然简单,但麻雀虽小五脏俱全。你想想看,加上文件选择对话框、进度条、播放模式切换,就是一个完整的播放器了。
23.7 避坑指南与最佳实践
- 音频输出必须显式设置 —— Qt 6 不再默认输出音频,忘了 setAudioOutput 就是静音。
- 视频黑屏先检查硬件加速 —— 虚拟机、远程桌面下经常黑屏,试试 AA_UseOpenGLES。
- 摄像头启动失败检查权限 —— 在 macOS 和 iOS 上需要 Info.plist 里声明摄像头权限,Android 需要运行时权限申请。
- 播放列表切换时重置状态 —— 切换媒体时,建议先 stop() 再 setSource(),避免状态混乱。
- 释放资源要彻底 —— 停止播放后调用 player->stop(),再释放 QMediaPlayer 对象。
我曾经在一个嵌入式项目里,因为没及时释放摄像头资源,导致程序运行两天后摄像头无法再打开。后来加上了 stop() 和 deleteLater() 的时序控制,问题才解决。
好了,这一章的内容就到这里。多媒体模块其实还有很多细节,比如字幕支持、音轨切换、录制视频等,但掌握了今天讲的这些核心组件,你已经能应对 90% 的开发场景了。多动手写写,遇到问题多看看 Qt 官方文档,慢慢就熟练了。
公众号:蓝海资料掘金营,微信deep3321