From 0419676af2090df03d863fae84b19fa5a3db8a29 Mon Sep 17 00:00:00 2001 From: Elias Steurer Date: Sat, 23 Oct 2021 10:35:15 +0200 Subject: [PATCH] Add MultimediaWebView This is for now macOS only because macOS MM only can play h264, so we still need the webview to render webm --- .../inc/public/ScreenPlayUtil/contenttypes.h | 23 +++ .../inc/public/ScreenPlayUtil/util.h | 1 + ScreenPlayUtil/src/util.cpp | 28 ++++ ScreenPlayWallpaper/SPWResources.qrc | 1 + ScreenPlayWallpaper/qml/MultimediaView.qml | 7 - ScreenPlayWallpaper/qml/MultimediaWebView.qml | 157 ++++++++++++++++++ ScreenPlayWallpaper/qml/Wallpaper.qml | 21 ++- ScreenPlayWallpaper/src/basewindow.cpp | 44 +++++ ScreenPlayWallpaper/src/basewindow.h | 7 + 9 files changed, 279 insertions(+), 10 deletions(-) create mode 100644 ScreenPlayWallpaper/qml/MultimediaWebView.qml diff --git a/ScreenPlayUtil/inc/public/ScreenPlayUtil/contenttypes.h b/ScreenPlayUtil/inc/public/ScreenPlayUtil/contenttypes.h index 393ae69d..d8767025 100644 --- a/ScreenPlayUtil/inc/public/ScreenPlayUtil/contenttypes.h +++ b/ScreenPlayUtil/inc/public/ScreenPlayUtil/contenttypes.h @@ -102,4 +102,27 @@ namespace InstalledType { Q_ENUM_NS(InstalledType) } + +/*! + \namespace ScreenPlay::InstalledType + \inmodule ScreenPlayUtil + \brief When changing the enum, one also needs to change: + GlobalVariables::getAvailableWallpaper + GlobalVariables::getAvailableWidgets + Common/Util.js isWallpaper() and isWidget() + ScreenPlayWallpaper: BaseWindow::parseWallpaperType() +*/ +namespace VideoCodec { + Q_NAMESPACE + + enum class VideoCodec { + Unknown, + VP8, + VP9, + AV1, + H264, + H265 + }; + Q_ENUM_NS(VideoCodec) +} } diff --git a/ScreenPlayUtil/inc/public/ScreenPlayUtil/util.h b/ScreenPlayUtil/inc/public/ScreenPlayUtil/util.h index bf9acc11..11396b85 100644 --- a/ScreenPlayUtil/inc/public/ScreenPlayUtil/util.h +++ b/ScreenPlayUtil/inc/public/ScreenPlayUtil/util.h @@ -44,6 +44,7 @@ namespace ScreenPlayUtil { QJsonArray fillArray(const QVector& items); ScreenPlay::SearchType::SearchType getSearchTypeFromInstalledType(const ScreenPlay::InstalledType::InstalledType type); std::optional getInstalledTypeFromString(const QString& type); +std::optional getVideoCodecFromString(const QString& type); std::optional parseQByteArrayToQJsonObject(const QByteArray& byteArray); std::optional openJsonFileToObject(const QString& path); std::optional openJsonFileToString(const QString& path); diff --git a/ScreenPlayUtil/src/util.cpp b/ScreenPlayUtil/src/util.cpp index bd56101c..00119aba 100644 --- a/ScreenPlayUtil/src/util.cpp +++ b/ScreenPlayUtil/src/util.cpp @@ -266,6 +266,33 @@ std::optional getInstalledTypeFromStri return std::nullopt; } + +/*! + \brief Maps the video codec type from a QString to an enum. Used for parsing the project.json. +*/ +std::optional getVideoCodecFromString(const QString &type) +{ + if(type.isEmpty()) + return std::nullopt; + + if(type.contains("vp8",Qt::CaseInsensitive)) + return ScreenPlay::VideoCodec::VideoCodec::VP8; + + if(type.contains("vp9",Qt::CaseInsensitive)) + return ScreenPlay::VideoCodec::VideoCodec::VP9; + + if(type.contains("av1",Qt::CaseInsensitive)) + return ScreenPlay::VideoCodec::VideoCodec::AV1; + + if(type.contains("h264",Qt::CaseInsensitive)) + return ScreenPlay::VideoCodec::VideoCodec::H264; + + if(type.contains("h265",Qt::CaseInsensitive)) + return ScreenPlay::VideoCodec::VideoCodec::H264; + + return std::nullopt; +} + /*! \brief Converts the given \a url string to a local file path. */ @@ -368,4 +395,5 @@ std::optional> parseStringToIntegerList(const QString string) return list; } + } diff --git a/ScreenPlayWallpaper/SPWResources.qrc b/ScreenPlayWallpaper/SPWResources.qrc index 9dc8f818..4db24768 100644 --- a/ScreenPlayWallpaper/SPWResources.qrc +++ b/ScreenPlayWallpaper/SPWResources.qrc @@ -9,5 +9,6 @@ qml/WebsiteWallpaper.qml qml/WebView.qml qml/MultimediaView.qml + qml/MultimediaWebView.qml diff --git a/ScreenPlayWallpaper/qml/MultimediaView.qml b/ScreenPlayWallpaper/qml/MultimediaView.qml index 157bb6a1..c0d7463b 100644 --- a/ScreenPlayWallpaper/qml/MultimediaView.qml +++ b/ScreenPlayWallpaper/qml/MultimediaView.qml @@ -13,11 +13,4 @@ Video { if (Wallpaper.loops) root.play() } - - //loops: MediaPlayer.Infinite - // onErrorOccurred: function (error, errorString) { - // console.log("[qmlvideo] VideoItem.onError error " + error - // + " errorString " + errorString) - // root.fatalError() - // } } diff --git a/ScreenPlayWallpaper/qml/MultimediaWebView.qml b/ScreenPlayWallpaper/qml/MultimediaWebView.qml new file mode 100644 index 00000000..08d1a67e --- /dev/null +++ b/ScreenPlayWallpaper/qml/MultimediaWebView.qml @@ -0,0 +1,157 @@ +import QtQuick +import QtWebEngine +import ScreenPlay.Enums.InstalledType 1.0 +import ScreenPlayWallpaper 1.0 + +/*! + * The native macOS multimedia stack does not support VP8/VP9. For this we must use the WebEngine. + */ +Item { + id: root + + signal requestFadeIn() + + function getSetVideoCommand() { + // TODO 30: + // Currently wont work. Commit anyways til QtCreator and Qt work with js template literals + var src = "" + src += "var videoPlayer = document.getElementById('videoPlayer');" + src += "var videoSource = document.getElementById('videoSource');" + src += "videoSource.src = '" + Wallpaper.projectSourceFileAbsolute + "';" + src += "videoPlayer.load();" + src += "videoPlayer.volume = " + Wallpaper.volume + ";" + src += "videoPlayer.setAttribute('style', 'object-fit :" + Wallpaper.fillMode + ";');" + src += "videoPlayer.play();" + print(src) + return src + } + + Component.onCompleted: { + WebEngine.settings.localContentCanAccessFileUrls = true + WebEngine.settings.localContentCanAccessRemoteUrls = true + WebEngine.settings.allowRunningInsecureContent = true + WebEngine.settings.accelerated2dCanvasEnabled = true + WebEngine.settings.javascriptCanOpenWindows = false + WebEngine.settings.showScrollBars = false + WebEngine.settings.playbackRequiresUserGesture = false + WebEngine.settings.focusOnNavigationEnabled = true + } + + WebEngineView { + id: webView + anchors.fill: parent + // url:"https://www.google.de" + url: "qrc:/index.html" + onJavaScriptConsoleMessage:(lineNumber, message)=> print(lineNumber, message) + onLoadProgressChanged: { + if (loadProgress === 100) { + loadHtml("") + requestFadeIn() + return + webView.runJavaScript(root.getSetVideoCommand(), + function (result) { + requestFadeIn() + }) + } + } + } + + + + Text { + id: txtVisualsPaused + text: qsTr("If you can read this, then the VisualsPaused optimization does not work on your system. You can fix this by disable this in: \n Settings -> Perfromance -> Pause wallpaper video rendering while another app is in the foreground ") + font.pointSize: 32 + visible: false + horizontalAlignment: Text.AlignHCenter + verticalAlignment: Text.AlignVCenter + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + anchors.centerIn: parent + width: parent.width * 0.8 + color: "white" + } + + Timer { + id: timerCover + + interval: 300 + onTriggered: { + webView.visible = !Wallpaper.visualsPaused + txtVisualsPaused.visible = Wallpaper.visualsPaused + } + } + + Connections { + function onReloadVideo(oldType) { + webView.runJavaScript(root.getSetVideoCommand()) + } + + function onQmlExit() { + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.volume = 0;") + } + + function onMutedChanged(muted) { + if (muted) + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.volume = 0;") + else + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.volume = " + Wallpaper.volume + ";") + } + + function onFillModeChanged(fillMode) { + if (webView.loadProgress === 100) + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.setAttribute('style', 'object-fit :" + fillMode + ";');") + } + + function onLoopsChanged(loops) { + if (webView.loadProgress === 100) + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.loop = " + loops + ";") + } + + function onVolumeChanged(volume) { + if (webView.loadProgress === 100) + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.volume = " + volume + ";") + } + + function onCurrentTimeChanged(currentTime) { + if (webView.loadProgress === 100) + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.currentTime = " + + currentTime + " * videoPlayer.duration;") + } + + function onPlaybackRateChanged(playbackRate) { + if (webView.loadProgress === 100) + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.playbackRate = " + playbackRate + ";") + } + + function onVisualsPausedChanged(visualsPaused) { + if (visualsPaused) { + // Wait until Wallpaper animation is finsihed + timerCover.restart() + } else { + webView.visible = true + txtVisualsPaused.visible = false + } + } + + function onIsPlayingChanged(isPlaying) { + if (webView.loadProgress === 100) { + if (isPlaying) + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.play();") + else + webView.runJavaScript( + "var videoPlayer = document.getElementById('videoPlayer'); videoPlayer.pause();") + } + } + + target: Wallpaper + } +} diff --git a/ScreenPlayWallpaper/qml/Wallpaper.qml b/ScreenPlayWallpaper/qml/Wallpaper.qml index ac1815e4..933f274f 100644 --- a/ScreenPlayWallpaper/qml/Wallpaper.qml +++ b/ScreenPlayWallpaper/qml/Wallpaper.qml @@ -3,16 +3,31 @@ import QtQuick import QtQuick.Controls import ScreenPlayWallpaper 1.0 import ScreenPlay.Enums.InstalledType 1.0 +import ScreenPlay.Enums.VideoCodec 1.0 Rectangle { id: root + onStateChanged: print(state) property bool canFadeByWallpaperFillMode: true function init() { + print("init") switch (Wallpaper.type) { case InstalledType.VideoWallpaper: - loader.source = "qrc:/qml/MultimediaView.qml"; + if(Wallpaper.videoCodec === VideoCodec.Unknown){ + Wallpaper.terminate() + } + if(Qt.platform.os === "osx") { + // macOS only supports h264 via the native Qt MM + if(Wallpaper.videoCodec === VideoCodec.VP8 || Wallpaper.videoCodec === VideoCodec.VP9){ + print(Qt.resolvedUrl(Wallpaper.projectSourceFileAbsolute)) + loader.source = "qrc:/qml/MultimediaWebView.qml"; + print(loader.status) + }else { + loader.source = "qrc:/qml/MultimediaView.qml"; + } + } fadeIn(); break; case InstalledType.HTMLWallpaper: @@ -106,11 +121,11 @@ Rectangle { anchors.fill: parent // QML Engine deadlocks in 5.15.2 when a loader cannot load // an item. QApplication::quit(); waits for the destruction forever. - asynchronous: true + //asynchronous: true onStatusChanged: { if (loader.status === Loader.Error) { loader.source = ""; - Wallpaper.terminate(); + // Wallpaper.terminate(); } } diff --git a/ScreenPlayWallpaper/src/basewindow.cpp b/ScreenPlayWallpaper/src/basewindow.cpp index c70f8d97..054fea9c 100644 --- a/ScreenPlayWallpaper/src/basewindow.cpp +++ b/ScreenPlayWallpaper/src/basewindow.cpp @@ -41,6 +41,12 @@ BaseWindow::BaseWindow( "InstalledType", "Error: only enums"); + qmlRegisterUncreatableMetaObject(ScreenPlay::VideoCodec::staticMetaObject, + "ScreenPlay.Enums.VideoCodec", + 1, 0, + "VideoCodec", + "Error: only enums"); + qmlRegisterType("ScreenPlay.Wallpaper", 1, 0, "Wallpaper"); if (!appID.contains("appID=")) { @@ -76,8 +82,33 @@ BaseWindow::BaseWindow( QApplication::exit(-4); } + + if (auto typeOpt = ScreenPlayUtil::getInstalledTypeFromString(project.value("type").toString())) { setType(typeOpt.value()); + + if (!project.contains("videoCodec") ) { + qWarning("No videoCodec was specified inside the json object!"); + // QApplication::exit(-4); + const QString filename = project.value("file").toString(); + qInfo() << filename; + if(filename.endsWith(".mp4")){ + setVideoCodec(ScreenPlay::VideoCodec::VideoCodec::H264); + } else if(filename.endsWith(".webm")){ + setVideoCodec(ScreenPlay::VideoCodec::VideoCodec::VP8); + } + } else { + if(this->type() == ScreenPlay::InstalledType::InstalledType::VideoWallpaper){ + if (auto videoCodecOpt = ScreenPlayUtil::getVideoCodecFromString(project.value("videoCodec").toString())) { + setVideoCodec(videoCodecOpt.value()); + } else { + qCritical() << "Cannot parse Wallpaper video codec from value" << project.value("type"); + } + + } + } + + } else { qCritical() << "Cannot parse Wallpaper type from value" << project.value("type"); } @@ -239,3 +270,16 @@ void BaseWindow::setupLiveReloading() QObject::connect(&m_liveReloadLimiter, &QTimer::timeout, this, reloadQMLLambda); m_fileSystemWatcher.addPaths({ QUrl::fromUserInput(projectPath()).toLocalFile() }); } + +ScreenPlay::VideoCodec::VideoCodec BaseWindow::videoCodec() const +{ + return m_videoCodec; +} + +void BaseWindow::setVideoCodec(ScreenPlay::VideoCodec::VideoCodec newVideoCodec) +{ + if (m_videoCodec == newVideoCodec) + return; + m_videoCodec = newVideoCodec; + emit videoCodecChanged(); +} diff --git a/ScreenPlayWallpaper/src/basewindow.h b/ScreenPlayWallpaper/src/basewindow.h index d9e64371..0059253e 100644 --- a/ScreenPlayWallpaper/src/basewindow.h +++ b/ScreenPlayWallpaper/src/basewindow.h @@ -91,6 +91,7 @@ public: Q_PROPERTY(float currentTime READ currentTime WRITE setCurrentTime NOTIFY currentTimeChanged) Q_PROPERTY(ScreenPlay::InstalledType::InstalledType type READ type WRITE setType NOTIFY typeChanged) + Q_PROPERTY(ScreenPlay::VideoCodec::VideoCodec videoCodec READ videoCodec WRITE setVideoCodec NOTIFY videoCodecChanged) Q_PROPERTY(QString OSVersion READ OSVersion WRITE setOSVersion NOTIFY OSVersionChanged) Q_PROPERTY(ScreenPlaySDK* sdk READ sdk WRITE setSdk NOTIFY sdkChanged) @@ -117,6 +118,9 @@ public: const QString& projectSourceFile() const { return m_projectSourceFile; } const QUrl& projectSourceFileAbsolute() const { return m_projectSourceFileAbsolute; } + ScreenPlay::VideoCodec::VideoCodec videoCodec() const; + void setVideoCodec(ScreenPlay::VideoCodec::VideoCodec newVideoCodec); + signals: void qmlExit(); void reloadQML(const ScreenPlay::InstalledType::InstalledType oldType); @@ -146,6 +150,8 @@ signals: void projectSourceFileChanged(const QString& projectSourceFile); void projectSourceFileAbsoluteChanged(const QUrl& rojectSourceFileAbsolute); + void videoCodecChanged(); + public slots: virtual void destroyThis() { } virtual void setVisible(bool show) { Q_UNUSED(show) } @@ -385,4 +391,5 @@ private: QSysInfo m_sysinfo; std::unique_ptr m_sdk; QUrl m_projectSourceFileAbsolute; + ScreenPlay::VideoCodec::VideoCodec m_videoCodec = ScreenPlay::VideoCodec::VideoCodec::Unknown; };