Added 'iterationCount' and 'onIterationsDone' properties to the video element

This commit is contained in:
Leon Styhre 2023-08-13 14:48:00 +02:00
parent 89d94bc1b0
commit c15aa73de2
6 changed files with 57 additions and 16 deletions

View file

@ -88,7 +88,7 @@ bool MediaViewer::startMediaViewer(FileData* game)
void MediaViewer::stopMediaViewer() void MediaViewer::stopMediaViewer()
{ {
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
ViewController::getInstance()->stopViewVideos(); ViewController::getInstance()->startViewVideos();
mVideoFile = ""; mVideoFile = "";
mVideo.reset(); mVideo.reset();

View file

@ -48,7 +48,7 @@ bool PDFViewer::startPDFViewer(FileData* game)
LOG(LogError) << "Couldn't find PDF conversion binary es-pdf-convert"; LOG(LogError) << "Couldn't find PDF conversion binary es-pdf-convert";
#endif #endif
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
ViewController::getInstance()->stopViewVideos(); ViewController::getInstance()->startViewVideos();
return false; return false;
} }
@ -58,7 +58,7 @@ bool PDFViewer::startPDFViewer(FileData* game)
if (!Utils::FileSystem::exists(mManualPath)) { if (!Utils::FileSystem::exists(mManualPath)) {
LOG(LogError) << "No PDF manual found for game \"" << mGame->getName() << "\""; LOG(LogError) << "No PDF manual found for game \"" << mGame->getName() << "\"";
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
ViewController::getInstance()->stopViewVideos(); ViewController::getInstance()->startViewVideos();
return false; return false;
} }
@ -89,7 +89,7 @@ bool PDFViewer::startPDFViewer(FileData* game)
if (!getDocumentInfo()) { if (!getDocumentInfo()) {
LOG(LogError) << "PDFViewer: Couldn't load file \"" << mManualPath << "\""; LOG(LogError) << "PDFViewer: Couldn't load file \"" << mManualPath << "\"";
ViewController::getInstance()->stopViewVideos(); ViewController::getInstance()->startViewVideos();
return false; return false;
} }
@ -98,7 +98,7 @@ bool PDFViewer::startPDFViewer(FileData* game)
for (int i {1}; i <= mPageCount; ++i) { for (int i {1}; i <= mPageCount; ++i) {
if (mPages.find(i) == mPages.end()) { if (mPages.find(i) == mPages.end()) {
LOG(LogError) << "Couldn't read information for page " << i << ", invalid PDF file?"; LOG(LogError) << "Couldn't read information for page " << i << ", invalid PDF file?";
ViewController::getInstance()->stopViewVideos(); ViewController::getInstance()->startViewVideos();
return false; return false;
} }
@ -183,7 +183,7 @@ bool PDFViewer::startPDFViewer(FileData* game)
void PDFViewer::stopPDFViewer() void PDFViewer::stopPDFViewer()
{ {
NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND); NavigationSounds::getInstance().playThemeNavigationSound(SCROLLSOUND);
ViewController::getInstance()->stopViewVideos(); ViewController::getInstance()->startViewVideos();
mPages.clear(); mPages.clear();
mPageImage.reset(); mPageImage.reset();

View file

@ -305,6 +305,8 @@ std::map<std::string, std::map<std::string, ThemeData::ElementPropertyType>>
{"metadataElement", BOOLEAN}, {"metadataElement", BOOLEAN},
{"gameselector", STRING}, {"gameselector", STRING},
{"gameselectorEntry", UNSIGNED_INTEGER}, {"gameselectorEntry", UNSIGNED_INTEGER},
{"iterationCount", UNSIGNED_INTEGER},
{"onIterationsDone", STRING},
{"audio", BOOLEAN}, {"audio", BOOLEAN},
{"interpolation", STRING}, {"interpolation", STRING},
{"color", COLOR}, {"color", COLOR},

View file

@ -31,6 +31,7 @@ VideoComponent::VideoComponent()
, mTopLeftCrop {0.0f, 0.0f} , mTopLeftCrop {0.0f, 0.0f}
, mBottomRightCrop {1.0f, 1.0f} , mBottomRightCrop {1.0f, 1.0f}
, mPillarboxThreshold {0.85f, 0.90f} , mPillarboxThreshold {0.85f, 0.90f}
, mOnIterationsDone {OnIterationsDone::NOTHING}
, mStartTime {0} , mStartTime {0}
, mIsPlaying {false} , mIsPlaying {false}
, mIsActuallyPlaying {false} , mIsActuallyPlaying {false}
@ -46,6 +47,8 @@ VideoComponent::VideoComponent()
, mGeneralFade {false} , mGeneralFade {false}
, mFadeIn {1.0f} , mFadeIn {1.0f}
, mFadeInTime {1000.0f} , mFadeInTime {1000.0f}
, mIterationCount {0}
, mPlayCount {0}
{ {
// Setup default configuration. // Setup default configuration.
mConfig.showStaticImageDelay = false; mConfig.showStaticImageDelay = false;
@ -186,6 +189,23 @@ void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme,
if (elem->has("metadataElement") && elem->get<bool>("metadataElement")) if (elem->has("metadataElement") && elem->get<bool>("metadataElement"))
mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT; mComponentThemeFlags |= ComponentThemeFlags::METADATA_ELEMENT;
if (elem->has("iterationCount")) {
mIterationCount = glm::clamp(elem->get<unsigned int>("iterationCount"), 0u, 10u);
if (properties && elem->has("onIterationsDone")) {
const std::string& onIterationsDone {elem->get<std::string>("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")) if (elem->has("audio"))
mPlayAudio = elem->get<bool>("audio"); mPlayAudio = elem->get<bool>("audio");
@ -343,6 +363,9 @@ std::vector<HelpPrompt> VideoComponent::getHelpPrompts()
void VideoComponent::update(int deltaTime) 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 // 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. // the video from being rendered. This can happen on application startup in some instances.
if (deltaTime == 0) if (deltaTime == 0)
@ -397,6 +420,8 @@ void VideoComponent::update(int deltaTime)
void VideoComponent::startVideoPlayer() void VideoComponent::startVideoPlayer()
{ {
mPlayCount = 0;
if (mIsPlaying) if (mIsPlaying)
stopVideoPlayer(); stopVideoPlayer();
@ -408,9 +433,9 @@ void VideoComponent::startVideoPlayer()
mPaused = false; 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; return;
if (mStaticImagePath != "") { if (mStaticImagePath != "") {

View file

@ -92,7 +92,12 @@ public:
protected: protected:
virtual void startVideoStream() {} virtual void startVideoStream() {}
void renderStaticImage(const glm::mat4& parentTrans); void renderStaticImage(const glm::mat4& parentTrans, bool forceRender);
enum class OnIterationsDone {
NOTHING,
IMAGE
};
ImageComponent mStaticImage; ImageComponent mStaticImage;
@ -116,6 +121,7 @@ protected:
"cover", "backcover", "3dbox", "physicalmedia", "fanart"}; "cover", "backcover", "3dbox", "physicalmedia", "fanart"};
std::string mVideoPath; std::string mVideoPath;
OnIterationsDone mOnIterationsDone;
unsigned mStartTime; unsigned mStartTime;
std::atomic<bool> mIsPlaying; std::atomic<bool> mIsPlaying;
std::atomic<bool> mIsActuallyPlaying; std::atomic<bool> mIsActuallyPlaying;
@ -131,6 +137,8 @@ protected:
bool mGeneralFade; bool mGeneralFade;
float mFadeIn; float mFadeIn;
float mFadeInTime; float mFadeInTime;
int mIterationCount;
int mPlayCount;
Configuration mConfig; Configuration mConfig;
}; };

View file

@ -165,6 +165,12 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
if (!mHasVideo && mStaticImagePath == "") if (!mHasVideo && mStaticImagePath == "")
return; return;
if (mIterationCount != 0 && mPlayCount == mIterationCount) {
if (mOnIterationsDone == OnIterationsDone::IMAGE)
VideoComponent::renderStaticImage(parentTrans, true);
return;
}
glm::mat4 trans {parentTrans * getTransform()}; glm::mat4 trans {parentTrans * getTransform()};
GuiComponent::renderChildren(trans); GuiComponent::renderChildren(trans);
@ -203,7 +209,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
vertices[3].color = mColorShiftEnd; vertices[3].color = mColorShiftEnd;
// Round vertices. // Round vertices.
for (int i = 0; i < 4; ++i) for (int i {0}; i < 4; ++i)
vertices[i].position = glm::round(vertices[i].position); vertices[i].position = glm::round(vertices[i].position);
if (mFadeIn < 1.0f || mThemeOpacity < 1.0f) if (mFadeIn < 1.0f || mThemeOpacity < 1.0f)
@ -265,8 +271,7 @@ void VideoFFmpegComponent::render(const glm::mat4& parentTrans)
Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA); Renderer::BlendFactor::ONE_MINUS_SRC_ALPHA);
} }
else { else {
if (mVisible) VideoComponent::renderStaticImage(parentTrans, false);
VideoComponent::renderStaticImage(parentTrans);
} }
} }
@ -647,7 +652,7 @@ void VideoFFmpegComponent::readFrames()
} }
if (mVideoCodecContext && mFormatContext) { if (mVideoCodecContext && mFormatContext) {
for (int i = 0; i < readLoops; ++i) { for (int i {0}; i < readLoops; ++i) {
if (static_cast<int>(mVideoFrameQueue.size()) < mVideoTargetQueueSize || if (static_cast<int>(mVideoFrameQueue.size()) < mVideoTargetQueueSize ||
(mAudioStreamIndex >= 0 && (mAudioStreamIndex >= 0 &&
static_cast<int>(mAudioFrameQueue.size()) < mAudioTargetQueueSize)) { static_cast<int>(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. // 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)}; const AVCodecHWConfig* config {avcodec_get_hw_config(mHardwareCodec, i)};
if (!config) { if (!config) {
LOG(LogDebug) << "VideoFFmpegComponent::decoderInitHW(): Hardware decoder \"" 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 // 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 // that the hardware encoder can actually be used, otherwise the fallback
// to software decoding would take place when it's not necessary. // 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) { if (avcodec_receive_frame(checkCodecContext, checkFrame) < 0) {
av_packet_unref(checkPacket); av_packet_unref(checkPacket);
while (av_read_frame(mFormatContext, checkPacket) == 0) { while (av_read_frame(mFormatContext, checkPacket) == 0) {
@ -1611,13 +1616,14 @@ void VideoFFmpegComponent::pauseVideoPlayer()
void VideoFFmpegComponent::handleLooping() void VideoFFmpegComponent::handleLooping()
{ {
if (mIsPlaying && mEndOfVideo) { if (mIsPlaying && mEndOfVideo) {
++mPlayCount;
// If the screensaver video swap time is set to 0, it means we should // 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. // skip to the next game when the video has finished playing.
if (mScreensaverMode && if (mScreensaverMode &&
Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) { Settings::getInstance()->getInt("ScreensaverSwapVideoTimeout") == 0) {
mWindow->screensaverTriggerNextGame(); mWindow->screensaverTriggerNextGame();
} }
else { else if (mIterationCount == 0 || mPlayCount < mIterationCount) {
stopVideoPlayer(); stopVideoPlayer();
startVideoStream(); startVideoStream();
} }