多媒体的应用
# 08.多媒体的应用
# 目录介绍
# 8.1 多媒体介绍
# 8.1.1 多媒体支持
Qt 提供了丰富的多媒体支持,涵盖了音频、视频、摄像头、图像处理等功能。
# 8.1.2 多媒体模块概述
Qt 多媒体模块位于 QtMultimedia 库中,提供了以下功能:
- 音频播放与录制
- 视频播放与录制
- 摄像头访问
- 图像捕捉与处理
- 音频效果处理
要使用 Qt 多媒体模块,需要在项目文件(.pro)中添加以下配置:
QT += multimedia
# 8.2 音频播放
# 8.2.1 QMediaPlayer介绍
QMediaPlayer 用于播放音频文件。
#include <QMediaPlayer>
#include <QUrl>
#include <QDebug>
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
// 创建媒体播放器
QMediaPlayer player;
// 设置音频文件
player.setMedia(QUrl::fromLocalFile("/path/to/audio.mp3"));
// 播放音频
player.play();
// 监听播放状态
QObject::connect(&player, &QMediaPlayer::stateChanged, [](QMediaPlayer::State state) {
if (state == QMediaPlayer::PlayingState) {
qDebug() << "Audio is playing!";
}
});
return app.exec();
}
# 8.2.2 QMediaPlayer功能
QMediaPlayer 提供了以下主要功能:
- (1) 媒体播放:支持播放多种格式的音频和视频文件(如 MP3、WAV、MP4 等)。支持从本地文件、网络流或内存中加载媒体内容。
- (2) 播放控制:播放/暂停:开始或暂停媒体播放。停止:停止播放并释放资源。跳转:跳转到指定时间点播放。循环播放:设置媒体是否循环播放。
- (3) 状态和错误处理:提供媒体播放状态(如播放中、暂停、停止、错误等)。捕获并处理播放错误(如文件不存在、格式不支持等)。
- (4) 元数据访问:获取媒体的元数据(如标题、作者、时长、封面等)。
- (5) 音量控制:设置和获取播放音量。
- (6) 视频输出:支持将视频渲染到
QVideoWidget或自定义的QAbstractVideoSurface。 - (7) 信号与槽机制:通过信号与槽机制,实时响应播放状态、位置、音量等变化。
# 8.2.3 QMediaPlayer作用
QMediaPlayer 的主要作用是为 Qt 应用程序提供多媒体播放能力,适用于以下场景:
- 音频播放:播放背景音乐、提示音、语音等。
- 视频播放:播放本地或网络视频。
- 流媒体:支持实时流媒体播放(如网络电台、直播等)。
- 多媒体应用:用于开发音乐播放器、视频播放器、多媒体演示等应用。
# 8.2.4 核心方法API
以下是 QMediaPlayer 的常用方法:
| 方法 | 描述 |
|---|---|
setMedia(const QMediaContent &media) | 设置媒体内容(文件路径或 URL)。 |
play() | 开始播放。 |
pause() | 暂停播放。 |
stop() | 停止播放。 |
setPosition(qint64 position) | 跳转到指定时间点(毫秒)。 |
setVolume(int volume) | 设置音量(0-100)。 |
state() | 获取当前播放状态。 |
duration() | 获取媒体总时长(毫秒)。 |
position() | 获取当前播放位置(毫秒)。 |
setVideoOutput(QVideoWidget *output) | 设置视频输出到 QVideoWidget。 |
播放状态
state():获取当前播放状态(PlayingState、PausedState、StoppedState)。mediaStatus():获取媒体状态(NoMedia、LoadedMedia、BufferingMedia等)。
# 8.2.5 QMediaPlayer原理
QMediaPlayer 的工作原理基于 Qt 的多媒体模块,其核心流程如下:
(1) 媒体加载:使用 setMedia() 方法加载媒体文件或 URL。媒体内容可以是本地文件、网络资源或内存数据。
(2) 解码与播放:QMediaPlayer 依赖于底层的多媒体框架(如 GStreamer、DirectShow、AVFoundation 等)进行媒体解码。解码后的音频数据通过音频设备输出,视频数据通过 QVideoWidget 或自定义渲染器显示。
(3) 状态管理:QMediaPlayer 维护一个内部状态机,通过 state() 方法获取当前状态。包括以下状态:
StoppedState:停止状态。无论是手动调用 stop() 方法,还是播放结束,播放器都会进入 StoppedState。PlayingState:播放中。PausedState:暂停状态。
(4) 信号与槽:QMediaPlayer 通过信号与槽机制通知状态变化和事件,例如:
stateChanged(QMediaPlayer::State state):播放状态变化时触发。mediaStatusChanged(QMediaPlayer::MediaStatus status):媒体状态变化时触发。positionChanged(qint64 position):播放位置变化时触发。durationChanged(qint64 duration):媒体总时长变化时触发。error(QMediaPlayer::Error error):发生错误时触发。
(5) 资源管理:播放完成后,调用 stop() 方法释放资源。使用 setMedia(nullptr) 清除当前媒体内容。
# 8.2.6 QMediaPlayer信号槽
以下是 QMediaPlayer 的常用信号:
| 信号 | 描述 |
|---|---|
stateChanged(QMediaPlayer::State state) | 播放状态变化时触发。 |
positionChanged(qint64 position) | 播放位置变化时触发。 |
durationChanged(qint64 duration) | 媒体时长变化时触发。 |
errorOccurred(QMediaPlayer::Error error, const QString &errorString) | 播放出错时触发。 |
mediaStatusChanged(QMediaPlayer::MediaStatus status) | 媒体状态变化时触发(如加载中、加载完成等)。 |
// 监听播放状态
QObject::connect(&player, &QMediaPlayer::stateChanged, [](QMediaPlayer::State state) {
qDebug() << "State changed:" << state;
});
// 监听媒体状态
QObject::connect(&player, &QMediaPlayer::mediaStatusChanged, [](QMediaPlayer::MediaStatus status) {
qDebug() << "Media status changed:" << status;
});
// 监听播放位置
QObject::connect(&player, &QMediaPlayer::positionChanged, [](qint64 position) {
qDebug() << "Position changed:" << position;
});
// 监听错误
QObject::connect(&player, QOverload<QMediaPlayer::Error>::of(&QMediaPlayer::error), [](QMediaPlayer::Error error) {
qDebug() << "Error occurred:" << error;
});
# 8.2.7 综合播放案例
#include <QCoreApplication>
#include <QMediaPlayer>
#include <QAudioOutput>
#include <QDebug>
#include <QFileInfo>
class AudioPlayer : public QObject {
Q_OBJECT
public:
AudioPlayer(const QString &filePath, QObject *parent = nullptr)
: QObject(parent), m_player(new QMediaPlayer(this)), m_audioOutput(new QAudioOutput(this)) {
// 设置音频输出
m_player->setAudioOutput(m_audioOutput);
// 设置媒体文件
QFileInfo fileInfo(filePath);
if (!fileInfo.exists()) {
qWarning() << "File does not exist:" << filePath;
QCoreApplication::exit(1);
return;
}
m_player->setSource(QUrl::fromLocalFile(filePath));
// 连接信号和槽
connect(m_player, &QMediaPlayer::stateChanged, this, &AudioPlayer::onStateChanged);
connect(m_player, &QMediaPlayer::mediaStatusChanged, this, &AudioPlayer::onMediaStatusChanged);
connect(m_player, &QMediaPlayer::errorOccurred, this, &AudioPlayer::onErrorOccurred);
// 开始播放
m_player->play();
}
private slots:
// 处理播放状态变化
void onStateChanged(QMediaPlayer::PlaybackState state) {
switch (state) {
case QMediaPlayer::StoppedState:
qDebug() << "State: Stopped";
break;
case QMediaPlayer::PlayingState:
qDebug() << "State: Playing";
break;
case QMediaPlayer::PausedState:
qDebug() << "State: Paused";
break;
}
}
// 处理媒体状态变化
void onMediaStatusChanged(QMediaPlayer::MediaStatus status) {
switch (status) {
case QMediaPlayer::NoMedia:
qDebug() << "MediaStatus: No Media";
break;
case QMediaPlayer::LoadingMedia:
qDebug() << "MediaStatus: Loading Media";
break;
case QMediaPlayer::LoadedMedia:
qDebug() << "MediaStatus: Media Loaded";
break;
case QMediaPlayer::StalledMedia:
qDebug() << "MediaStatus: Media Stalled";
break;
case QMediaPlayer::BufferingMedia:
qDebug() << "MediaStatus: Buffering Media";
break;
case QMediaPlayer::BufferedMedia:
qDebug() << "MediaStatus: Media Buffered";
break;
case QMediaPlayer::EndOfMedia:
qDebug() << "MediaStatus: End of Media";
QCoreApplication::quit(); // 播放结束后退出程序
break;
case QMediaPlayer::InvalidMedia:
qDebug() << "MediaStatus: Invalid Media";
break;
}
}
// 处理播放错误
void onErrorOccurred(QMediaPlayer::Error error, const QString &errorString) {
qWarning() << "Error:" << errorString;
switch (error) {
case QMediaPlayer::NoError:
break;
case QMediaPlayer::ResourceError:
qWarning() << "Resource Error: Unable to load media.";
break;
case QMediaPlayer::FormatError:
qWarning() << "Format Error: Unsupported media format.";
break;
case QMediaPlayer::NetworkError:
qWarning() << "Network Error: Network issue occurred.";
break;
case QMediaPlayer::AccessDeniedError:
qWarning() << "Access Denied Error: Permission issue.";
break;
default:
qWarning() << "Unknown Error.";
break;
}
QCoreApplication::exit(1); // 发生错误时退出程序
}
private:
QMediaPlayer *m_player;
QAudioOutput *m_audioOutput;
};
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
if (argc < 2) {
qWarning() << "Usage: ./AudioPlayer <audio_file_path>";
return 1;
}
QString filePath = argv[1];
AudioPlayer player(filePath);
return app.exec();
}
#include "main.moc"
# 8.2.8 音频效果处理
QAudioEffect 用于处理音频效果,如均衡器、混响等。
#include <QMediaPlayer>
#include <QAudioEffect>
#include <QUrl>
int main(int argc, char *argv[]) {
QCoreApplication app(argc, argv);
// 创建媒体播放器
QMediaPlayer player;
player.setMedia(QUrl::fromLocalFile("/path/to/audio.mp3"));
// 创建音频效果(例如均衡器)
QAudioEffect *effect = new QAudioEffect(&player);
effect->setEffectType(QAudioEffect::EqualizerEffect);
effect->setEnabled(true);
// 播放音频
player.play();
return app.exec();
}
# 8.3 音频录制
# 8.3.1 QAudioRecorder介绍
QAudioRecorder 用于录制音频。
#include <QAudioRecorder>
#include <QDebug>
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
// 创建音频录制器
QAudioRecorder recorder;
// 设置输出文件
recorder.setOutputLocation(QUrl::fromLocalFile("/path/to/recording.wav"));
// 开始录制
recorder.record();
qDebug() << "Recording started!";
// 停止录制
QTimer::singleShot(5000, [&recorder]() {
recorder.stop();
qDebug() << "Recording stopped!";
});
return app.exec();
}
# 8.4 视频播放
# 8.4.1 视频播放案例
QMediaPlayer 结合 QVideoWidget 可以播放视频。
#include <QApplication>
#include <QMediaPlayer>
#include <QVideoWidget>
#include <QUrl>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建媒体播放器和视频窗口
QMediaPlayer player;
QVideoWidget videoWidget;
// 设置视频文件
player.setMedia(QUrl::fromLocalFile("/path/to/video.mp4"));
// 将视频输出到窗口
player.setVideoOutput(&videoWidget);
// 显示窗口并播放视频
videoWidget.show();
player.play();
return app.exec();
}
# 8.4.2 视频录制
QCamera 和 QMediaRecorder 用于录制视频。
#include <QApplication>
#include <QCamera>
#include <QMediaRecorder>
#include <QCameraViewfinder>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建摄像头和视图
QCamera camera;
QCameraViewfinder viewfinder;
// 设置摄像头视图
camera.setViewfinder(&viewfinder);
// 创建媒体录制器
QMediaRecorder recorder(&camera);
// 设置输出文件
recorder.setOutputLocation(QUrl::fromLocalFile("/path/to/recording.mp4"));
// 显示摄像头视图
viewfinder.show();
// 开始录制
camera.start();
recorder.record();
qDebug() << "Recording started!";
// 停止录制
QTimer::singleShot(10000, [&recorder]() {
recorder.stop();
qDebug() << "Recording stopped!";
});
return app.exec();
}
# 8.5 QCamera
# 8.5.1 QCamera介绍
QCamera 用于访问摄像头并显示实时画面。
#include <QApplication>
#include <QCamera>
#include <QCameraViewfinder>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建摄像头和视图
QCamera camera;
QCameraViewfinder viewfinder;
// 设置摄像头视图
camera.setViewfinder(&viewfinder);
// 显示摄像头视图
viewfinder.show();
// 启动摄像头
camera.start();
return app.exec();
}
# 8.5.2 图像捕捉
QCameraImageCapture 用于从摄像头捕捉静态图像。
#include <QApplication>
#include <QCamera>
#include <QCameraImageCapture>
#include <QCameraViewfinder>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 创建摄像头和视图
QCamera camera;
QCameraViewfinder viewfinder;
camera.setViewfinder(&viewfinder);
// 创建图像捕捉器
QCameraImageCapture imageCapture(&camera);
// 显示摄像头视图
viewfinder.show();
// 启动摄像头
camera.start();
// 捕捉图像
imageCapture.capture("/path/to/capture.jpg");
return app.exec();
}
# 8.6 Qt 6 多媒体架构
# 8.6.1 Qt 5 vs Qt 6 多媒体模块变革
Qt 6 完全重写了多媒体模块——从 QMediaPlayer + QMediaRecorder 变为以 QMediaCaptureSession 为核心:
Qt 5 架构: Qt 6 架构:
QMediaPlayer QMediaPlayer
├── setMedia(url) ├── setSource(url)
├── setVideoOutput(widget) → ├── setVideoOutput(QVideoWidget)
└── setAudioOutput(widget) └── setAudioOutput(QAudioOutput)
QMediaRecorder QMediaCaptureSession (核心)
├── setOutput(url) ├── setCamera(QCamera)
└── record() / stop() ├── setAudioInput(QAudioInput)
├── setImageCapture(QImageCapture)
├── setVideoOutput(QVideoWidget)
└── setRecorder(QMediaRecorder)
QMediaRecorder
└── record() / stop()
# 8.6.2 QMediaCaptureSession 综合使用
// Qt 6 典型的视频录制 + 预览
QMediaCaptureSession session;
QCamera camera;
QMediaRecorder recorder;
QVideoWidget viewfinder;
// 组装管线
session.setCamera(&camera);
session.setVideoOutput(&viewfinder);
session.setRecorder(&recorder);
// 配置录制
QMediaFormat format(QMediaFormat::MPEG4);
format.setVideoCodec(QMediaFormat::VideoCodec::H264);
recorder.setMediaFormat(format);
recorder.setOutputLocation(QUrl::fromLocalFile("/tmp/output.mp4"));
// 开始预览
camera.start();
viewfinder.show();
// 开始录制
recorder.record();
// ...
recorder.stop();
# 8.6.3 后端实现原理
Qt 多媒体在不同平台使用不同后端:
Qt 多媒体模块(Qt Multimedia):
├── Linux: GStreamer 管道
│ QMediaPlayer → playbin
│ QCamera → v4l2src → ...
│
├── macOS/iOS: AVFoundation
│ QMediaPlayer → AVPlayer
│ QCamera → AVCaptureSession
│
├── Windows: Windows Media Foundation (Qt 6)
│ QMediaPlayer → IMFMediaEngine
│
└── 嵌入式 Linux: GStreamer + v4l2 + DRM/KMS
QCamera → v4l2src ! video/x-raw ! ...
渲染 → appsink → QVideoFrame → QVideoWidget
# 8.7 音频设备与编解码
# 8.7.1 QAudioDevice / QMediaDevices
Qt 6 统一了设备发现 API:
#include <QMediaDevices>
// 列出所有音频输出设备
for (const QAudioDevice& device : QMediaDevices::audioOutputs()) {
qDebug() << "Output:" << device.description()
<< "channels:" << device.maximumChannelCount();
}
// 列出所有音频输入设备
for (const QAudioDevice& device : QMediaDevices::audioInputs()) {
qDebug() << "Input:" << device.description();
}
// 列出所有摄像头
for (const QCameraDevice& device : QMediaDevices::videoInputs()) {
qDebug() << "Camera:" << device.description()
<< "position:" << device.position();
}
// 选择特定设备播放
QAudioOutput output(QMediaDevices::defaultAudioOutput());
// 设备变化通知
connect(&QMediaDevices::instance(), &QMediaDevices::audioOutputsChanged,
this, [this]() { rescanAudioDevices(); });
# 8.7.2 QAudioSink 低级音频输出
// 低级音频播放——直接推送 PCM 数据
QAudioFormat format;
format.setSampleRate(44100);
format.setChannelCount(2);
format.setSampleFormat(QAudioFormat::Int16);
QAudioDevice device = QMediaDevices::defaultAudioOutput();
if (!device.isFormatSupported(format)) {
format = device.preferredFormat();
}
QAudioSink* audioSink = new QAudioSink(device, format, this);
QIODevice* stream = audioSink->start(); // 返回可写入的 QIODevice
// 生成并推送 PCM 数据
QByteArray pcmData = generateSineWave(440, 44100, 1000); // 1 秒 440Hz 正弦波
stream->write(pcmData);
# 8.7.3 支持的编解码格式
| 类型 | 常见格式 | 依赖后端 |
|---|---|---|
| 音频播放 | MP3, WAV, OGG, FLAC, AAC | GStreamer / AVFoundation |
| 视频播放 | MP4, AVI, MKV, WebM | GStreamer |
| 视频编码 | H.264, H.265, VP8, VP9 | 硬件编码器 |
| 音频编码 | AAC, MP3, Opus | GStreamer / CoreAudio |
| 图像拍照 | JPEG, PNG, BMP | 内置 |
# 8.8 嵌入式多媒体实战
# 8.8.1 硬件加速
嵌入式 ARM 板(如 i.MX8)有硬件编解码器——Qt 通过 GStreamer 调用:
// 启用硬件加速视频解码
// 环境变量:
// GST_PLUGIN_PATH=/usr/lib/gstreamer-1.0
// QT_GSTREAMER_USE_PLAYBIN_VO=1
// GStreamer 管道(Qt 自动构建,这里展示对应关系):
// 软件解码:
// filesrc ! qtdemux ! h264parse ! avdec_h264 ! videoconvert ! appsink
//
// 硬件解码(i.MX8 VPU):
// filesrc ! qtdemux ! h264parse ! vpudec ! videoconvert ! appsink
// 在代码中检查是否支持硬件加速
QMediaFormat format(QMediaFormat::MPEG4);
if (format.videoCodec() == QMediaFormat::VideoCodec::H264) {
qDebug() << "Using H.264 codec";
}
# 8.8.2 QVideoSink ——直接访问视频帧
class VideoProcessor : public QObject {
Q_OBJECT
public:
void setPlayer(QMediaPlayer* player) {
QVideoSink* sink = player->videoSink();
connect(sink, &QVideoSink::videoFrameChanged,
this, &VideoProcessor::onFrameChanged);
}
private:
void onFrameChanged(const QVideoFrame& frame) {
if (!frame.isValid()) return;
// 映射帧到 CPU 可访问内存
QVideoFrame mappedFrame = frame;
if (mappedFrame.map(QVideoFrame::ReadOnly)) {
QImage image = mappedFrame.toImage();
detectMotion(image); // 运动检测
mappedFrame.unmap();
}
}
};
# 8.8.3 低延迟视频预览
QCamera camera;
QMediaCaptureSession session;
QVideoWidget viewfinder;
session.setCamera(&camera);
session.setVideoOutput(&viewfinder);
void setupPreview() {
viewfinder.setAspectRatioMode(Qt::KeepAspectRatio);
viewfinder.setFullScreen(true);
viewfinder.show();
camera.start();
// 调整相机参数
camera.setFocusMode(QCamera::FocusModeAuto);
camera.setWhiteBalanceMode(QCamera::WhiteBalanceAuto);
}
# 8.9 综合案例
# 案例:嵌入式视频监控与抓拍
class SurveillanceSystem : public QObject {
Q_OBJECT
public:
SurveillanceSystem() {
auto cameras = QMediaDevices::videoInputs();
if (cameras.isEmpty()) {
qFatal("No camera found!");
return;
}
m_camera.reset(new QCamera(cameras.first()));
m_session.setCamera(m_camera.get());
m_session.setVideoOutput(&m_viewfinder);
m_viewfinder.show();
m_imageCapture.reset(new QImageCapture);
m_session.setImageCapture(m_imageCapture.get());
m_recorder.reset(new QMediaRecorder);
m_session.setRecorder(m_recorder.get());
QMediaFormat format(QMediaFormat::MPEG4);
format.setVideoCodec(QMediaFormat::VideoCodec::H264);
m_recorder->setMediaFormat(format);
// 运动检测——每 200ms 检查
connect(&m_motionTimer, &QTimer::timeout, this, &SurveillanceSystem::checkMotion);
m_motionTimer.start(200);
m_camera->start();
}
void captureSnapshot(const QString& path) {
m_imageCapture->captureToFile(path);
}
void startRecording(const QString& path) {
m_recorder->setOutputLocation(QUrl::fromLocalFile(path));
m_recorder->record();
}
void stopRecording() {
m_recorder->stop();
}
private:
void checkMotion() {
if (motionDetected()) {
startRecording(generateTimestampedPath());
QTimer::singleShot(30000, this, [this]() { stopRecording(); });
}
}
QMediaCaptureSession m_session;
QScopedPointer<QCamera> m_camera;
QScopedPointer<QImageCapture> m_imageCapture;
QScopedPointer<QMediaRecorder> m_recorder;
QVideoWidget m_viewfinder;
QTimer m_motionTimer;
};
# 8.10 速查表
| 类(Qt 6) | 用途 | 关键方法 |
|---|---|---|
QMediaPlayer | 音频/视频播放 | setSource(), play(), pause(), stop() |
QAudioOutput | 音频输出 | setVolume(), setMuted() |
QMediaCaptureSession | 多媒体管线核心 | setCamera(), setRecorder(), setImageCapture() |
QCamera | 摄像头访问 | start(), stop(), setFocusMode() |
QImageCapture | 图像抓拍 | captureToFile(), capture() |
QMediaRecorder | 视频录制 | record(), stop(), setMediaFormat() |
QVideoWidget | 视频预览窗口 | show(), setAspectRatioMode() |
QVideoSink | 视频帧访问 | videoFrameChanged() |
QMediaDevices | 设备发现 | audioOutputs(), videoInputs() |
QAudioSink | 低级音频输出 | start()(返回 QIODevice) |
QMediaFormat | 编解码格式 | setVideoCodec(), setAudioCodec() |
多媒体铁律:
- Qt 5 代码迁移到 Qt 6 需要重写多媒体部分——架构完全不同
- 嵌入式必须确保 GStreamer 插件齐全——缺失编解码器时静默失败
QCamera在启动前检查isAvailable()——设备可能被占用- 视频处理放到
QVideoSink::videoFrameChanged——不阻塞主线程 - 录制时使用
QMediaFormat::H264硬件编码——CPU 软件编码无法实时 QMediaCaptureSession是 Qt 6 的核心——它连接所有多媒体组件