From c15aa73de2e80c04d92e03cec8c4f8e830afc180 Mon Sep 17 00:00:00 2001 From: Leon Styhre Date: Sun, 13 Aug 2023 14:48:00 +0200 Subject: [PATCH] Added 'iterationCount' and 'onIterationsDone' properties to the video element --- es-app/src/MediaViewer.cpp | 2 +- es-app/src/PDFViewer.cpp | 10 +++---- es-core/src/ThemeData.cpp | 2 ++ es-core/src/components/VideoComponent.cpp | 29 +++++++++++++++++-- es-core/src/components/VideoComponent.h | 10 ++++++- .../src/components/VideoFFmpegComponent.cpp | 20 ++++++++----- 6 files changed, 57 insertions(+), 16 deletions(-) diff --git a/es-app/src/MediaViewer.cpp b/es-app/src/MediaViewer.cpp index 4481e697e..6b854e693 100644 --- a/es-app/src/MediaViewer.cpp +++ b/es-app/src/MediaViewer.cpp @@ -88,7 +88,7 @@ bool MediaViewer::startMediaViewer(FileData* game) void MediaViewer::stopMediaViewer() { NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - ViewController::getInstance()->stopViewVideos(); + ViewController::getInstance()->startViewVideos(); mVideoFile = ""; mVideo.reset(); diff --git a/es-app/src/PDFViewer.cpp b/es-app/src/PDFViewer.cpp index ed933d44b..98b373cf7 100644 --- a/es-app/src/PDFViewer.cpp +++ b/es-app/src/PDFViewer.cpp @@ -48,7 +48,7 @@ bool PDFViewer::startPDFViewer(FileData* game) LOG(LogError) << "Couldn't find PDF conversion binary es-pdf-convert"; #endif NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - ViewController::getInstance()->stopViewVideos(); + ViewController::getInstance()->startViewVideos(); return false; } @@ -58,7 +58,7 @@ bool PDFViewer::startPDFViewer(FileData* game) if (!Utils::FileSystem::exists(mManualPath)) { LOG(LogError) << "No PDF manual found for game \"" << mGame->getName() << "\""; NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - ViewController::getInstance()->stopViewVideos(); + ViewController::getInstance()->startViewVideos(); return false; } @@ -89,7 +89,7 @@ bool PDFViewer::startPDFViewer(FileData* game) if (!getDocumentInfo()) { LOG(LogError) << "PDFViewer: Couldn't load file \"" << mManualPath << "\""; - ViewController::getInstance()->stopViewVideos(); + ViewController::getInstance()->startViewVideos(); return false; } @@ -98,7 +98,7 @@ bool PDFViewer::startPDFViewer(FileData* game) for (int i {1}; i <= mPageCount; ++i) { if (mPages.find(i) == mPages.end()) { LOG(LogError) << "Couldn't read information for page " << i << ", invalid PDF file?"; - ViewController::getInstance()->stopViewVideos(); + ViewController::getInstance()->startViewVideos(); return false; } @@ -183,7 +183,7 @@ bool PDFViewer::startPDFViewer(FileData* game) void PDFViewer::stopPDFViewer() { NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); - ViewController::getInstance()->stopViewVideos(); + ViewController::getInstance()->startViewVideos(); mPages.clear(); mPageImage.reset(); diff --git a/es-core/src/ThemeData.cpp b/es-core/src/ThemeData.cpp index 4bf6ae63d..86cc12307 100644 --- a/es-core/src/ThemeData.cpp +++ b/es-core/src/ThemeData.cpp @@ -305,6 +305,8 @@ std::map> {"metadataElement", BOOLEAN}, {"gameselector", STRING}, {"gameselectorEntry", UNSIGNED_INTEGER}, + {"iterationCount", UNSIGNED_INTEGER}, + {"onIterationsDone", STRING}, {"audio", BOOLEAN}, {"interpolation", STRING}, {"color", COLOR}, diff --git a/es-core/src/components/VideoComponent.cpp b/es-core/src/components/VideoComponent.cpp index c297bc1bb..05027bb73 100644 --- a/es-core/src/components/VideoComponent.cpp +++ b/es-core/src/components/VideoComponent.cpp @@ -31,6 +31,7 @@ VideoComponent::VideoComponent() , mTopLeftCrop {0.0f, 0.0f} , mBottomRightCrop {1.0f, 1.0f} , mPillarboxThreshold {0.85f, 0.90f} + , mOnIterationsDone {OnIterationsDone::NOTHING} , mStartTime {0} , mIsPlaying {false} , mIsActuallyPlaying {false} @@ -46,6 +47,8 @@ VideoComponent::VideoComponent() , mGeneralFade {false} , mFadeIn {1.0f} , mFadeInTime {1000.0f} + , mIterationCount {0} + , mPlayCount {0} { // Setup default configuration. mConfig.showStaticImageDelay = false; @@ -186,6 +189,23 @@ void VideoComponent::applyTheme(const std::shared_ptr& theme, if (elem->has("metadataElement") && elem->get("metadataElement")) mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT; + if (elem->has("iterationCount")) { + mIterationCount = glm::clamp(elem->get("iterationCount"), 0u, 10u); + + if (properties && elem->has("onIterationsDone")) { + const std::string& onIterationsDone {elem->get("onIterationsDone")}; + if (onIterationsDone == "nothing") + mOnIterationsDone = OnIterationsDone::NOTHING; + else if (onIterationsDone == "image") + mOnIterationsDone = OnIterationsDone::IMAGE; + else + LOG(LogWarning) << "VideoComponent: Invalid theme configuration, property " + "\"onIterationsDone\" for element \"" + << element.substr(6) << "\" defined as \"" << onIterationsDone + << "\""; + } + } + if (elem->has("audio")) mPlayAudio = elem->get("audio"); @@ -343,6 +363,9 @@ std::vector VideoComponent::getHelpPrompts() void VideoComponent::update(int deltaTime) { + if (mIterationCount != 0 && mPlayCount == mIterationCount) + return; + // A deltaTime value of 0 would lead to mFadeIn being an invalid number which would prevent // the video from being rendered. This can happen on application startup in some instances. if (deltaTime == 0) @@ -397,6 +420,8 @@ void VideoComponent::update(int deltaTime) void VideoComponent::startVideoPlayer() { + mPlayCount = 0; + if (mIsPlaying) stopVideoPlayer(); @@ -408,9 +433,9 @@ void VideoComponent::startVideoPlayer() mPaused = false; } -void VideoComponent::renderStaticImage(const glm::mat4& parentTrans) +void VideoComponent::renderStaticImage(const glm::mat4& parentTrans, bool forceRender) { - if (mHasVideo && (!mConfig.showStaticImageDelay || mConfig.startDelay == 0)) + if (mHasVideo && (!forceRender && (!mConfig.showStaticImageDelay || mConfig.startDelay == 0))) return; if (mStaticImagePath != "") { diff --git a/es-core/src/components/VideoComponent.h b/es-core/src/components/VideoComponent.h index 73319dc23..40ccc8fb7 100644 --- a/es-core/src/components/VideoComponent.h +++ b/es-core/src/components/VideoComponent.h @@ -92,7 +92,12 @@ public: protected: virtual void startVideoStream() {} - void renderStaticImage(const glm::mat4& parentTrans); + void renderStaticImage(const glm::mat4& parentTrans, bool forceRender); + + enum class OnIterationsDone { + NOTHING, + IMAGE + }; ImageComponent mStaticImage; @@ -116,6 +121,7 @@ protected: "cover", "backcover", "3dbox", "physicalmedia", "fanart"}; std::string mVideoPath; + OnIterationsDone mOnIterationsDone; unsigned mStartTime; std::atomic mIsPlaying; std::atomic mIsActuallyPlaying; @@ -131,6 +137,8 @@ protected: bool mGeneralFade; float mFadeIn; float mFadeInTime; + int mIterationCount; + int mPlayCount; Configuration mConfig; }; diff --git a/es-core/src/components/VideoFFmpegComponent.cpp b/es-core/src/components/VideoFFmpegComponent.cpp index a194184a0..9367fb7d8 100644 --- a/es-core/src/components/VideoFFmpegComponent.cpp +++ b/es-core/src/components/VideoFFmpegComponent.cpp @@ -165,6 +165,12 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) if (!mHasVideo && mStaticImagePath == "") return; + if (mIterationCount != 0 && mPlayCount == mIterationCount) { + if (mOnIterationsDone == OnIterationsDone::IMAGE) + VideoComponent::renderStaticImage(parentTrans, true); + return; + } + glm::mat4 trans {parentTrans * getTransform()}; GuiComponent::renderChildren(trans); @@ -203,7 +209,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) vertices[3].color = mColorShiftEnd; // Round vertices. - for (int i = 0; i < 4; ++i) + for (int i {0}; i < 4; ++i) vertices[i].position = glm::round(vertices[i].position); if (mFadeIn < 1.0f || mThemeOpacity < 1.0f) @@ -265,8 +271,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans) Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA); } else { - if (mVisible) - VideoComponent::renderStaticImage(parentTrans); + VideoComponent::renderStaticImage(parentTrans, false); } } @@ -647,7 +652,7 @@ void VideoFFmpegComponent::readFrames() } if (mVideoCodecContext && mFormatContext) { - for (int i = 0; i < readLoops; ++i) { + for (int i {0}; i < readLoops; ++i) { if (static_cast(mVideoFrameQueue.size()) < mVideoTargetQueueSize || (mAudioStreamIndex >= 0 && static_cast(mAudioFrameQueue.size()) < mAudioTargetQueueSize)) { @@ -1152,7 +1157,7 @@ bool VideoFFmpegComponent::decoderInitHW() } // 50 is just an arbitrary number so we don't potentially get stuck in an endless loop. - for (int i = 0; i < 50; ++i) { + for (int i {0}; i < 50; ++i) { const AVCodecHWConfig* config {avcodec_get_hw_config(mHardwareCodec, i)}; if (!config) { LOG(LogDebug) << "VideoFFmpegComponent::decoderInitHW(): Hardware decoder \"" @@ -1245,7 +1250,7 @@ bool VideoFFmpegComponent::decoderInitHW() // For some videos we need to process at least one extra frame to verify // that the hardware encoder can actually be used, otherwise the fallback // to software decoding would take place when it's not necessary. - for (int i = 0; i < 3; ++i) { + for (int i {0}; i < 3; ++i) { if (avcodec_receive_frame(checkCodecContext, checkFrame) < 0) { av_packet_unref(checkPacket); while (av_read_frame(mFormatContext, checkPacket) == 0) { @@ -1611,13 +1616,14 @@ void VideoFFmpegComponent::pauseVideoPlayer() void VideoFFmpegComponent::handleLooping() { if (mIsPlaying && mEndOfVideo) { + ++mPlayCount; // If the screensaver video swap time is set to 0, it means we should // skip to the next game when the video has finished playing. if (mScreensaverMode && Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) { mWindow->screensaverTriggerNextGame(); } - else { + else if (mIterationCount == 0 || mPlayCount < mIterationCount) { stopVideoPlayer(); startVideoStream(); }