├── .gitignore ├── .qmake.conf ├── LICENSE ├── README.md ├── iodevice.cpp ├── iodevice.h ├── mdkmediaservice.json ├── mdkmediaservice.pro ├── mdkmediaserviceplugin.cpp ├── mdkmediaserviceplugin.h ├── mediaplayercontrol.cpp ├── mediaplayercontrol.h ├── mediaplayerservice.cpp ├── mediaplayerservice.h ├── metadatareadercontrol.cpp ├── metadatareadercontrol.h ├── renderercontrol.cpp ├── renderercontrol.h ├── videowidgetcontrol.cpp └── videowidgetcontrol.h /.gitignore: -------------------------------------------------------------------------------- 1 | mdk-sdk/ 2 | out/ 3 | 4 | # C++ objects and libs 5 | *.slo 6 | *.lo 7 | *.o 8 | *.a 9 | *.la 10 | *.lai 11 | *.so 12 | *.dll 13 | *.dylib 14 | 15 | # Qt-es 16 | object_script.*.Release 17 | object_script.*.Debug 18 | *_plugin_import.cpp 19 | /.qmake.cache 20 | /.qmake.stash 21 | *.pro.user 22 | *.pro.user.* 23 | *.qbs.user 24 | *.qbs.user.* 25 | *.moc 26 | moc_*.cpp 27 | moc_*.h 28 | qrc_*.cpp 29 | ui_*.h 30 | *.qmlc 31 | *.jsc 32 | Makefile* 33 | *build-* 34 | 35 | # Qt unit tests 36 | target_wrapper.* 37 | 38 | # QtCreator 39 | *.autosave 40 | 41 | # QtCreator Qml 42 | *.qmlproject.user 43 | *.qmlproject.user.* 44 | 45 | # QtCreator CMake 46 | CMakeLists.txt.user* 47 | -------------------------------------------------------------------------------- /.qmake.conf: -------------------------------------------------------------------------------- 1 | PLUGIN_TYPE = mediaservice 2 | PLUGIN_CLASS_NAME = MDKPlugin 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 WangBin 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # qtmultimedia-plugins-mdk 2 | qt multimedia plugins implemented on top of [mdk-sdk](https://github.com/wang-bin/mdk-sdk) 3 | 4 | Multimedia plugins are looked up in alphabetical order, so mdk plugin may be not selected. Since Qt5.13 environment var `QT_MULTIMEDIA_PREFERRED_PLUGINS=mdk` can enable mdk as backend. 5 | 6 | ## Features 7 | - All formats. You can replace ffmpeg library in the sdk to support more formats 8 | - GPU decoders (hardcoded because of qtmutimedia limitation, see [QTBUG-74393](https://bugreports.qt.io/browse/QTBUG-74393) 9 | - Optimized OpenGL rendering 10 | - HDR tone mapping 11 | 12 | ## Build 13 | - Download a sdk from https://sourceforge.net/projects/mdk-sdk/files/nightly or https://github.com/wang-bin/mdk-sdk/releases 14 | - Extract sdk into this dir 15 | - Build and install. In QtCreator you can add a **Make** step with ***Make arguments: install***. Then plugin and mdk runtime files will be automatically installed to Qt dir 16 | - Try an Qt multimedia example 17 | 18 | >> Note: mdk-sdk-apple.tar.xz contains xcframework which is not supported yet in this project, use mdk-sdk-macOS.tar.xz or mdk-sdk-iOS.tar.xz instead 19 | 20 | ## Issues 21 | - Qt for android adds abi suffix to shared libraries to support muliti abi, libmdk has no such suffix 22 | -------------------------------------------------------------------------------- /iodevice.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2019 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #include "iodevice.h" 7 | #ifdef MDK_ABI 8 | #include 9 | #include 10 | 11 | void QMediaIO::registerOnce() 12 | { 13 | MediaIO::registerOnce("QIODevice", []{ return new QMediaIO();}); 14 | } 15 | 16 | QMediaIO::QMediaIO(QIODevice* io) 17 | : MediaIO() 18 | , owns_io_(!!io) 19 | , io_(io) 20 | { 21 | } 22 | 23 | bool QMediaIO::onUrlChanged() 24 | { 25 | if (io_) 26 | io_->close(); 27 | if (owns_io_) { 28 | delete io_; 29 | io_ = nullptr; 30 | } 31 | if (url().empty()) 32 | return true; 33 | if (!io_) { 34 | auto s = QString::fromStdString(url()); 35 | static const std::string kScheme = "qio:"; 36 | if (url().substr(0, kScheme.size()) != kScheme) { 37 | io_ = new QFile(s); 38 | } else { 39 | io_ = (QIODevice*)s.mid(4).toULongLong(); 40 | } 41 | } 42 | if (io_->isOpen() && io_->isReadable()) 43 | return true; 44 | if (!io_->open(accessMode() == MediaIO::AccessMode::Write ? QIODevice::WriteOnly : QIODevice::ReadOnly)) { 45 | qWarning() << "Failed to open qiodevice: " << io_->errorString(); 46 | if (owns_io_) { 47 | delete io_; 48 | io_ = nullptr; 49 | } 50 | return false; 51 | } 52 | return true; 53 | } 54 | 55 | bool QMediaIO::seek(int64_t offset, int from) 56 | { 57 | if (!io_) 58 | return false; 59 | int p = offset; 60 | if (from == SEEK_CUR) 61 | p += io_->pos(); 62 | else if (from == SEEK_END) 63 | p += size(); // offset < 0 64 | return io_->seek(p); 65 | } 66 | #endif // MDK_ABI -------------------------------------------------------------------------------- /iodevice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2019 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #pragma once 7 | // qio: qrc: etc 8 | #include "mdk/global.h" 9 | #ifdef MDK_ABI 10 | #include "mdk/MediaIO.h" 11 | #include 12 | using namespace MDK_NS; 13 | 14 | class QMediaIO final : public MediaIO 15 | { 16 | public: 17 | static void registerOnce(); 18 | 19 | QMediaIO(QIODevice* io = nullptr); 20 | 21 | const char* name() const override { return "QIODevice";} 22 | const std::set& protocols() const override { 23 | static const std::set s{"qio", "qrc"}; 24 | return s; 25 | } 26 | 27 | bool isSeekable() const override { return io_ && !io_->isSequential(); } 28 | bool isWritable() const override { return io_ && io_->isWritable(); } 29 | int64_t read(uint8_t *data, int64_t maxSize) override { 30 | if (!io_) 31 | return 0; 32 | return io_->read((char*)data, maxSize); 33 | } 34 | int64_t write(const uint8_t *data, int64_t maxSize) override { 35 | if (!io_) 36 | return 0; 37 | return io_->write((const char*)data, maxSize); 38 | } 39 | 40 | bool seek(int64_t offset, int from) override; 41 | int64_t position() const override { 42 | if (!io_) 43 | return 0; 44 | return io_->pos(); 45 | } 46 | 47 | int64_t size() const override { 48 | if (!io_) 49 | return 0; 50 | return io_->size(); 51 | } 52 | protected: 53 | bool onUrlChanged() override; 54 | private: 55 | bool owns_io_ = true; 56 | QIODevice* io_ = nullptr; 57 | }; 58 | #endif //MDK_ABI 59 | -------------------------------------------------------------------------------- /mdkmediaservice.json: -------------------------------------------------------------------------------- 1 | { 2 | "Keys": ["mdkservice"], 3 | "Services": ["org.qt-project.qt.mediaplayer"] 4 | } 5 | -------------------------------------------------------------------------------- /mdkmediaservice.pro: -------------------------------------------------------------------------------- 1 | QT += multimedia 2 | qtHaveModule(widgets): QT += multimediawidgets 3 | 4 | # rtti is disabled in mdk 5 | CONFIG += rtti_off c++1z c++17 6 | gcc:isEmpty(QMAKE_CXXFLAGS_RTTI_ON): QMAKE_CXXFLAGS += -fno-rtti 7 | 8 | # or use CONFIG+=qt_plugin and add .qmake.config with PLUGIN_TYPE PLUGIN_CLASS_NAME, but error "Could not find feature stack-protector-strong". can be fixed by `load(qt_build_config)` 9 | QTDIR_build { 10 | # This is only for the Qt build. Do not use externally. We mean it. 11 | PLUGIN_TYPE = mediaservice 12 | PLUGIN_CLASS_NAME = MDKPlugin 13 | load(qt_plugin) 14 | } else { 15 | TARGET = $$qtLibraryTarget(mdkmediaservice) 16 | TEMPLATE = lib 17 | load(qt_build_config) # see https://github.com/CrimsonAS/gtkplatform/issues/20#issuecomment-331722270 18 | CONFIG += plugin qt_plugin 19 | target.path = $$[QT_INSTALL_PLUGINS]/mediaservice 20 | INSTALLS += target 21 | } 22 | 23 | SOURCES += \ 24 | iodevice.cpp \ 25 | mediaplayerservice.cpp \ 26 | mediaplayercontrol.cpp \ 27 | metadatareadercontrol.cpp \ 28 | renderercontrol.cpp \ 29 | mdkmediaserviceplugin.cpp 30 | 31 | HEADERS += \ 32 | iodevice.h \ 33 | mediaplayerservice.h \ 34 | mediaplayercontrol.h \ 35 | metadatareadercontrol.h \ 36 | renderercontrol.h \ 37 | mdkmediaserviceplugin.h 38 | 39 | qtHaveModule(widgets) { 40 | SOURCES += videowidgetcontrol.cpp 41 | HEADERS += videowidgetcontrol.h 42 | } 43 | 44 | OTHER_FILES += mdkmediaservice.json 45 | 46 | ######## MDK SDK ########## 47 | MDK_SDK = $$PWD/mdk-sdk 48 | !exists("$$MDK_SDK"): error("Extract mdk-sdk in project dir $$PWD first! Download: https://sourceforge.net/projects/mdk-sdk/files/nightly") 49 | 50 | INCLUDEPATH += $$MDK_SDK/include 51 | contains(QT_ARCH, x.*64) { 52 | android: MDK_ARCH = x86_64 53 | else:linux: MDK_ARCH = amd64 54 | else: MDK_ARCH = x64 55 | } else:contains(QT_ARCH, .*86) { 56 | MDK_ARCH = x86 57 | } else:contains(QT_ARCH, a.*64.*) { 58 | android: MDK_ARCH = arm64-v8a 59 | else: MDK_ARCH = arm64 60 | } else:contains(QT_ARCH, arm.*) { 61 | android: MDK_ARCH = armeabi-v7a 62 | else:linux: MDK_ARCH = armhf 63 | else: MDK_ARCH = arm 64 | } 65 | 66 | mdk_runtime.path = $$[QT_INSTALL_LIBS] 67 | macx|ios { 68 | MDK_ARCH= 69 | QMAKE_CXXFLAGS += -F$$MDK_SDK/lib 70 | LIBS += -F$$MDK_SDK/lib -framework mdk 71 | mdk_runtime.files = $$MDK_SDK/lib/* 72 | } else { 73 | LIBS += -L$$MDK_SDK/lib/$$MDK_ARCH -lmdk 74 | win32: { 75 | LIBS += -L$$MDK_SDK/bin/$$MDK_ARCH # qtcreator will prepend $$LIBS to PATH to run targets 76 | mdk_runtime.files = $$MDK_SDK/bin/$$MDK_ARCH/mdk.* $$MDK_SDK/bin/$$MDK_ARCH/ffmpeg-?.dll 77 | mdk_runtime.path = $$[QT_INSTALL_BINS] 78 | } else { 79 | mdk_runtime.files = $$MDK_SDK/lib/$$MDK_ARCH/libmdk.* $$MDK_SDK/lib/$$MDK_ARCH/libffmpeg.* 80 | } 81 | } 82 | linux: LIBS += -Wl,-rpath-link,$$MDK_SDK/lib/$$MDK_ARCH # for libc++ symbols 83 | INSTALLS += mdk_runtime 84 | 85 | mac { 86 | RPATHDIR *= @executable_path/Frameworks $$[QT_INSTALL_LIBS] /usr/local/lib 87 | QMAKE_LFLAGS_SONAME = -Wl,-install_name,@rpath/ 88 | isEmpty(QMAKE_LFLAGS_RPATH): QMAKE_LFLAGS_RPATH = -Wl,-rpath, 89 | for(R,RPATHDIR) { 90 | QMAKE_LFLAGS *= \'$${QMAKE_LFLAGS_RPATH}$$R\' 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /mdkmediaserviceplugin.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2019 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #include "mdkmediaserviceplugin.h" 7 | #include "mediaplayerservice.h" 8 | 9 | QMediaService* MDKPlugin::create(QString const& key) 10 | { 11 | if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) 12 | return new MediaPlayerService(); 13 | qWarning("MDKPlugin: unsupported key: %s", qPrintable(key)); 14 | return nullptr; 15 | } 16 | 17 | void MDKPlugin::release(QMediaService *service) 18 | { 19 | delete service; 20 | } 21 | 22 | QMediaServiceProviderHint::Features MDKPlugin::supportedFeatures(const QByteArray &service) const 23 | { 24 | if (service == Q_MEDIASERVICE_MEDIAPLAYER) 25 | return QMediaServiceProviderHint::VideoSurface; //QMediaServiceProviderHint::StreamPlayback 26 | return QMediaServiceProviderHint::Features(); 27 | } 28 | -------------------------------------------------------------------------------- /mdkmediaserviceplugin.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #pragma once 7 | #include 8 | // TODO: formats 9 | class MDKPlugin : public QMediaServiceProviderPlugin 10 | // , public QMediaServiceSupportedFormatsInterface 11 | , 12 | public QMediaServiceFeaturesInterface { 13 | Q_OBJECT 14 | // Q_INTERFACES(QMediaServiceSupportedFormatsInterface) 15 | Q_INTERFACES(QMediaServiceFeaturesInterface) 16 | Q_PLUGIN_METADATA(IID "org.qt-project.qt.mediaserviceproviderfactory/5.0" FILE 17 | "mdkmediaservice.json") 18 | public: 19 | QMediaService *create(QString const &key) Q_DECL_OVERRIDE; 20 | void release(QMediaService *service) Q_DECL_OVERRIDE; 21 | 22 | QMediaServiceProviderHint::Features 23 | supportedFeatures(const QByteArray &service) const Q_DECL_OVERRIDE; 24 | }; 25 | -------------------------------------------------------------------------------- /mediaplayercontrol.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2020 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #include "mediaplayercontrol.h" 7 | #include "mdk/MediaInfo.h" 8 | 9 | static QMediaPlayer::State toQt(State value) { 10 | switch (value) { 11 | case State::Playing: return QMediaPlayer::PlayingState; 12 | case State::Paused: return QMediaPlayer::PausedState; 13 | case State::Stopped: return QMediaPlayer::StoppedState; 14 | } 15 | return QMediaPlayer::StoppedState; 16 | } 17 | 18 | // TODO: mask value&xxx 19 | static QMediaPlayer::MediaStatus toQt(MediaStatus value) { 20 | switch (value) { 21 | case MediaStatus::NoMedia: return QMediaPlayer::NoMedia; 22 | case MediaStatus::Invalid: return QMediaPlayer::InvalidMedia; 23 | default: break; 24 | } 25 | if (test_flag(value & MediaStatus::Loading)) 26 | return QMediaPlayer::LoadingMedia; 27 | if (test_flag(value & MediaStatus::Stalled)) 28 | return QMediaPlayer::StalledMedia; 29 | if (test_flag(value & MediaStatus::Buffering)) 30 | return QMediaPlayer::BufferingMedia; 31 | if (test_flag(value & MediaStatus::Buffered)) 32 | return QMediaPlayer::BufferedMedia; // playing or paused 33 | if (test_flag(value & MediaStatus::End)) 34 | return QMediaPlayer::EndOfMedia; // playback complete 35 | if (test_flag(value & MediaStatus::Loaded)) 36 | return QMediaPlayer::LoadedMedia; // connected, stopped. so last 37 | return QMediaPlayer::UnknownMediaStatus; 38 | } 39 | 40 | MediaPlayerControl::MediaPlayerControl(QObject* parent) : QMediaPlayerControl(parent) 41 | { 42 | player_.setVideoDecoders({ 43 | #if defined(Q_OS_WIN) 44 | "MFT:d3d=11", "MFT:d3d=9", "D3D11", 45 | #elif defined(Q_OS_DARWIN) 46 | "VT", "VideoToolbox", 47 | #elif defined(Q_OS_ANDROID) 48 | "AMediaCodec:java=0:async=1", 49 | #elif defined(Q_OS_LINUX) 50 | "VAAPI", "VDPAU", 51 | #endif 52 | "CUDA", "FFmpeg"}); // no display for 2nd video 53 | player_.onStateChanged([this](State value){ 54 | Q_EMIT stateChanged(toQt(value)); 55 | }); 56 | player_.onMediaStatusChanged([this](MediaStatus value){ 57 | Q_EMIT mediaStatusChanged(toQt(value)); 58 | return true; 59 | }); 60 | player_.onEvent([this](const MediaEvent& e){ 61 | if (e.error < 0) { 62 | if (e.category == "decoder.audio" 63 | || e.category == "decoder.video") { 64 | Q_EMIT error(QMediaPlayer::FormatError, tr("Unsupported media, a codec is missing.")); 65 | } 66 | } 67 | return false; 68 | }); 69 | player_.setRenderCallback([this](void*){ 70 | Q_EMIT frameAvailable(); 71 | }); 72 | } 73 | 74 | QMediaPlayer::State MediaPlayerControl::state() const 75 | { 76 | return toQt(player_.state()); 77 | } 78 | 79 | QMediaPlayer::MediaStatus MediaPlayerControl::mediaStatus() const 80 | { 81 | return toQt(player_.mediaStatus()); 82 | } 83 | 84 | qint64 MediaPlayerControl::duration() const 85 | { 86 | return duration_; 87 | } 88 | 89 | qint64 MediaPlayerControl::position() const 90 | { 91 | return player_.position(); 92 | } 93 | 94 | void MediaPlayerControl::setPosition(qint64 position) 95 | { 96 | player_.seek(position); 97 | } 98 | 99 | int MediaPlayerControl::volume() const 100 | { 101 | return vol_; 102 | } 103 | 104 | void MediaPlayerControl::setVolume(int volume) 105 | { 106 | vol_ = volume; 107 | player_.setVolume(float(volume)/100.0f); 108 | } 109 | 110 | bool MediaPlayerControl::isMuted() const 111 | { 112 | return mute_; 113 | } 114 | 115 | void MediaPlayerControl::setMuted(bool muted) 116 | { 117 | mute_ = muted; 118 | player_.setMute(muted); 119 | } 120 | 121 | int MediaPlayerControl::bufferStatus() const 122 | { 123 | return 0; 124 | } 125 | 126 | bool MediaPlayerControl::isAudioAvailable() const 127 | { 128 | return has_a_; 129 | } 130 | 131 | bool MediaPlayerControl::isVideoAvailable() const 132 | { 133 | return has_v_; 134 | } 135 | 136 | bool MediaPlayerControl::isSeekable() const 137 | { 138 | return true; 139 | } 140 | 141 | QMediaTimeRange MediaPlayerControl::availablePlaybackRanges() const 142 | { 143 | return QMediaTimeRange(); 144 | } 145 | 146 | qreal MediaPlayerControl::playbackRate() const 147 | { 148 | return qreal(player_.playbackRate()); 149 | } 150 | 151 | void MediaPlayerControl::setPlaybackRate(qreal rate) 152 | { 153 | const auto old = playbackRate(); 154 | if (playbackRate() == rate) 155 | return; 156 | player_.setPlaybackRate(float(rate)); 157 | if (playbackRate() != old) // success 158 | Q_EMIT playbackRateChanged(playbackRate()); 159 | } 160 | 161 | QMediaContent MediaPlayerControl::media() const 162 | { 163 | return QMediaContent(); 164 | } 165 | 166 | const QIODevice* MediaPlayerControl::mediaStream() const 167 | { 168 | return nullptr; 169 | } 170 | 171 | void MediaPlayerControl::setMedia(const QMediaContent& media, QIODevice* io) 172 | { 173 | stop(); 174 | if (io) { 175 | player_.setMedia(QString("qio:%1").arg(qintptr(io)).toUtf8().constData()); 176 | } else { 177 | if (media.canonicalUrl().isLocalFile()) 178 | player_.setMedia(media.canonicalUrl().toLocalFile().toUtf8().constData()); // for windows 179 | else 180 | player_.setMedia(media.canonicalUrl().url().toUtf8().constData()); 181 | } 182 | 183 | Q_EMIT positionChanged(0); 184 | player_.waitFor(State::Stopped); 185 | player_.prepare(0, [this](int64_t position, bool*){ 186 | if (position < 0) { 187 | Q_EMIT error(QMediaPlayer::ResourceError, tr("Failed to load source.")); 188 | } 189 | const auto& info = player_.mediaInfo(); 190 | duration_ = info.duration; 191 | has_a_ = !info.audio.empty(); 192 | has_v_ = !info.video.empty(); 193 | Q_EMIT durationChanged(duration_); 194 | Q_EMIT audioAvailableChanged(has_a_); 195 | Q_EMIT videoAvailableChanged(has_v_); 196 | Q_EMIT seekableChanged(position >= 0); 197 | #if defined(MDK_VERSION_CHECK) 198 | # if MDK_VERSION_CHECK(0, 5, 0) 199 | return true; 200 | # endif 201 | #endif 202 | }); 203 | } 204 | 205 | void MediaPlayerControl::play() 206 | { 207 | player_.setState(State::Playing); 208 | } 209 | 210 | void MediaPlayerControl::pause() 211 | { 212 | player_.setState(State::Paused); 213 | } 214 | 215 | void MediaPlayerControl::stop() 216 | { 217 | player_.setState(State::Stopped); 218 | } 219 | -------------------------------------------------------------------------------- /mediaplayercontrol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #pragma once 7 | #include 8 | #include "mdk/Player.h" 9 | using namespace MDK_NS; 10 | 11 | class MediaPlayerControl : public QMediaPlayerControl { 12 | Q_OBJECT 13 | public: 14 | explicit MediaPlayerControl(QObject* parent = nullptr); 15 | 16 | Player* player() { return &player_; } 17 | 18 | QMediaPlayer::State state() const override; 19 | QMediaPlayer::MediaStatus mediaStatus() const override; 20 | qint64 duration() const override; 21 | qint64 position() const override; 22 | void setPosition(qint64 position) override; 23 | int volume() const override; 24 | void setVolume(int volume) override; 25 | bool isMuted() const override; 26 | void setMuted(bool muted) override; 27 | int bufferStatus() const override; 28 | bool isAudioAvailable() const override; 29 | bool isVideoAvailable() const override; 30 | bool isSeekable() const override; 31 | QMediaTimeRange availablePlaybackRanges() const override; 32 | qreal playbackRate() const override; 33 | void setPlaybackRate(qreal rate) override; 34 | QMediaContent media() const override; 35 | const QIODevice *mediaStream() const override; 36 | void setMedia(const QMediaContent &media, QIODevice *stream) override; 37 | void play() override; 38 | void pause() override; 39 | void stop() override; 40 | Q_SIGNALS: 41 | void frameAvailable(); 42 | private: 43 | bool has_a_ = true; 44 | bool has_v_ = true; 45 | bool mute_ = false; 46 | int vol_ = 100; 47 | int64_t duration_ = 0; 48 | Player player_; 49 | }; 50 | -------------------------------------------------------------------------------- /mediaplayerservice.cpp: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2018-2019 Wang Bin - wbsecg1 at gmail.com 4 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 5 | * MIT License 6 | */ 7 | #include "iodevice.h" 8 | #include "mediaplayerservice.h" 9 | #include "mediaplayercontrol.h" 10 | #include "metadatareadercontrol.h" 11 | #include "renderercontrol.h" 12 | #ifdef QT_MULTIMEDIAWIDGETS_LIB 13 | #include "videowidgetcontrol.h" 14 | #endif //QT_MULTIMEDIAWIDGETS_LIB 15 | 16 | MediaPlayerService::MediaPlayerService(QObject* parent) 17 | : QMediaService(parent) 18 | , mpc_(new MediaPlayerControl(parent)) 19 | { 20 | #ifdef MDK_ABI 21 | QMediaIO::registerOnce(); 22 | #endif // MDK_ABI 23 | qInfo("create service..."); 24 | } 25 | 26 | QMediaControl* MediaPlayerService::requestControl(const char* name) 27 | { 28 | qInfo("requestControl %s", name); 29 | if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) // requested when constructing QMediaObject(QMediaPlayer) 30 | return new MetaDataReaderControl(mpc_); 31 | if (qstrcmp(name, QMediaPlayerControl_iid) == 0) 32 | return mpc_; 33 | if (qstrcmp(name, QVideoRendererControl_iid) == 0) 34 | return new RendererControl(mpc_, this); 35 | #ifdef QT_MULTIMEDIAWIDGETS_LIB 36 | if (qstrcmp(name, QVideoWidgetControl_iid) == 0) 37 | return new VideoWidgetControl(mpc_, this); 38 | #endif //QT_MULTIMEDIAWIDGETS_LIB 39 | qWarning("MediaPlayerService: unsupported control: %s", qPrintable(name)); 40 | return nullptr; 41 | } 42 | 43 | void MediaPlayerService::releaseControl(QMediaControl* control) 44 | { 45 | qInfo() << "release control " << control; 46 | delete control; 47 | if (control == mpc_) 48 | mpc_ = nullptr; 49 | } 50 | -------------------------------------------------------------------------------- /mediaplayerservice.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #pragma once 7 | #include 8 | 9 | class MediaPlayerControl; 10 | class VideoWindowControl; 11 | 12 | class MediaPlayerService : public QMediaService { 13 | Q_OBJECT 14 | public: 15 | explicit MediaPlayerService(QObject* parent = nullptr); 16 | QMediaControl* requestControl(const char* name) override; 17 | void releaseControl(QMediaControl* control) override; 18 | private: 19 | MediaPlayerControl* mpc_ = nullptr; 20 | }; 21 | -------------------------------------------------------------------------------- /metadatareadercontrol.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2019 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #include "metadatareadercontrol.h" 7 | #include "mediaplayercontrol.h" 8 | #include "mdk/MediaInfo.h" 9 | #include 10 | #include 11 | 12 | MetaDataReaderControl::MetaDataReaderControl(MediaPlayerControl* mpc, QObject *parent) 13 | : QMetaDataReaderControl(parent) 14 | , mpc_(mpc) 15 | { 16 | connect(mpc, &QMediaPlayerControl::durationChanged, this, &MetaDataReaderControl::readMetaData, Qt::DirectConnection); 17 | } 18 | 19 | void MetaDataReaderControl::readMetaData() 20 | { 21 | using namespace QMediaMetaData; 22 | const auto& info = mpc_->player()->mediaInfo(); 23 | QVariantMap m; 24 | m[Size] = (qint64)info.size; 25 | m[Duration] = (qint64)info.duration; 26 | m[QMediaMetaData::MediaType] = info.video.empty() ? "audio" : "video"; // FIXME: album cover image can be a video stream 27 | 28 | if (!info.metadata.empty()) { // TODO: metadata update 29 | struct { 30 | const char* key; 31 | QString qkey; 32 | } key_map[] = { 33 | {"title", Title}, 34 | //{"Sub_Title", SubTitle}, 35 | {"author", Author}, 36 | {"comment", Comment}, 37 | {"description", Description},// 38 | //{"category", Category}, // stringlist 39 | {"genre", Genre}, // stringlist 40 | {"year", Year}, // int 41 | {"date", Date}, // ISO 8601=>QDate 42 | //{"UserRating", UserRating}, 43 | //{"Keywords", Keywords}, 44 | {"language", Language}, 45 | {"publisher", Publisher}, 46 | {"copyright", Copyright}, 47 | //{"ParentalRating", ParentalRating}, 48 | //{"RatingOrganization", RatingOrganization}, 49 | 50 | // movies 51 | {"performer", LeadPerformer}, 52 | 53 | {"album", AlbumTitle}, 54 | {"album_artist", AlbumArtist}, 55 | //{"ContributingArtist", ContributingArtist}, 56 | {"composer", Composer}, 57 | //{"Conductor", Conductor}, 58 | //{"Lyrics", Lyrics}, // AV_DISPOSITION_LYRICS 59 | //{"mood", Mood}, 60 | {"track", TrackNumber}, 61 | //{"CoverArtUrlSmall", CoverArtUrlSmall}, 62 | //{"CoverArtUrlLarge", CoverArtUrlLarge}, 63 | //{"AV_DISPOSITION_ATTACHED_PIC", CoverArtImage}, // qimage 64 | {"track", TrackNumber}, 65 | }; 66 | for (const auto& k : key_map) { 67 | const auto i = info.metadata.find(k.key); 68 | if (i != info.metadata.cend()) 69 | m[k.qkey] = i->second.data(); 70 | } 71 | } 72 | // AV_DISPOSITION_TIMED_THUMBNAILS => ThumbnailImage. qimage 73 | if (!info.audio.empty()) { 74 | const auto& p = info.audio[0].codec; 75 | m[AudioBitRate] = (int)p.bit_rate; 76 | m[AudioCodec] = QString::fromStdString(p.codec); 77 | //m[AverageLevel] 78 | m[ChannelCount] = p.channels; 79 | m[SampleRate] = p.sample_rate; 80 | } 81 | 82 | if (!info.video.empty()) { 83 | const auto& p = info.video[0].codec; 84 | m[VideoFrameRate] = qreal(p.frame_rate); 85 | m[VideoBitRate] = (int)p.bit_rate; 86 | m[VideoCodec] = QString::fromStdString(p.codec); 87 | m[Resolution] = QSize(p.width, p.height); 88 | // PixelAspectRatio 89 | } 90 | 91 | bool avail_change = tag_.empty() != m.empty(); 92 | tag_ = m; 93 | if (avail_change) 94 | Q_EMIT metaDataAvailableChanged(!tag_.isEmpty()); 95 | Q_EMIT metaDataChanged(); 96 | } 97 | -------------------------------------------------------------------------------- /metadatareadercontrol.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | class MediaPlayerControl; 5 | class MetaDataReaderControl final: public QMetaDataReaderControl 6 | { 7 | public: 8 | MetaDataReaderControl(MediaPlayerControl* mpc, QObject *parent = nullptr); 9 | 10 | bool isMetaDataAvailable() const override {return !tag_.isEmpty();} 11 | QVariant metaData(const QString &key) const override {return tag_.value(key);} 12 | QStringList availableMetaData() const override {return tag_.keys();} 13 | private: 14 | void readMetaData(); 15 | private: 16 | MediaPlayerControl* mpc_ = nullptr; 17 | QVariantMap tag_; 18 | }; -------------------------------------------------------------------------------- /renderercontrol.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2019 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | // GLTextureVideoBuffer render to fbo texture, texture as frame 7 | // map to host: store tls ui context in map(GLTextureArray), create tls context shared with ui ctx, download 8 | // move to mdk public NativeVideoBuffer::fromTexture() 9 | #include "renderercontrol.h" 10 | #include "mediaplayercontrol.h" 11 | #include "mdk/MediaInfo.h" 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #ifdef MDK_ABI 22 | # include "mdk/VideoFrame.h" 23 | static QVideoFrame::PixelFormat toQt(PixelFormat fmt) { 24 | switch (fmt) { 25 | case PixelFormat::YUV420P: return QVideoFrame::Format_YUV420P; 26 | case PixelFormat::UYVY422: return QVideoFrame::Format_UYVY; 27 | case PixelFormat::NV12: return QVideoFrame::Format_NV12; 28 | case PixelFormat::YV12: return QVideoFrame::Format_YV12; 29 | case PixelFormat::BGRA: return QVideoFrame::Format_BGRA32; 30 | case PixelFormat::BGRX: return QVideoFrame::Format_BGR32; 31 | default: return QVideoFrame::Format_Invalid; 32 | } 33 | } 34 | 35 | class HostVideoBuffer final: public QAbstractPlanarVideoBuffer 36 | { 37 | public: 38 | HostVideoBuffer(const VideoFrame& frame) 39 | : QAbstractPlanarVideoBuffer(NoHandle) 40 | , frame_(frame) 41 | { 42 | } 43 | 44 | MapMode mapMode() const override { return mode_; } 45 | 46 | int map(MapMode mode, int *numBytes, int bytesPerLine[4], uchar *data[4]) override { 47 | const int planes = frame_.format().planeCount(); 48 | if (mode_ == mode) 49 | return planes; 50 | mode_ = mode; 51 | int bytes = 0; 52 | for (int i = 0; i < planes; ++i) { 53 | auto b = frame_.buffer(i); 54 | bytes += b->size(); 55 | bytesPerLine[i] = b->stride(); 56 | if (mode & WriteOnly) { 57 | data[i] = b->data(); 58 | } else { 59 | data[i] = (uchar*)b->constData(); 60 | } 61 | } 62 | if (numBytes) 63 | *numBytes = bytes; 64 | return planes; 65 | } 66 | 67 | void unmap() override { 68 | mode_ = NotMapped; 69 | } 70 | private: 71 | MapMode mode_ = NotMapped; 72 | VideoFrame frame_; 73 | }; 74 | #endif // MDK_ABI 75 | 76 | // qtmultimedia only support packed rgb gltexture handle, so offscreen rendering may be required 77 | class FBOVideoBuffer final : public QAbstractVideoBuffer { 78 | public: 79 | FBOVideoBuffer(Player* player, QOpenGLFramebufferObject** fbo, int width, int height) : QAbstractVideoBuffer(GLTextureHandle) 80 | , w_(width), h_(height) 81 | , player_(player) 82 | , fbo_(fbo) 83 | {} 84 | 85 | MapMode mapMode() const override { return mode_; } 86 | 87 | uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) override { 88 | if (mode_ == mode) 89 | return img_.bits(); 90 | if (mode & WriteOnly) 91 | return nullptr; 92 | mode_ = mode; 93 | renderToFbo(); 94 | img_ = (*fbo_)->toImage(false); 95 | if (numBytes) 96 | *numBytes = img_.byteCount(); 97 | if (bytesPerLine) 98 | *bytesPerLine = img_.bytesPerLine(); 99 | return img_.bits(); 100 | } 101 | 102 | void unmap() override { 103 | mode_ = NotMapped; 104 | } 105 | 106 | // current context is not null! 107 | QVariant handle() const override { 108 | auto that = const_cast(this); 109 | that->renderToFbo(); 110 | return (*fbo_)->texture(); 111 | } 112 | 113 | private: 114 | void renderToFbo() { 115 | auto f = QOpenGLContext::currentContext()->functions(); 116 | GLint prevFbo = 0; 117 | f->glGetIntegerv(GL_FRAMEBUFFER_BINDING, &prevFbo); 118 | auto fbo = *fbo_; 119 | if (!fbo || fbo->size() != QSize(w_, h_)) { 120 | player_->scale(1.0f, -1.0f); // flip y in fbo 121 | player_->setVideoSurfaceSize(w_, h_); 122 | delete fbo; 123 | fbo = new QOpenGLFramebufferObject(w_, h_); 124 | *fbo_ = fbo; 125 | } 126 | fbo->bind(); 127 | player_->renderVideo(); 128 | f->glBindFramebuffer(GL_FRAMEBUFFER, prevFbo); 129 | } 130 | private: 131 | MapMode mode_ = NotMapped; 132 | int w_; 133 | int h_; 134 | Player* player_; 135 | QOpenGLFramebufferObject** fbo_ = nullptr; 136 | QImage img_; 137 | }; 138 | 139 | RendererControl::RendererControl(MediaPlayerControl* player, QObject* parent) : QVideoRendererControl(parent) 140 | , mpc_(player) 141 | { 142 | mpc_ = player; 143 | connect(mpc_, &MediaPlayerControl::frameAvailable, this, &RendererControl::onFrameAvailable); 144 | } 145 | 146 | QAbstractVideoSurface* RendererControl::surface() const 147 | { 148 | return surface_; 149 | } 150 | 151 | void RendererControl::setSurface(QAbstractVideoSurface* surface) 152 | { 153 | if (surface_ && surface_->isActive()) 154 | surface_->stop(); 155 | surface_ = surface; 156 | if (!surface) { 157 | mpc_->player()->setRenderCallback(nullptr); // surfcace is set to null before destroy, avoid invokeMethod() on invalid this 158 | return; 159 | } 160 | //const QSize r = surface->nativeResolution(); // may be (-1, -1) 161 | // mdk player needs a vo. add before delivering a video frame 162 | mpc_->player()->setVideoSurfaceSize(1,1);//r.width(), r.height()); 163 | 164 | if (!mpc_->player()->mediaInfo().video.empty()) { 165 | auto& c = mpc_->player()->mediaInfo().video[0].codec; 166 | video_w_ = c.width; 167 | video_h_ = c.height; 168 | } 169 | 170 | mpc_->player()->onMediaStatusChanged([this](MediaStatus s){ 171 | if (flags_added(status_, s, MediaStatus::Loaded)) { 172 | if (!mpc_->player()->mediaInfo().video.empty()) { 173 | auto& c = mpc_->player()->mediaInfo().video[0].codec; 174 | video_w_ = c.width; 175 | video_h_ = c.height; 176 | } 177 | } 178 | status_ = s; 179 | return true; 180 | }); 181 | } 182 | 183 | void RendererControl::onFrameAvailable() 184 | { 185 | if (!surface_) 186 | return; 187 | #ifdef MDK_ABI 188 | mpc_->player()->renderVideo(); // required to get frame 189 | VideoFrame v; 190 | mpc_->player()->getVideoFrame(&v); 191 | if (!v) 192 | return; 193 | const auto& qfmt = toQt(v.format().format()); 194 | QVideoFrame frame; 195 | if (v.nativeBuffer() || !surface_->isFormatSupported(QVideoSurfaceFormat(QSize(v.width(), v.height()), qfmt))) { 196 | frame = QVideoFrame(new FBOVideoBuffer(mpc_->player(), &fbo_, v.width(), v.height()), QSize(v.width(), v.height()), QVideoFrame::Format_BGR32); // RGB32 for qimage 197 | } else { 198 | frame = QVideoFrame(new HostVideoBuffer(v), QSize(v.width(), v.height()), qfmt); 199 | } 200 | #else 201 | if (video_w_ <= 0 || video_h_ <= 0) 202 | return; // not playing, e.g. when stop() is called there is also a frameAvailable signal to update renderer which is required by mdk internally. if create fbo with an invalid size anyway, qt gl rendering will be broken forever 203 | QVideoFrame frame(new FBOVideoBuffer(mpc_->player(), &fbo_, video_w_, video_h_), QSize(video_w_, video_h_), QVideoFrame::Format_BGR32); // RGB32 for qimage 204 | #endif // MDK_ABI 205 | if (!surface_->isActive()) { // || surfaceFormat()!= 206 | QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), frame.handleType()); 207 | surface_->start(format); 208 | } 209 | surface_->present(frame); // main thread 210 | } 211 | -------------------------------------------------------------------------------- /renderercontrol.h: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * Copyright (C) 2018-2019 Wang Bin - wbsecg1 at gmail.com 4 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 5 | * MIT License 6 | */ 7 | #pragma once 8 | #include "mdk/global.h" 9 | #include 10 | using namespace MDK_NS; 11 | 12 | class MediaPlayerControl; 13 | class QOpenGLFramebufferObject; 14 | class RendererControl : public QVideoRendererControl 15 | { 16 | Q_OBJECT 17 | public: 18 | RendererControl(MediaPlayerControl* player, QObject *parent = nullptr); 19 | QAbstractVideoSurface *surface() const override; 20 | void setSurface(QAbstractVideoSurface *surface) override; 21 | 22 | void setSource(); 23 | public Q_SLOTS: 24 | void onFrameAvailable(); 25 | private: 26 | QAbstractVideoSurface* surface_ = nullptr; 27 | MediaPlayerControl* mpc_ = nullptr; 28 | QOpenGLFramebufferObject *fbo_ = nullptr; 29 | 30 | // video_w/h_ is from MediaInfo, which may be incorrect. A better value is from VideoFrame but it's not a public class now. 31 | int video_w_ = 0; 32 | int video_h_ = 0; 33 | MediaStatus status_; 34 | }; -------------------------------------------------------------------------------- /videowidgetcontrol.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2022 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #include "videowidgetcontrol.h" 7 | #include "mediaplayercontrol.h" 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | class VideoWidget : public QOpenGLWidget, protected QOpenGLFunctions 14 | { 15 | public: 16 | VideoWidget(QWidget *parent = nullptr) : QOpenGLWidget(parent) {} 17 | 18 | void setSource(Player* player) { 19 | player_ = player; 20 | } 21 | 22 | protected: 23 | void initializeGL() override { 24 | initializeOpenGLFunctions(); 25 | connect(context(), &QOpenGLContext::aboutToBeDestroyed, [this]{ 26 | makeCurrent(); 27 | Player::foreignGLContextDestroyed(); 28 | doneCurrent(); 29 | }); 30 | } 31 | 32 | void resizeGL(int w, int h) override { 33 | if (!player_) 34 | return; 35 | player_->setVideoSurfaceSize(w * devicePixelRatio(), h * devicePixelRatio(), this); 36 | } 37 | 38 | void paintGL() override { 39 | if (!player_) 40 | return; 41 | glClearColor(0.0f, 0.0f, 0.0f, 1.0f); 42 | glClear(GL_COLOR_BUFFER_BIT); 43 | player_->renderVideo(this); 44 | } 45 | private: 46 | Player* player_ = nullptr; 47 | }; 48 | 49 | VideoWidgetControl::VideoWidgetControl(MediaPlayerControl* player, QObject* parent) 50 | : QVideoWidgetControl(parent) 51 | , vw_(new VideoWidget()) 52 | , mpc_(player) 53 | { 54 | vw_->setSource(mpc_->player()); 55 | //connect(mpc_, &MediaPlayerControl::frameAvailable, vw_, &VideoWidget::update, Qt::QueuedConnection); 56 | connect(mpc_, SIGNAL(frameAvailable()), vw_, SLOT(update()), Qt::QueuedConnection); 57 | } 58 | 59 | VideoWidgetControl::~VideoWidgetControl() 60 | { 61 | delete vw_; 62 | } 63 | 64 | bool VideoWidgetControl::isFullScreen() const 65 | { 66 | return fs_; 67 | } 68 | 69 | void VideoWidgetControl::setFullScreen(bool fullScreen) 70 | { 71 | fs_ = fullScreen; 72 | } 73 | 74 | Qt::AspectRatioMode VideoWidgetControl::aspectRatioMode() const 75 | { 76 | return am_; 77 | } 78 | 79 | static float fromQt(Qt::AspectRatioMode value) 80 | { 81 | switch (value) { 82 | case Qt::IgnoreAspectRatio: return IgnoreAspectRatio; 83 | case Qt::KeepAspectRatioByExpanding: return KeepAspectRatioCrop; 84 | case Qt::KeepAspectRatio: return KeepAspectRatio; 85 | default: return KeepAspectRatio; 86 | } 87 | } 88 | 89 | void VideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode mode) 90 | { 91 | am_ = mode; 92 | // mpc_ is set internally & never null 93 | mpc_->player()->setAspectRatio(fromQt(mode), vw_); 94 | } 95 | 96 | void VideoWidgetControl::setBrightness(int brightness) 97 | { 98 | brightness_ = brightness; 99 | float v = float(brightness)/100.0f; 100 | mpc_->player()->set(VideoEffect::Brightness, v, vw_); 101 | } 102 | 103 | void VideoWidgetControl::setContrast(int contrast) 104 | { 105 | contrast_ = contrast; 106 | float v = float(contrast)/100.0f; 107 | mpc_->player()->set(VideoEffect::Contrast, v, vw_); 108 | } 109 | 110 | void VideoWidgetControl::setHue(int hue) 111 | { 112 | hue_ = hue; 113 | float v = float(hue)/100.0f; 114 | mpc_->player()->set(VideoEffect::Hue, v, vw_); 115 | } 116 | 117 | void VideoWidgetControl::setSaturation(int saturation) 118 | { 119 | saturation_ = saturation; 120 | float v = float(saturation)/100.0f; 121 | mpc_->player()->set(VideoEffect::Saturation, v, vw_); 122 | } 123 | 124 | QWidget* VideoWidgetControl::videoWidget() 125 | { 126 | return vw_; 127 | } 128 | -------------------------------------------------------------------------------- /videowidgetcontrol.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2018-2020 Wang Bin - wbsecg1 at gmail.com 3 | * https://github.com/wang-bin/qtmultimedia-plugins-mdk 4 | * MIT License 5 | */ 6 | #pragma once 7 | #include 8 | 9 | class MediaPlayerControl; 10 | class VideoWidget; 11 | class VideoWidgetControl : public QVideoWidgetControl { 12 | Q_OBJECT 13 | public: 14 | explicit VideoWidgetControl(MediaPlayerControl* player, QObject* parent = nullptr); 15 | ~VideoWidgetControl() override; 16 | 17 | bool isFullScreen() const override; 18 | void setFullScreen(bool fullScreen) override; 19 | Qt::AspectRatioMode aspectRatioMode() const override; 20 | void setAspectRatioMode(Qt::AspectRatioMode mode) override; 21 | int brightness() const override { return brightness_; } 22 | void setBrightness(int brightness) override; 23 | int contrast() const override { return contrast_; } 24 | void setContrast(int contrast) override; 25 | int hue() const override { return hue_; } 26 | void setHue(int hue) override; 27 | int saturation() const override { return saturation_; } 28 | void setSaturation(int saturation) override; 29 | QWidget* videoWidget() override; 30 | private: 31 | bool fs_ = false; 32 | Qt::AspectRatioMode am_ = Qt::KeepAspectRatio; 33 | VideoWidget* vw_ = nullptr; 34 | MediaPlayerControl* mpc_ = nullptr; 35 | int brightness_ = 0; 36 | int contrast_ = 0; 37 | int hue_ = 0; 38 | int saturation_ = 0; 39 | }; 40 | --------------------------------------------------------------------------------