## 一、前言
在
qt5中的多媒体框架明显比qt4丰富了很多,使用也极其友好,提供的api接口非常简单明了,不需要像qt4中那样还需要绑定和创建路径之类的。同样也还是依赖本地解码器,qt6中的多媒体框架据说重写了,性能暴增很多,后端还支持多种解码方式,比如可以选用ffmpeg作为后端解码,这样的话就统一起来了,而且完全的跨平台,性能上主要体现在cpu的占用极低,但是也有缺点,那就是目前为止的版本比如Qt6.5及以下,还不支持视频流的播放,具体原因未知,不知道后期是否会加入支持还是架构原因不支持。
播放视频基本流程:
- 实例化视频播放控件 new QMediaPlayer
- 实例化视频
显示控件 new QVideoWidget
- 设置视频输出对象 mediaPlayer->setVideoOutput(videoWidget)
- Qt6需要单独指定音频输出 new QAudioOutput/mediaPlayer->setAudioOutput(audioOutput)
- 设置播放地址 mediaPlayer->setMedia/mediaPlayer->setSource
- 开始/暂停/停止 mediaPlayer->play()/mediaPlayer->pause()/mediaPlayer->stop()
## 二、效果图

## 三、体验地址
1. 国内站点:[
https://gitee.com/feiyangqingyun](https://gitee.com/feiyangqingyun)
2. 国际站点:[
https://github.com/feiyangqingyun](https://github.com/feiyangqingyun)
3. 个人作品:[
https://blog.csdn.net/feiyangqingyun/article/details/97565652](https://blog.csdn.net/feiyangqingyun/article/details/97565652)
4. 体验地址:[
https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g](https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g) 提取码:01jf
文件名:bin_video_demo/bin_linux_video。
## 四、相关代码
```cpp
QMediaThread::QMediaThread(QObject *parent) : VideoThread(parent)
{
//部分版本非句柄模式需要初始化音频输出设备(这可能是Qt的bug/不这样处理会崩溃)
#if 1
QAudioFormat format;
format.setSampleRate(8000);
format.setChannelCount(1);
format.setSampleSize(16);
format.setCodec("audio/pcm");
format.setSampleType(QAudioFormat::SignedInt);
format.setByteOrder(QAudioFormat::LittleEndian);
QAudioOutput *audioOutput = new QAudioOutput(format);
audioOutput->start();
audioOutput->deleteLater();
#endif
//实例化视频播放对象
mediaPlayer = new QMediaPlayer(this);
connect(mediaPlayer, SIGNAL(durationChanged(qint64)), this, SLOT(slot_receiveDuration(qint64)));
connect(mediaPlayer, SIGNAL(positionChanged(qint64)), this, SLOT(slot_receivePosition(qint64)));
connect(mediaPlayer, SIGNAL(volumeChanged(int)), this, SIGNAL(receiveVolume(int)));
connect(mediaPlayer, SIGNAL(mutedChanged(bool)), this, SIGNAL(receiveMuted(bool)));
connect(mediaPlayer, SIGNAL(metaDataChanged()), this, SLOT(readMediaInfo()));
connect(mediaPlayer, SIGNAL(stateChanged(QMediaPlayer::State)), this, SLOT(stateChanged(QMediaPlayer::State)));
connect(mediaPlayer, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), this, SLOT(mediaStatusChanged(QMediaPlayer::MediaStatus)));
//实例化视频录制对象
mediaRecorder = new QMediaRecorder(mediaPlayer, this);
connect(mediaRecorder, SIGNAL(stateChanged(QMediaRecorder::State)), this, SLOT(updateRecorderState(QMediaRecorder::State)));
this->updateRecorderState(mediaRecorder->state());
//设置录制文件音视频编码
格式 QAudioEncoderSettings audioSettings;
audioSettings.setCodec("aac");
audioSettings.setSampleRate(48000);
mediaRecorder->setAudioSettings(audioSettings);
QVideoEncoderSettings videoSettings;
videoSettings.setCodec("h264");
mediaRecorder->setVideoSettings(videoSettings);
//实例化视频显示控件
videoWidget = new QVideoWidget;
//要加上这行不然可能会闪烁
videoWidget->setAttribute(Qt::WA_OpaquePaintEvent);
//设置拉伸填充
videoWidget->setAspectRatioMode(Qt::IgnoreAspectRatio);
//实例化视频画布控件用于回调拿到每一帧图片
数据 videoSurface = new AbstractVideoSurface;
connect(videoSurface, SIGNAL(receiveImage(QImage, int)), this, SIGNAL(receiveImage(QImage, int)));
connect(videoSurface, SIGNAL(receiveRgb(int, int, quint8 *, int)), this, SIGNAL(receiveFrame(int, int, quint8 *, int)));
connect(videoSurface, SIGNAL(resolutionChanged(int, int)), this, SLOT(resolutionChanged(int, int)));
mediaPlayer->setVideoOutput(videoSurface);
//设置播放进度触发间隔
mediaPlayer->setNotifyInterval(300);
}
QMediaThread::~QMediaThread()
{
videoWidget->deleteLater();
videoSurface->deleteLater();
}
bool QMediaThread::openVideo()
{
//先检查地址是否正常(文件是否存在或者网络地址是否可达)
if (!VideoHelper::checkUrl(this, videoType, videoUrl, connectTimeout)) {
return false;
}
//启动计时
timer.start();
//句柄模式将视频显示控件放到句柄控件布局中
if (videoMode == VideoMode_Hwnd) {
hwndWidget->layout()->addWidget(videoWidget);
}
//视频需要主动显示对应显示控件
if (!onlyAudio && videoMode == VideoMode_Hwnd) {
QMetaObject::invokeMethod(videoWidget, "show");
}
//绘制模式拿到图片/GPU模式取出rgb数据用
opengl绘制
if (videoMode == VideoMode_Painter) {
videoSurface->setType(1);
} else if (videoMode == VideoMode_Opengl) {
videoSurface->setType(2);
}
//设置地址开始播放
if (videoType == VideoType_FileLocal) {
mediaPlayer->setMedia(QUrl::fromLocalFile(videoUrl));
} else {
mediaPlayer->setMedia(QUrl(VideoHelper::getRightUrl(videoType, videoUrl)));
}
mediaPlayer->play();
isOk = true;
emit recorderStateChanged(RecorderState_Stopped, fileName);
lastTime = QDateTime::currentDateTime();
int time = timer.elapsed();
debug("打开成功",
QString("用时: %1 毫秒").arg(time));
//只有获取到了宽高信息才算真正打开完成
//emit receivePlayStart(time);
return isOk;
}
void QMediaThread::closeVideo()
{
//先停止录制
recordStop();
//隐藏视频控件
QMetaObject::invokeMethod(videoWidget, "hide");
//停止播放
#if (QT_VERSION >= QT_VERSION_CHECK(5,6,0))
mediaPlayer->stop();
#else
QMetaObject::invokeMethod(mediaPlayer, "stop");
#endif
//调用父类关闭
VideoThread::closeVideo();
}
void QMediaThread::slot_receiveDuration(qint64 duration)
{
this->updateTime();
this->duration = duration;
this->checkVideoType();
if (getIsFile()) {
emit receiveDuration(duration);
}
}
void QMediaThread::slot_receivePosition(qint64 position)
{
//文件和视频流都有进度需要更新消息时间(只有文件才需要更新进度)
this->updateTime();
this->position = position;
if (getIsFile()) {
emit receivePosition(position);
//如果设置了重复循环播放则快到了文件末尾重新设置位置即可
if (this->getPlayRepeat() && (this->getDuration() - position) < 500) {
QMetaObject::invokeMethod(this, "setPosition", Q_ARG(qint64, 0));
}
}
}
```