一、环境介绍
操作系统: win10 64位
QT版本: QT5.12.6
编译器: MinGW 32
二、mdk-sdk 介绍
github 首页地址: https://github.com/wang-bin/mdk-sdk
Features
Simple and powerful API set
Cross platform: Windows, UWP, Linux, macOS, Android, iOS, Raspberry Pi
Hardware accelerated decoding and 0-copy GPU rendering for all platforms
OpenGL, D3D11, Vulkan and Metal rendering w/ or w/o user provided context
Integrated with any gui toolkits or apps via OpenGL, D3D11, Vulkan and Metal (OBS, Qt, SDL, glfw, SFML etc.) easily
Seamless/Gapless media and bitrate switch for any media
HDR rendering in GPU
Optimized Continuous seeking. As fast as mpv, but much lower cpu, memory and gpu load. Suitable for timeline preview
Smart FFmpeg runtime. See https://github.com/wang-bin/mdk-sdk/wiki/FFmpeg-Runtime
mdk-sdk的API使用介绍: https://github.com/wang-bin/mdk-sdk/wiki/Player-APIs
mdk-sdk的使用例子(CSDN): https://download.csdn.net/download/xiaolong1126626497/16273881
mdk-sdk的SDK包(CSDN)--截止当前是最新版本: https://download.csdn.net/download/xiaolong1126626497/16273875
mdk-sdk的使用例子(GitHub):https://github.com/wang-bin/mdk-examples
三、工程源码
工程源码地址: https://download.csdn.net/download/xiaolong1126626497/16548885
下面播放器例子比较简单,但是完成了播放器基本功能,支持的功能如下:
1. 音量设置
2. 静音切换
3. 拍照(保存在程序运行目录下 png图片)
4. 跳转 (鼠标可以点击播放进度条进行跳转导致指定位置)
5. 播放进度时间
6. 暂停、加载、播放、重新播放
7. 播放的速率设置
完整的播放器实现看这里:https://xiaolong.blog.csdn.net/article/details/117557963
3.1 运行效果
3.2 pro工程文件
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets opengl CONFIG += c++17 utf8_source MDK_SDK = $$PWD/mdk-sdk INCLUDEPATH += $$MDK_SDK/include MDK_ARCH = x86 LIBS += -L$$MDK_SDK/lib/$$MDK_ARCH -lmdk LIBS += -L$$MDK_SDK/bin/$$MDK_ARCH # The following define makes your compiler emit warnings if you use # any Qt feature that has been marked deprecated (the exact warnings # depend on your compiler). Please consult the documentation of the # deprecated API in order to know how to port your code away from it. DEFINES += QT_DEPRECATED_WARNINGS # You can also make your code fail to compile if it uses deprecated APIs. # In order to do so, uncomment the following line. # You can also select to disable deprecated APIs only up to a certain version of Qt. #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0 SOURCES += \ QMDKWidget.cpp \ main.cpp \ widget.cpp HEADERS += \ QMDKWidget.h \ widget.h FORMS += \ widget.ui # Default rules for deployment. qnx: target.path = /tmp/$${TARGET}/bin else: unix:!android: target.path = /opt/$${TARGET}/bin !isEmpty(target.path): INSTALLS += target
3.3 QMDKWidget.cpp
1./* * Copyright (c) 2020 WangBin <wbsecg1 at gmail.com> * MDK SDK with QOpenGLWidget example */ #include "QMDKWidget.h" #include "mdk/Player.h" #include <QCoreApplication> #include <QDebug> #include <QDir> #include <QKeyEvent> #include <QOpenGLContext> #include <QStringList> #include <QScreen> bool av_MediaStatusChanged(mdk::MediaStatus m) { qDebug()<<"av_MediaStatusChanged."; return true; } bool av_MediaEvent(const mdk::MediaEvent&m) { qDebug()<<"av_MediaEvent."; return true; } using namespace MDK_NS; QMDKWidget::QMDKWidget(QWidget *parent, Qt::WindowFlags f) : QOpenGLWidget(parent, f) , player_(std::make_shared<Player>()) { player_->setVideoDecoders({"VT", "VAAPI", "MFT:d3d=11", "DXVA", "MMAL", "AMediaCodec:java=1:copy=0:surface=1:async=0", "FFmpeg"}); //设置回调: 例如在渲染器中接收到新帧时,将调用该回调 player_->setRenderCallback([this](void*){ QMetaObject::invokeMethod(this, "update", Qt::QueuedConnection); }); //设置回调: 事件 player_->onEvent(av_MediaEvent); //获取状态 // MediaStatus a=player_->mediaStatus(); //设置回调: 状态改变 player_->onMediaStatusChanged(av_MediaStatusChanged); } QMDKWidget::~QMDKWidget() { makeCurrent(); player_->setVideoSurfaceSize(-1, -1); // cleanup gl renderer resources } //设置音量 值:> = 0。1.0是原始音量 void QMDKWidget::setVolume(float value) { player_->setVolume(value); } //当前进度 当前播放时间(以毫秒为单位)。相对于媒体的第一个时间戳。即mediaInfo().start_time,通常为0。 int64_t QMDKWidget::position() { return player_->position(); } //总时间 int64_t QMDKWidget::duration() { return player_->mediaInfo().duration; } //设置静音 void QMDKWidget::setMute(bool value) { player_->setMute(value); } void QMDKWidget::setPlaybackRate(float value) { //设置播放器播放速度 正常速度是1.0 player_->setPlaybackRate(value); } //旋转视频 逆时针0、90、180、270 //player_->rotate(int degree, void* vo_opaque = nullptr); void QMDKWidget::setrotate(int degree) //设置旋转 { player_->rotate(degree); } /* 工程: QMDK_Player 日期: 2021-04-08 作者: DS小龙哥 环境: win10 QT5.12.6 MinGW32 功能: 设置解码器 */ void QMDKWidget::setDecoders(const QStringList &dec) { std::vector<std::string> v; foreach (QString d, dec) { v.push_back(d.toStdString()); } player_->setVideoDecoders(v); } /* 工程: QMDK_Player 日期: 2021-04-08 作者: DS小龙哥 环境: win10 QT5.12.6 MinGW32 功能: 设置媒体文件 */ void QMDKWidget::setMedia(const QString &url) { player_->setMedia(url.toUtf8().constData()); } void QMDKWidget::play() { player_->setState(State::Playing); } void QMDKWidget::pause() { player_->setState(State::Paused); } void QMDKWidget::stop() { player_->setState(State::Stopped); } bool QMDKWidget::isPaused() const { return player_->state() == State::Paused; } void QMDKWidget::seek(qint64 ms) { player_->seek(ms); } void QMDKWidget::snapshot() { Player::SnapshotRequest sr{}; player_->snapshot(&sr, [](Player::SnapshotRequest *_sr, double frameTime) { const QString path = QDir::toNativeSeparators( QString("%1/%2.png") .arg(QCoreApplication::applicationDirPath()) .arg(frameTime)); return path.toStdString(); // Here's how to convert SnapshotRequest to QImage and save it to disk. /*if (_sr) { const QImage img = QImage(_sr->data, _sr->width, _sr->height, QImage::Format_RGBA8888); if (img.save(path)) { qDebug() << "Snapshot saved:" << path; } else { qDebug() << "Failed to save:" << path; } } else { qDebug() << "Snapshot failed."; } return "";*/ }); } void QMDKWidget::resizeGL(int w, int h) { //auto s = screen(); //qDebug("resizeGL>>>>>dpr: %f, logical dpi: (%f,%f), phy dpi: (%f,%f)", s->devicePixelRatio(), s->logicalDotsPerInchX(), s->logicalDotsPerInchY(), s->physicalDotsPerInchX(), s->physicalDotsPerInchY()); player_->setVideoSurfaceSize(w*devicePixelRatio(), h*devicePixelRatio()); } void QMDKWidget::paintGL() { player_->renderVideo(); }
3.4 QMDKWidget.h
/* * Copyright (c) 2020 WangBin <wbsecg1 at gmail.com> * MDK SDK with QOpenGLWindow example */ #pragma once #include <QOpenGLWidget> #include <memory> namespace mdk { class Player; } class QMDKWidget : public QOpenGLWidget { public: QMDKWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); ~QMDKWidget(); void setDecoders(const QStringList& dec); void setMedia(const QString& url); void play(); void pause(); void stop(); bool isPaused() const; void seek(qint64 ms); void snapshot(); void setPlaybackRate(float value); //播放速度 void setrotate(int degree); //设置旋转 void setMute(bool value); //设置静音 void setVolume(float value); //设置音量 int64_t duration(); //视频总时间 int64_t position(); //当前进度 protected: void resizeGL(int w, int h) override; void paintGL() override; private: std::shared_ptr<mdk::Player> player_; };
3.5 widget.cpp
#include "widget.h" #include "ui_widget.h" Widget::Widget(QWidget *parent) : QWidget(parent) , ui(new Ui::Widget) { ui->setupUi(this); timer = new QTimer(this); connect(timer, SIGNAL(timeout()), this, SLOT(timeout_update())); } Widget::~Widget() { delete ui; } bool Widget::eventFilter(QObject *obj, QEvent *event) { if(obj==ui->horizontalSlider) { if (event->type()==QEvent::MouseButtonPress) //判断类型 { QMouseEvent *mouseEvent = static_cast<QMouseEvent *>(event); if (mouseEvent->button() == Qt::LeftButton) //判断左键 { int value = QStyle::sliderValueFromPosition(ui->horizontalSlider->minimum(), ui->horizontalSlider->maximum(), mouseEvent->pos().x(), ui->horizontalSlider->width()); ui->horizontalSlider->setValue(value); //跳转播放器 ui->AV_player->seek(value); } } } return QObject::eventFilter(obj,event); } //定时器超时 void Widget::timeout_update() { int64_t pos=ui->AV_player->position(); ui->horizontalSlider->setValue(pos); // 设置时间 ui->label_t2->setText(QString("%1").arg(pos)); } /* 工程: QMDK_Player 日期: 2021-03-31 作者: DS小龙哥 环境: win10 QT5.12.6 MinGW32 功能: 加载视频文件 */ void Widget::on_pushButton_load_clicked() { QString filename=QFileDialog::getOpenFileName(this,"选择打开的文件","C:/",tr("*.*")); ui->AV_player->setMedia(filename); ui->AV_player->play(); QThread::msleep(100); //设置总时间 ui->label_t1->setText(QString("%1").arg(ui->AV_player->duration())); //设置进度条滑块范围 ui->horizontalSlider->setMinimum(0); ui->horizontalSlider->setMaximum(ui->AV_player->duration()); //事件监听 ui->horizontalSlider->installEventFilter(this); //开启定时器 timer->start(100); } /* 工程: QMDK_Player 日期: 2021-03-31 作者: DS小龙哥 环境: win10 QT5.12.6 MinGW32 功能: 暂停/继续 */ void Widget::on_pushButton_pause_clicked() { timer->stop(); ui->AV_player->pause(); } /* 工程: QMDK_Player 日期: 2021-03-31 作者: DS小龙哥 环境: win10 QT5.12.6 MinGW32 功能: 重新播放 */ void Widget::on_pushButton_reset_clicked() { ui->AV_player->seek(0); ui->AV_player->play(); timer->start(100); } /* 工程: QMDK_Player 日期: 2021-03-31 作者: DS小龙哥 环境: win10 QT5.12.6 MinGW32 功能: 播放 */ void Widget::on_pushButton_play_clicked() { ui->AV_player->play(); timer->start(100); } /* 工程: QMDK_Player 日期: 2021-03-31 作者: DS小龙哥 环境: win10 QT5.12.6 MinGW32 功能: 停止播放 */ void Widget::on_pushButton_stop_clicked() { ui->AV_player->stop(); } //静音设置 void Widget::on_pushButton_Volume_clicked() { static bool value=false; value=!value; ui->AV_player->setMute(value); } //音量设置 void Widget::on_horizontalSlider_Volume_valueChanged(int value) { ui->AV_player->setVolume(float(value/10.0)); } //拍照. 保存到本地目录下png void Widget::on_pushButton_snapshot_clicked() { ui->AV_player->snapshot(); } //设置速度 void Widget::on_comboBox_speed_currentTextChanged(const QString &arg1) { qDebug()<<"速度:"<<arg1; ui->AV_player->setPlaybackRate(arg1.toDouble()); }
3.6 widget.h代码
#ifndef WIDGET_H #define WIDGET_H #include <QWidget> #include <QOpenGLWidget> #include <memory> #include "QMDKWidget.h" #include <QFileDialog> #include "QDebug" #include <QThread> #include <QMouseEvent> #include <QStyle> #include <QTimer> namespace mdk { class Player; } QT_BEGIN_NAMESPACE namespace Ui { class Widget; } QT_END_NAMESPACE class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = nullptr); ~Widget(); private slots: void timeout_update(); void on_pushButton_load_clicked(); void on_pushButton_pause_clicked(); void on_pushButton_reset_clicked(); void on_pushButton_play_clicked(); void on_pushButton_stop_clicked(); void on_pushButton_Volume_clicked(); void on_horizontalSlider_Volume_valueChanged(int value); void on_pushButton_snapshot_clicked(); void on_comboBox_speed_currentTextChanged(const QString &arg1); protected: bool eventFilter(QObject *obj, QEvent *event); private: Ui::Widget *ui; QTimer *timer; }; #endif // WIDGET_H
3.7 ui文件