2016-12-04 23:47:34 +00:00
|
|
|
#include "components/VideoComponent.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
|
|
|
#include "resources/ResourceManager.h"
|
2018-01-09 22:55:09 +00:00
|
|
|
#include "utils/FileSystemUtil.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "PowerSaver.h"
|
2016-12-04 23:47:34 +00:00
|
|
|
#include "Renderer.h"
|
|
|
|
#include "ThemeData.h"
|
2017-03-25 17:02:28 +00:00
|
|
|
#include "Window.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include <SDL_timer.h>
|
2016-12-04 23:47:34 +00:00
|
|
|
|
|
|
|
#define FADE_TIME_MS 200
|
|
|
|
|
2017-06-01 20:08:44 +00:00
|
|
|
std::string getTitlePath() {
|
|
|
|
std::string titleFolder = getTitleFolder();
|
|
|
|
return titleFolder + "last_title.srt";
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string getTitleFolder() {
|
2018-01-27 20:04:08 +00:00
|
|
|
std::string home = Utils::FileSystem::getHomePath();
|
2017-06-01 20:08:44 +00:00
|
|
|
return home + "/.emulationstation/tmp/";
|
|
|
|
}
|
|
|
|
|
|
|
|
void writeSubtitle(const char* gameName, const char* systemName, bool always)
|
|
|
|
{
|
|
|
|
FILE* file = fopen(getTitlePath().c_str(), "w");
|
2017-08-23 08:21:31 +00:00
|
|
|
int end = (int)(Settings::getInstance()->getInt("ScreenSaverSwapVideoTimeout") / (1000));
|
2017-06-01 20:08:44 +00:00
|
|
|
if (always) {
|
2017-10-28 20:21:48 +00:00
|
|
|
fprintf(file, "1\n00:00:01,000 --> 00:00:%d,000\n", end);
|
2017-06-01 20:08:44 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fprintf(file, "1\n00:00:01,000 --> 00:00:08,000\n");
|
|
|
|
}
|
|
|
|
fprintf(file, "%s\n", gameName);
|
|
|
|
fprintf(file, "<i>%s</i>\n\n", systemName);
|
|
|
|
|
|
|
|
if (!always) {
|
2017-08-23 08:21:31 +00:00
|
|
|
if (end > 12)
|
|
|
|
{
|
2017-10-28 20:21:48 +00:00
|
|
|
fprintf(file, "2\n00:00:%d,000 --> 00:00:%d,000\n%s\n<i>%s</i>\n", end-4, end, gameName, systemName);
|
2017-08-23 08:21:31 +00:00
|
|
|
}
|
2017-06-01 20:08:44 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fflush(file);
|
|
|
|
fclose(file);
|
|
|
|
file = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::setScreensaverMode(bool isScreensaver)
|
|
|
|
{
|
|
|
|
mScreensaverMode = isScreensaver;
|
|
|
|
}
|
|
|
|
|
2016-12-04 23:47:34 +00:00
|
|
|
VideoComponent::VideoComponent(Window* window) :
|
|
|
|
GuiComponent(window),
|
|
|
|
mStaticImage(window),
|
|
|
|
mVideoHeight(0),
|
|
|
|
mVideoWidth(0),
|
|
|
|
mStartDelayed(false),
|
|
|
|
mIsPlaying(false),
|
2017-02-24 01:42:35 +00:00
|
|
|
mShowing(false),
|
2017-01-25 15:00:56 +00:00
|
|
|
mScreensaverActive(false),
|
|
|
|
mDisable(false),
|
2017-06-01 20:08:44 +00:00
|
|
|
mScreensaverMode(false),
|
2017-02-24 01:42:35 +00:00
|
|
|
mTargetIsMax(false),
|
|
|
|
mTargetSize(0, 0)
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
|
|
|
// Setup the default configuration
|
|
|
|
mConfig.showSnapshotDelay = false;
|
|
|
|
mConfig.showSnapshotNoVideo = false;
|
|
|
|
mConfig.startDelay = 0;
|
2017-03-25 17:02:28 +00:00
|
|
|
if (mWindow->getGuiStackSize() > 1) {
|
|
|
|
topWindow(false);
|
|
|
|
}
|
|
|
|
|
2017-06-01 20:08:44 +00:00
|
|
|
std::string path = getTitleFolder();
|
2018-01-09 22:55:09 +00:00
|
|
|
if(!Utils::FileSystem::exists(path))
|
|
|
|
Utils::FileSystem::createDirectory(path);
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoComponent::~VideoComponent()
|
|
|
|
{
|
|
|
|
// Stop any currently running video
|
|
|
|
stopVideo();
|
2017-06-01 20:08:44 +00:00
|
|
|
// Delete subtitle file, if existing
|
|
|
|
remove(getTitlePath().c_str());
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
2017-07-17 03:10:29 +00:00
|
|
|
void VideoComponent::onOriginChanged()
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
|
|
|
// Update the embeded static image
|
2017-07-17 03:10:29 +00:00
|
|
|
mStaticImage.setOrigin(mOrigin);
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::onSizeChanged()
|
|
|
|
{
|
|
|
|
// Update the embeded static image
|
|
|
|
mStaticImage.onSizeChanged();
|
|
|
|
}
|
|
|
|
|
|
|
|
bool VideoComponent::setVideo(std::string path)
|
|
|
|
{
|
2017-01-25 15:00:56 +00:00
|
|
|
// Convert the path into a generic format
|
2018-01-29 22:50:10 +00:00
|
|
|
std::string fullPath = Utils::FileSystem::getCanonicalPath(path);
|
2016-12-04 23:47:34 +00:00
|
|
|
|
|
|
|
// Check that it's changed
|
|
|
|
if (fullPath == mVideoPath)
|
|
|
|
return !path.empty();
|
|
|
|
|
|
|
|
// Store the path
|
|
|
|
mVideoPath = fullPath;
|
|
|
|
|
|
|
|
// If the file exists then set the new video
|
2018-01-29 22:50:10 +00:00
|
|
|
if (!fullPath.empty() && ResourceManager::getInstance()->fileExists(fullPath))
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
|
|
|
// Return true to show that we are going to attempt to play a video
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
// Return false to show that no video will be displayed
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::setImage(std::string path)
|
|
|
|
{
|
|
|
|
// Check that the image has changed
|
|
|
|
if (path == mStaticImagePath)
|
|
|
|
return;
|
2017-01-25 15:00:56 +00:00
|
|
|
|
2016-12-04 23:47:34 +00:00
|
|
|
mStaticImage.setImage(path);
|
|
|
|
mFadeIn = 0.0f;
|
|
|
|
mStaticImagePath = path;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::setDefaultVideo()
|
|
|
|
{
|
|
|
|
setVideo(mConfig.defaultVideoPath);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::setOpacity(unsigned char opacity)
|
|
|
|
{
|
|
|
|
mOpacity = opacity;
|
|
|
|
// Update the embeded static image
|
|
|
|
mStaticImage.setOpacity(opacity);
|
|
|
|
}
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
void VideoComponent::render(const Transform4x4f& parentTrans)
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
2017-10-28 20:24:35 +00:00
|
|
|
Transform4x4f trans = parentTrans * getTransform();
|
2016-12-04 23:47:34 +00:00
|
|
|
GuiComponent::renderChildren(trans);
|
|
|
|
|
|
|
|
Renderer::setMatrix(trans);
|
2017-01-25 15:00:56 +00:00
|
|
|
|
2016-12-04 23:47:34 +00:00
|
|
|
// Handle the case where the video is delayed
|
|
|
|
handleStartDelay();
|
|
|
|
|
|
|
|
// Handle looping of the video
|
|
|
|
handleLooping();
|
2017-06-13 22:57:18 +00:00
|
|
|
}
|
2016-12-04 23:47:34 +00:00
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
void VideoComponent::renderSnapshot(const Transform4x4f& parentTrans)
|
2017-06-13 22:57:18 +00:00
|
|
|
{
|
|
|
|
// This is the case where the video is not currently being displayed. Work out
|
|
|
|
// if we need to display a static image
|
|
|
|
if ((mConfig.showSnapshotNoVideo && mVideoPath.empty()) || (mStartDelayed && mConfig.showSnapshotDelay))
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
2017-06-13 22:57:18 +00:00
|
|
|
// Display the static image instead
|
|
|
|
mStaticImage.setOpacity((unsigned char)(mFadeIn * 255.0f));
|
|
|
|
mStaticImage.render(parentTrans);
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::applyTheme(const std::shared_ptr<ThemeData>& theme, const std::string& view, const std::string& element, unsigned int properties)
|
|
|
|
{
|
|
|
|
using namespace ThemeFlags;
|
|
|
|
|
|
|
|
const ThemeData::ThemeElement* elem = theme->getElement(view, element, "video");
|
|
|
|
if(!elem)
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
Vector2f scale = getParent() ? getParent()->getSize() : Vector2f((float)Renderer::getScreenWidth(), (float)Renderer::getScreenHeight());
|
2016-12-04 23:47:34 +00:00
|
|
|
|
|
|
|
if ((properties & POSITION) && elem->has("pos"))
|
|
|
|
{
|
2017-10-28 20:24:35 +00:00
|
|
|
Vector2f denormalized = elem->get<Vector2f>("pos") * scale;
|
|
|
|
setPosition(Vector3f(denormalized.x(), denormalized.y(), 0));
|
|
|
|
mStaticImage.setPosition(Vector3f(denormalized.x(), denormalized.y(), 0));
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
2017-02-24 01:42:35 +00:00
|
|
|
if(properties & ThemeFlags::SIZE)
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
2017-02-24 01:42:35 +00:00
|
|
|
if(elem->has("size"))
|
2017-10-28 20:24:35 +00:00
|
|
|
setResize(elem->get<Vector2f>("size") * scale);
|
2017-02-24 01:42:35 +00:00
|
|
|
else if(elem->has("maxSize"))
|
2017-10-28 20:24:35 +00:00
|
|
|
setMaxSize(elem->get<Vector2f>("maxSize") * scale);
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// position + size also implies origin
|
|
|
|
if (((properties & ORIGIN) || ((properties & POSITION) && (properties & ThemeFlags::SIZE))) && elem->has("origin"))
|
2017-10-28 20:24:35 +00:00
|
|
|
setOrigin(elem->get<Vector2f>("origin"));
|
2016-12-04 23:47:34 +00:00
|
|
|
|
|
|
|
if(elem->has("default"))
|
|
|
|
mConfig.defaultVideoPath = elem->get<std::string>("default");
|
|
|
|
|
|
|
|
if((properties & ThemeFlags::DELAY) && elem->has("delay"))
|
|
|
|
mConfig.startDelay = (unsigned)(elem->get<float>("delay") * 1000.0f);
|
|
|
|
|
|
|
|
if (elem->has("showSnapshotNoVideo"))
|
|
|
|
mConfig.showSnapshotNoVideo = elem->get<bool>("showSnapshotNoVideo");
|
|
|
|
|
|
|
|
if (elem->has("showSnapshotDelay"))
|
|
|
|
mConfig.showSnapshotDelay = elem->get<bool>("showSnapshotDelay");
|
2017-04-22 14:15:16 +00:00
|
|
|
|
2017-07-17 03:10:29 +00:00
|
|
|
if(properties & ThemeFlags::ROTATION) {
|
|
|
|
if(elem->has("rotation"))
|
|
|
|
setRotationDegrees(elem->get<float>("rotation"));
|
|
|
|
if(elem->has("rotationOrigin"))
|
2017-10-28 20:24:35 +00:00
|
|
|
setRotationOrigin(elem->get<Vector2f>("rotationOrigin"));
|
2017-07-17 03:10:29 +00:00
|
|
|
}
|
2017-04-22 14:15:16 +00:00
|
|
|
|
|
|
|
if(properties & ThemeFlags::Z_INDEX && elem->has("zIndex"))
|
|
|
|
setZIndex(elem->get<float>("zIndex"));
|
|
|
|
else
|
|
|
|
setZIndex(getDefaultZIndex());
|
2016-12-04 23:47:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<HelpPrompt> VideoComponent::getHelpPrompts()
|
|
|
|
{
|
|
|
|
std::vector<HelpPrompt> ret;
|
|
|
|
ret.push_back(HelpPrompt("a", "select"));
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::handleStartDelay()
|
|
|
|
{
|
|
|
|
// Only play if any delay has timed out
|
|
|
|
if (mStartDelayed)
|
|
|
|
{
|
|
|
|
if (mStartTime > SDL_GetTicks())
|
|
|
|
{
|
|
|
|
// Timeout not yet completed
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
// Completed
|
|
|
|
mStartDelayed = false;
|
|
|
|
// Clear the playing flag so startVideo works
|
|
|
|
mIsPlaying = false;
|
|
|
|
startVideo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::handleLooping()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::startVideoWithDelay()
|
|
|
|
{
|
|
|
|
// If not playing then either start the video or initiate the delay
|
|
|
|
if (!mIsPlaying)
|
|
|
|
{
|
|
|
|
// Set the video that we are going to be playing so we don't attempt to restart it
|
|
|
|
mPlayingVideoPath = mVideoPath;
|
|
|
|
|
2017-08-11 17:03:12 +00:00
|
|
|
if (mConfig.startDelay == 0 || PowerSaver::getMode() == PowerSaver::INSTANT)
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
|
|
|
// No delay. Just start the video
|
|
|
|
mStartDelayed = false;
|
|
|
|
startVideo();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// Configure the start delay
|
|
|
|
mStartDelayed = true;
|
|
|
|
mFadeIn = 0.0f;
|
|
|
|
mStartTime = SDL_GetTicks() + mConfig.startDelay;
|
|
|
|
}
|
|
|
|
mIsPlaying = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::update(int deltaTime)
|
|
|
|
{
|
|
|
|
manageState();
|
|
|
|
|
|
|
|
// If the video start is delayed and there is less than the fade time then set the image fade
|
|
|
|
// accordingly
|
|
|
|
if (mStartDelayed)
|
|
|
|
{
|
|
|
|
Uint32 ticks = SDL_GetTicks();
|
2017-01-25 15:00:56 +00:00
|
|
|
if (mStartTime > ticks)
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
|
|
|
Uint32 diff = mStartTime - ticks;
|
2017-01-25 15:00:56 +00:00
|
|
|
if (diff < FADE_TIME_MS)
|
2016-12-04 23:47:34 +00:00
|
|
|
{
|
|
|
|
mFadeIn = (float)diff / (float)FADE_TIME_MS;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// If the fade in is less than 1 then increment it
|
|
|
|
if (mFadeIn < 1.0f)
|
|
|
|
{
|
|
|
|
mFadeIn += deltaTime / (float)FADE_TIME_MS;
|
|
|
|
if (mFadeIn > 1.0f)
|
|
|
|
mFadeIn = 1.0f;
|
|
|
|
}
|
|
|
|
GuiComponent::update(deltaTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::manageState()
|
|
|
|
{
|
2017-01-25 15:00:56 +00:00
|
|
|
// We will only show if the component is on display and the screensaver
|
|
|
|
// is not active
|
|
|
|
bool show = mShowing && !mScreensaverActive && !mDisable;
|
2016-12-04 23:47:34 +00:00
|
|
|
|
|
|
|
// See if we're already playing
|
|
|
|
if (mIsPlaying)
|
|
|
|
{
|
|
|
|
// If we are not on display then stop the video from playing
|
|
|
|
if (!show)
|
|
|
|
{
|
|
|
|
stopVideo();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if (mVideoPath != mPlayingVideoPath)
|
|
|
|
{
|
|
|
|
// Path changed. Stop the video. We will start it again below because
|
|
|
|
// mIsPlaying will be modified by stopVideo to be false
|
|
|
|
stopVideo();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Need to recheck variable rather than 'else' because it may be modified above
|
|
|
|
if (!mIsPlaying)
|
|
|
|
{
|
|
|
|
// If we are on display then see if we should start the video
|
|
|
|
if (show && !mVideoPath.empty())
|
|
|
|
{
|
|
|
|
startVideoWithDelay();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::onShow()
|
|
|
|
{
|
|
|
|
mShowing = true;
|
|
|
|
manageState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::onHide()
|
|
|
|
{
|
|
|
|
mShowing = false;
|
|
|
|
manageState();
|
|
|
|
}
|
|
|
|
|
2017-01-25 15:00:56 +00:00
|
|
|
void VideoComponent::onScreenSaverActivate()
|
|
|
|
{
|
|
|
|
mScreensaverActive = true;
|
|
|
|
manageState();
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoComponent::onScreenSaverDeactivate()
|
|
|
|
{
|
|
|
|
mScreensaverActive = false;
|
|
|
|
manageState();
|
|
|
|
}
|
2016-12-04 23:47:34 +00:00
|
|
|
|
2017-01-25 15:00:56 +00:00
|
|
|
void VideoComponent::topWindow(bool isTop)
|
|
|
|
{
|
|
|
|
mDisable = !isTop;
|
|
|
|
manageState();
|
|
|
|
}
|