2020-09-21 17:17:34 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2020-06-28 16:39:18 +00:00
|
|
|
//
|
2020-09-21 17:17:34 +00:00
|
|
|
// EmulationStation Desktop Edition
|
2020-06-28 16:39:18 +00:00
|
|
|
// VideoVlcComponent.cpp
|
|
|
|
//
|
|
|
|
// Video playing using libVLC.
|
|
|
|
//
|
|
|
|
|
2017-01-25 15:00:56 +00:00
|
|
|
#include "components/VideoVlcComponent.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
|
2019-08-08 20:16:11 +00:00
|
|
|
#include "renderers/Renderer.h"
|
2017-11-01 22:21:10 +00:00
|
|
|
#include "resources/TextureResource.h"
|
2018-01-29 22:50:10 +00:00
|
|
|
#include "utils/StringUtil.h"
|
2017-03-25 17:02:28 +00:00
|
|
|
#include "Settings.h"
|
2020-11-12 16:13:24 +00:00
|
|
|
#include "Window.h"
|
2020-06-26 16:03:55 +00:00
|
|
|
|
2020-08-23 13:42:10 +00:00
|
|
|
#if defined(__APPLE__)
|
|
|
|
#include "utils/FileSystemUtil.h"
|
|
|
|
#endif
|
|
|
|
|
2020-06-26 16:03:55 +00:00
|
|
|
#include <SDL2/SDL_mutex.h>
|
|
|
|
#include <SDL2/SDL_timer.h>
|
2017-11-01 22:21:10 +00:00
|
|
|
#include <vlc/vlc.h>
|
2017-07-20 07:07:02 +00:00
|
|
|
|
2020-08-23 13:42:10 +00:00
|
|
|
#if defined(_WIN64)
|
2020-07-03 18:23:51 +00:00
|
|
|
#include <cstring>
|
2020-09-21 17:17:34 +00:00
|
|
|
#include <codecvt>
|
2017-01-25 15:00:56 +00:00
|
|
|
#endif
|
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
libvlc_instance_t* VideoVlcComponent::mVLC = nullptr;
|
2017-01-25 15:00:56 +00:00
|
|
|
|
|
|
|
// VLC prepares to render a video frame.
|
2020-06-28 16:39:18 +00:00
|
|
|
static void* lock(void* data, void** p_pixels) {
|
2020-11-17 16:20:25 +00:00
|
|
|
struct VideoContext* c = reinterpret_cast<struct VideoContext*>(data);
|
2020-06-28 16:39:18 +00:00
|
|
|
SDL_LockMutex(c->mutex);
|
|
|
|
SDL_LockSurface(c->surface);
|
|
|
|
*p_pixels = c->surface->pixels;
|
|
|
|
return nullptr; // Picture identifier, not needed here.
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// VLC just rendered a video frame.
|
2020-06-28 16:39:18 +00:00
|
|
|
static void unlock(void* data, void* /*id*/, void *const* /*p_pixels*/) {
|
2020-11-17 16:20:25 +00:00
|
|
|
struct VideoContext* c = reinterpret_cast<struct VideoContext*>(data);
|
2020-06-28 16:39:18 +00:00
|
|
|
SDL_UnlockSurface(c->surface);
|
|
|
|
SDL_UnlockMutex(c->mutex);
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// VLC wants to display a video frame.
|
2017-11-17 14:58:52 +00:00
|
|
|
static void display(void* /*data*/, void* /*id*/) {
|
2020-06-28 16:39:18 +00:00
|
|
|
// Data to be displayed.
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
2020-10-17 14:06:01 +00:00
|
|
|
VideoVlcComponent::VideoVlcComponent(Window* window)
|
2020-10-11 16:57:37 +00:00
|
|
|
: VideoComponent(window), mMediaPlayer(nullptr), mContext({})
|
2017-01-25 15:00:56 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
// Get an empty texture for rendering the video.
|
|
|
|
mTexture = TextureResource::get("");
|
2017-01-25 15:00:56 +00:00
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
// Make sure VLC has been initialized.
|
2020-10-17 14:06:01 +00:00
|
|
|
setupVLC();
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
VideoVlcComponent::~VideoVlcComponent()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
stopVideo();
|
2020-10-11 16:57:37 +00:00
|
|
|
mTexture.reset();
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
2017-03-25 17:02:28 +00:00
|
|
|
void VideoVlcComponent::setResize(float width, float height)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mTargetSize = Vector2f(width, height);
|
|
|
|
mTargetIsMax = false;
|
|
|
|
mStaticImage.setResize(width, height);
|
|
|
|
resize();
|
2017-03-25 17:02:28 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoVlcComponent::setMaxSize(float width, float height)
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mTargetSize = Vector2f(width, height);
|
|
|
|
mTargetIsMax = true;
|
|
|
|
mStaticImage.setMaxSize(width, height);
|
|
|
|
resize();
|
2017-03-25 17:02:28 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 16:20:25 +00:00
|
|
|
void VideoVlcComponent::setupVLC()
|
|
|
|
{
|
|
|
|
// If VLC hasn't been initialised yet then do it now.
|
|
|
|
if (!mVLC) {
|
|
|
|
const char* args[] = { "--quiet" };
|
|
|
|
|
|
|
|
#if defined(__APPLE__)
|
|
|
|
// It's required to set the VLC_PLUGIN_PATH variable on macOS, or the libVLC
|
|
|
|
// initialization will fail (with no error message).
|
|
|
|
std::string vlcPluginPath = Utils::FileSystem::getExePath() + "/plugins";
|
|
|
|
if (Utils::FileSystem::isDirectory(vlcPluginPath))
|
|
|
|
setenv("VLC_PLUGIN_PATH", vlcPluginPath.c_str(), 1);
|
|
|
|
else
|
|
|
|
setenv("VLC_PLUGIN_PATH", "/Applications/VLC.app/Contents/MacOS/plugins/", 1);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mVLC = libvlc_new(1, args);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoVlcComponent::setupContext()
|
|
|
|
{
|
|
|
|
if (!mContext.valid) {
|
|
|
|
// Create an RGBA surface to render the video into.
|
|
|
|
mContext.surface = SDL_CreateRGBSurface(SDL_SWSURFACE, static_cast<int>(mVideoWidth),
|
|
|
|
static_cast<int>(mVideoHeight), 32, 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff);
|
|
|
|
mContext.mutex = SDL_CreateMutex();
|
|
|
|
mContext.valid = true;
|
|
|
|
resize();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoVlcComponent::freeContext()
|
|
|
|
{
|
|
|
|
if (mContext.valid) {
|
|
|
|
SDL_FreeSurface(mContext.surface);
|
|
|
|
SDL_DestroyMutex(mContext.mutex);
|
|
|
|
mContext.valid = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-25 17:02:28 +00:00
|
|
|
void VideoVlcComponent::resize()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (!mTexture)
|
|
|
|
return;
|
|
|
|
|
2020-11-17 16:20:25 +00:00
|
|
|
const Vector2f textureSize(static_cast<float>(mVideoWidth), static_cast<float>(mVideoHeight));
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
if (textureSize == Vector2f::Zero())
|
|
|
|
return;
|
|
|
|
|
|
|
|
// SVG rasterization is determined by height and rasterization is done in terms of pixels.
|
|
|
|
// If rounding is off enough in the rasterization step (for images with extreme aspect
|
|
|
|
// ratios), it can cause cutoff when the aspect ratio breaks.
|
|
|
|
// So we always make sure the resultant height is an integer to make sure cutoff doesn't
|
2020-11-17 16:20:25 +00:00
|
|
|
// happen, and scale width from that.
|
2020-06-28 16:39:18 +00:00
|
|
|
if (mTargetIsMax) {
|
|
|
|
mSize = textureSize;
|
|
|
|
|
|
|
|
Vector2f resizeScale((mTargetSize.x() / mSize.x()), (mTargetSize.y() / mSize.y()));
|
|
|
|
|
|
|
|
if (resizeScale.x() < resizeScale.y()) {
|
|
|
|
mSize[0] *= resizeScale.x();
|
|
|
|
mSize[1] *= resizeScale.x();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
mSize[0] *= resizeScale.y();
|
|
|
|
mSize[1] *= resizeScale.y();
|
|
|
|
}
|
|
|
|
|
2020-11-17 16:20:25 +00:00
|
|
|
// For SVG rasterization, always calculate width from rounded height.
|
2020-06-28 16:39:18 +00:00
|
|
|
mSize[1] = Math::round(mSize[1]);
|
|
|
|
mSize[0] = (mSize[1] / textureSize.y()) * textureSize.x();
|
|
|
|
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// If both components are set, we just stretch.
|
|
|
|
// If no components are set, we don't resize at all.
|
|
|
|
mSize = mTargetSize == Vector2f::Zero() ? textureSize : mTargetSize;
|
|
|
|
|
|
|
|
// If only one component is set, we resize in a way that maintains aspect ratio.
|
|
|
|
if (!mTargetSize.x() && mTargetSize.y()) {
|
|
|
|
mSize[1] = Math::round(mTargetSize.y());
|
|
|
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
|
|
|
}
|
|
|
|
else if (mTargetSize.x() && !mTargetSize.y()) {
|
|
|
|
mSize[1] = Math::round((mTargetSize.x() / textureSize.x()) * textureSize.y());
|
|
|
|
mSize[0] = (mSize.y() / textureSize.y()) * textureSize.x();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-11-17 16:20:25 +00:00
|
|
|
mTexture->rasterizeAt(static_cast<size_t>(Math::round(mSize.x())),
|
|
|
|
static_cast<size_t>(Math::round(mSize.y())));
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
onSizeChanged();
|
2017-03-25 17:02:28 +00:00
|
|
|
}
|
|
|
|
|
2017-10-28 20:24:35 +00:00
|
|
|
void VideoVlcComponent::render(const Transform4x4f& parentTrans)
|
2017-01-25 15:00:56 +00:00
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
VideoComponent::render(parentTrans);
|
|
|
|
Transform4x4f trans = parentTrans * getTransform();
|
|
|
|
GuiComponent::renderChildren(trans);
|
|
|
|
|
2020-11-16 22:34:08 +00:00
|
|
|
// Check the actual VLC state, i.e. if the video is really playing rather than
|
|
|
|
// still being opened.
|
|
|
|
if (mMediaPlayer && mIsPlaying && !mIsActuallyPlaying) {
|
|
|
|
libvlc_state_t state;
|
|
|
|
state = libvlc_media_player_get_state(mMediaPlayer);
|
|
|
|
if (state == libvlc_Playing)
|
|
|
|
mIsActuallyPlaying = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mIsPlaying && mContext.valid && mIsActuallyPlaying) {
|
|
|
|
unsigned int color;
|
|
|
|
if (mFadeIn < 1) {
|
|
|
|
const unsigned int fadeIn = mFadeIn * 255.0f;
|
2020-12-18 15:49:11 +00:00
|
|
|
color = Renderer::convertRGBAToABGR((fadeIn << 24) |
|
|
|
|
(fadeIn << 16) | (fadeIn << 8) | 255);
|
2020-11-16 22:34:08 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
color = 0xFFFFFFFF;
|
|
|
|
}
|
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
Renderer::Vertex vertices[4];
|
2020-11-16 22:34:08 +00:00
|
|
|
Renderer::setMatrix(parentTrans);
|
|
|
|
|
|
|
|
// Render the black rectangle behind the video.
|
|
|
|
if (mVideoRectangleCoords.size() == 4) {
|
|
|
|
Renderer::drawRect(mVideoRectangleCoords[0], mVideoRectangleCoords[1],
|
2020-11-17 16:20:25 +00:00
|
|
|
mVideoRectangleCoords[2], mVideoRectangleCoords[3], 0x000000FF, 0x000000FF);
|
2020-11-16 22:34:08 +00:00
|
|
|
}
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
vertices[0] = { { 0.0f , 0.0f }, { 0.0f, 0.0f }, color };
|
|
|
|
vertices[1] = { { 0.0f , mSize.y() }, { 0.0f, 1.0f }, color };
|
|
|
|
vertices[2] = { { mSize.x(), 0.0f }, { 1.0f, 0.0f }, color };
|
|
|
|
vertices[3] = { { mSize.x(), mSize.y() }, { 1.0f, 1.0f }, color };
|
|
|
|
|
|
|
|
// Round vertices.
|
|
|
|
for (int i = 0; i < 4; ++i)
|
|
|
|
vertices[i].pos.round();
|
|
|
|
|
|
|
|
// Build a texture for the video frame.
|
2020-11-17 22:06:54 +00:00
|
|
|
mTexture->initFromPixels(reinterpret_cast<unsigned char*>(mContext.surface->pixels),
|
2020-06-28 16:39:18 +00:00
|
|
|
mContext.surface->w, mContext.surface->h);
|
|
|
|
mTexture->bind();
|
|
|
|
|
2020-09-13 11:21:38 +00:00
|
|
|
#if defined(USE_OPENGL_21)
|
|
|
|
// Render scanlines if this option is enabled. However, if this is the video
|
2020-11-16 22:34:08 +00:00
|
|
|
// screensaver, then skip this as screensaver scanline rendering is handled in
|
|
|
|
// SystemScreenSaver as a postprocessing step.
|
2020-09-13 11:21:38 +00:00
|
|
|
if (!mScreensaverMode && Settings::getInstance()->getBool("GamelistVideoScanlines"))
|
|
|
|
vertices[0].shaders = Renderer::SHADER_SCANLINES;
|
|
|
|
#endif
|
|
|
|
|
2020-06-28 16:39:18 +00:00
|
|
|
// Render it.
|
2020-11-16 22:34:08 +00:00
|
|
|
Renderer::setMatrix(trans);
|
2020-09-13 11:21:38 +00:00
|
|
|
Renderer::drawTriangleStrips(&vertices[0], 4, trans);
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
VideoComponent::renderSnapshot(parentTrans);
|
|
|
|
}
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
2020-11-17 16:20:25 +00:00
|
|
|
void VideoVlcComponent::calculateBlackRectangle()
|
2017-01-25 15:00:56 +00:00
|
|
|
{
|
2020-11-17 16:20:25 +00:00
|
|
|
// Calculate the position and size for the black rectangle that will be rendered behind
|
|
|
|
// videos. If the option to display pillarboxes (and letterboxes) is enabled, then this
|
|
|
|
// would extend to the entire md_video area (if above the threshold as defined below) or
|
|
|
|
// otherwise it will exactly match the video size. The reason to add a black rectangle
|
|
|
|
// behind videos in this second instance is that the scanline rendering will make the
|
|
|
|
// video partially transparent so this may avoid some unforseen issues with some themes.
|
|
|
|
if (mVideoAreaPos != 0 && mVideoAreaSize != 0) {
|
|
|
|
mVideoRectangleCoords.clear();
|
|
|
|
|
|
|
|
if (Settings::getInstance()->getBool("GamelistVideoPillarbox")) {
|
|
|
|
float rectHeight;
|
|
|
|
float rectWidth;
|
|
|
|
// Video is in landscape orientation.
|
|
|
|
if (mSize.x() > mSize.y()) {
|
|
|
|
// Checking the Y size should not normally be required as landscape format
|
|
|
|
// should mean the height can't be higher than the max size defined by the
|
|
|
|
// theme. But as the height in mSize is provided by libVLC in integer format
|
|
|
|
// and then scaled, there could be rounding errors that make the video height
|
|
|
|
// slightly higher than allowed. It's only a single pixel or a few pixels, but
|
|
|
|
// it's still visible for some videos.
|
|
|
|
if (mSize.y() < mVideoAreaSize.y() && mSize.y() / mVideoAreaSize.y() < 0.90)
|
|
|
|
rectHeight = mVideoAreaSize.y();
|
|
|
|
else
|
|
|
|
rectHeight = mSize.y();
|
|
|
|
// Don't add a black border that is too narrow, that's what the 0.85 constant
|
|
|
|
// takes care of.
|
|
|
|
if (mSize.x() < mVideoAreaSize.x() && mSize.x() / mVideoAreaSize.x() < 0.85)
|
|
|
|
rectWidth = mVideoAreaSize.x();
|
|
|
|
else
|
|
|
|
rectWidth = mSize.x();
|
2020-11-12 16:13:24 +00:00
|
|
|
}
|
2020-11-17 16:20:25 +00:00
|
|
|
// Video is in portrait orientation (or completely square).
|
2020-11-12 16:13:24 +00:00
|
|
|
else {
|
2020-11-17 16:20:25 +00:00
|
|
|
rectWidth = mVideoAreaSize.x();
|
|
|
|
rectHeight = mSize.y();
|
2020-11-12 16:13:24 +00:00
|
|
|
}
|
2020-11-17 16:20:25 +00:00
|
|
|
// Populate the rectangle coordinates to be used in render().
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(mVideoAreaPos.x() -
|
|
|
|
rectWidth * mOrigin.x()));
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(mVideoAreaPos.y() -
|
|
|
|
rectHeight * mOrigin.y()));
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(rectWidth));
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(rectHeight));
|
|
|
|
}
|
|
|
|
// If the option to display pillarboxes is disabled, then make the rectangle equivalent
|
|
|
|
// to the size of the video.
|
|
|
|
else {
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(mPosition.x() - mSize.x() * mOrigin.x()));
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(mPosition.y() - mSize.y() * mOrigin.y()));
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(mSize.x()));
|
|
|
|
mVideoRectangleCoords.push_back(Math::round(mSize.y()));
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
2020-07-18 11:21:44 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-01-25 15:00:56 +00:00
|
|
|
void VideoVlcComponent::startVideo()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
if (!mIsPlaying) {
|
|
|
|
mVideoWidth = 0;
|
|
|
|
mVideoHeight = 0;
|
|
|
|
|
2020-08-23 15:04:30 +00:00
|
|
|
#if defined(_WIN64)
|
2020-06-28 16:39:18 +00:00
|
|
|
std::string path(Utils::String::replace(mVideoPath, "/", "\\"));
|
|
|
|
#else
|
|
|
|
std::string path(mVideoPath);
|
|
|
|
#endif
|
|
|
|
// Make sure we have a video path.
|
|
|
|
if (mVLC && (path.size() > 0)) {
|
|
|
|
// Set the video that we are going to be playing so we don't attempt to restart it.
|
|
|
|
mPlayingVideoPath = mVideoPath;
|
|
|
|
|
|
|
|
// Open the media.
|
|
|
|
mMedia = libvlc_media_new_path(mVLC, path.c_str());
|
|
|
|
if (mMedia) {
|
|
|
|
unsigned track_count;
|
|
|
|
int parseResult;
|
|
|
|
libvlc_event_t vlcEvent;
|
|
|
|
|
|
|
|
// Asynchronous media parsing.
|
2020-11-16 22:34:08 +00:00
|
|
|
libvlc_event_attach(libvlc_media_event_manager(mMedia),
|
|
|
|
libvlc_MediaParsedChanged, VlcMediaParseCallback, 0);
|
2020-06-28 16:39:18 +00:00
|
|
|
parseResult = libvlc_media_parse_with_options(mMedia, libvlc_media_parse_local, -1);
|
|
|
|
|
|
|
|
if (!parseResult) {
|
|
|
|
// Wait for a maximum of 1 second for the media parsing.
|
|
|
|
for (int i = 0; i < 200; i++) {
|
|
|
|
if (libvlc_media_get_parsed_status(mMedia))
|
|
|
|
break;
|
|
|
|
SDL_Delay(5);
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
libvlc_media_track_t** tracks;
|
|
|
|
track_count = libvlc_media_tracks_get(mMedia, &tracks);
|
|
|
|
for (unsigned track = 0; track < track_count; ++track) {
|
|
|
|
if (tracks[track]->i_type == libvlc_track_video) {
|
|
|
|
mVideoWidth = tracks[track]->video->i_width;
|
|
|
|
mVideoHeight = tracks[track]->video->i_height;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
libvlc_media_tracks_release(tracks, track_count);
|
|
|
|
|
|
|
|
// Make sure we found a valid video track.
|
|
|
|
if ((mVideoWidth > 0) && (mVideoHeight > 0)) {
|
|
|
|
setupContext();
|
|
|
|
|
|
|
|
// Setup the media player.
|
|
|
|
mMediaPlayer = libvlc_media_player_new_from_media(mMedia);
|
|
|
|
|
2020-07-27 19:31:02 +00:00
|
|
|
if ((!Settings::getInstance()->getBool("GamelistVideoAudio") &&
|
|
|
|
!mScreensaverMode) ||
|
2020-11-05 17:18:11 +00:00
|
|
|
(!Settings::getInstance()->getBool("ScreensaverVideoAudio") &&
|
2020-07-27 19:31:02 +00:00
|
|
|
mScreensaverMode))
|
2020-06-28 16:39:18 +00:00
|
|
|
libvlc_audio_set_mute(mMediaPlayer, 1);
|
|
|
|
|
|
|
|
libvlc_media_player_play(mMediaPlayer);
|
|
|
|
libvlc_video_set_callbacks(mMediaPlayer, lock, unlock, display,
|
2020-11-17 16:20:25 +00:00
|
|
|
reinterpret_cast<void*>(&mContext));
|
|
|
|
libvlc_video_set_format(mMediaPlayer, "RGBA", static_cast<int>(mVideoWidth),
|
|
|
|
static_cast<int>(mVideoHeight), static_cast<int>(mVideoWidth * 4));
|
2020-06-28 16:39:18 +00:00
|
|
|
|
|
|
|
// Update the playing state.
|
|
|
|
mIsPlaying = true;
|
|
|
|
mFadeIn = 0.0f;
|
|
|
|
}
|
2020-11-16 22:34:08 +00:00
|
|
|
if (mIsPlaying) {
|
2020-11-17 16:20:25 +00:00
|
|
|
calculateBlackRectangle();
|
2020-11-16 22:34:08 +00:00
|
|
|
}
|
2020-06-28 16:39:18 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void VideoVlcComponent::stopVideo()
|
|
|
|
{
|
2020-06-28 16:39:18 +00:00
|
|
|
mIsPlaying = false;
|
2020-11-16 22:34:08 +00:00
|
|
|
mIsActuallyPlaying = false;
|
2020-06-28 16:39:18 +00:00
|
|
|
mStartDelayed = false;
|
2020-07-18 11:21:44 +00:00
|
|
|
mPause = false;
|
2020-06-28 16:39:18 +00:00
|
|
|
// Release the media player so it stops calling back to us.
|
|
|
|
if (mMediaPlayer) {
|
|
|
|
libvlc_media_player_stop(mMediaPlayer);
|
|
|
|
libvlc_media_player_release(mMediaPlayer);
|
|
|
|
libvlc_media_release(mMedia);
|
|
|
|
mMediaPlayer = nullptr;
|
|
|
|
freeContext();
|
|
|
|
}
|
2017-01-25 15:00:56 +00:00
|
|
|
}
|
2020-11-17 16:20:25 +00:00
|
|
|
|
|
|
|
void VideoVlcComponent::pauseVideo()
|
|
|
|
{
|
|
|
|
// If a game has been launched and the flag to pause the video has been
|
|
|
|
// set, then rewind and pause.
|
|
|
|
if (!mPause || !mMediaPlayer)
|
|
|
|
return;
|
|
|
|
|
|
|
|
if (libvlc_media_player_get_state(mMediaPlayer) == libvlc_Playing) {
|
|
|
|
libvlc_media_player_set_position(mMediaPlayer, 0.0f);
|
|
|
|
libvlc_media_player_pause(mMediaPlayer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void VideoVlcComponent::handleLooping()
|
|
|
|
{
|
|
|
|
if (mIsPlaying && mMediaPlayer) {
|
|
|
|
libvlc_state_t state = libvlc_media_player_get_state(mMediaPlayer);
|
|
|
|
if (state == libvlc_Ended) {
|
|
|
|
// 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 {
|
|
|
|
libvlc_media_player_set_media(mMediaPlayer, mMedia);
|
|
|
|
|
|
|
|
if ((!Settings::getInstance()->getBool("GamelistVideoAudio") &&
|
|
|
|
!mScreensaverMode) || (!Settings::getInstance()->
|
|
|
|
getBool("ScreensaverVideoAudio") && mScreensaverMode))
|
|
|
|
libvlc_audio_set_mute(mMediaPlayer, 1);
|
|
|
|
|
|
|
|
libvlc_media_player_play(mMediaPlayer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|