23. QT多媒体:QMediaPlayer、QMediaPlaylist、QVideoWidget、QAudioOutput、摄像头操作

多媒体开发,说白了就是让程序能「看」能「听」。我记得刚入行那会儿,Qt 的多媒体模块还叫 Phonon,API 设计得挺别扭。后来 Qt 5 时代全面重构成了 QMediaPlayer 这套体系,用起来顺手多了。今天我们就来聊聊这套东西怎么用,以及我在实际项目中踩过的那些坑。

核心知识点一览:

  • QMediaPlayer —— 播放器的核心引擎
  • QMediaPlaylist —— 管理播放列表
  • QVideoWidget —— 视频渲染窗口
  • QAudioOutput —— 音频输出控制
  • QCamera —— 摄像头采集与预览
Qt 多媒体模块 QMediaPlayer 播放控制核心 QMediaPlaylist 播放列表管理 QVideoWidget 视频渲染窗口 QAudioOutput 音频输出控制 QCamera 摄像头采集 QCameraImageCapture 拍照与截图 多媒体模块各组件关系图 —— 核心是 QMediaPlayer,其他组件围绕它协同工作

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