add optional opengl-based rendering for libmpv
This commit is contained in:
parent
a242564f9c
commit
61e15f9042
@ -125,6 +125,7 @@ option(FORCE_BUNDLE_ICONS "Forcibly bundle icon themes into RSS Guard." OFF)
|
|||||||
option(ENABLE_COMPRESSED_SITEMAP "Enable support for gzip-compressed sitemap feeds. Requires zlib." OFF)
|
option(ENABLE_COMPRESSED_SITEMAP "Enable support for gzip-compressed sitemap feeds. Requires zlib." OFF)
|
||||||
option(ENABLE_MEDIAPLAYER_QTMULTIMEDIA "Enable built-in media player. Requires QtMultimedia FFMPEG plugin." OFF)
|
option(ENABLE_MEDIAPLAYER_QTMULTIMEDIA "Enable built-in media player. Requires QtMultimedia FFMPEG plugin." OFF)
|
||||||
option(ENABLE_MEDIAPLAYER_LIBMPV "Enable built-in media player. Requires libmpv library." ON)
|
option(ENABLE_MEDIAPLAYER_LIBMPV "Enable built-in media player. Requires libmpv library." ON)
|
||||||
|
option(MEDIAPLAYER_FORCE_OPENGL "Use opengl-based render API with libmpv." ON)
|
||||||
|
|
||||||
# Import Qt libraries.
|
# Import Qt libraries.
|
||||||
set(QT6_MIN_VERSION 6.3.0)
|
set(QT6_MIN_VERSION 6.3.0)
|
||||||
@ -168,6 +169,16 @@ if(ENABLE_MEDIAPLAYER_LIBMPV)
|
|||||||
set(LibMPV_ROOT "${CMAKE_SOURCE_DIR}/resources/scripts/libmpv")
|
set(LibMPV_ROOT "${CMAKE_SOURCE_DIR}/resources/scripts/libmpv")
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(MEDIAPLAYER_FORCE_OPENGL)
|
||||||
|
list(APPEND QT_COMPONENTS OpenGL)
|
||||||
|
|
||||||
|
if(BUILD_WITH_QT6)
|
||||||
|
list(APPEND QT_COMPONENTS OpenGLWidgets)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
add_compile_definitions(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
endif()
|
||||||
|
|
||||||
add_compile_definitions(ENABLE_MEDIAPLAYER_LIBMPV)
|
add_compile_definitions(ENABLE_MEDIAPLAYER_LIBMPV)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
@ -832,13 +832,27 @@ endif()
|
|||||||
|
|
||||||
if(ENABLE_MEDIAPLAYER_QTMULTIMEDIA)
|
if(ENABLE_MEDIAPLAYER_QTMULTIMEDIA)
|
||||||
target_link_libraries(rssguard PUBLIC
|
target_link_libraries(rssguard PUBLIC
|
||||||
|
Qt${QT_VERSION_MAJOR}::OpenGL
|
||||||
Qt${QT_VERSION_MAJOR}::MultimediaWidgets
|
Qt${QT_VERSION_MAJOR}::MultimediaWidgets
|
||||||
)
|
)
|
||||||
elseif(ENABLE_MEDIAPLAYER_LIBMPV)
|
elseif(ENABLE_MEDIAPLAYER_LIBMPV)
|
||||||
|
if(MEDIAPLAYER_FORCE_OPENGL)
|
||||||
|
target_link_libraries(rssguard PUBLIC
|
||||||
|
Qt${QT_VERSION_MAJOR}::OpenGL
|
||||||
|
)
|
||||||
|
|
||||||
|
if(BUILD_WITH_QT6)
|
||||||
|
target_link_libraries(rssguard PUBLIC
|
||||||
|
Qt${QT_VERSION_MAJOR}::OpenGLWidgets
|
||||||
|
)
|
||||||
|
endif()
|
||||||
|
endif()
|
||||||
|
|
||||||
target_include_directories(rssguard AFTER
|
target_include_directories(rssguard AFTER
|
||||||
PRIVATE
|
PRIVATE
|
||||||
${LibMPV_INCLUDE_DIRS}
|
${LibMPV_INCLUDE_DIRS}
|
||||||
)
|
)
|
||||||
|
|
||||||
target_link_libraries(rssguard PUBLIC
|
target_link_libraries(rssguard PUBLIC
|
||||||
${LibMPV_LIBRARIES}
|
${LibMPV_LIBRARIES}
|
||||||
)
|
)
|
||||||
|
@ -48,12 +48,10 @@ LibMpvBackend::LibMpvBackend(Application* app, QWidget* parent)
|
|||||||
|
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
layout()->addWidget(m_mpvContainer);
|
layout()->addWidget(m_mpvContainer);
|
||||||
|
|
||||||
m_mpvContainer->bind();
|
m_mpvContainer->bind();
|
||||||
|
|
||||||
mpv_set_option_string(m_mpvHandle, "msg-level", "all=v");
|
mpv_set_option_string(m_mpvHandle, "msg-level", "all=v");
|
||||||
mpv_set_option_string(m_mpvHandle, "config", "yes");
|
mpv_set_option_string(m_mpvHandle, "config", "yes");
|
||||||
mpv_set_option_string(m_mpvHandle, "force-window", "yes");
|
|
||||||
mpv_set_option_string(m_mpvHandle, "script-opts", "osc-idlescreen=no");
|
mpv_set_option_string(m_mpvHandle, "script-opts", "osc-idlescreen=no");
|
||||||
mpv_set_option_string(m_mpvHandle, "hwdec", "auto");
|
mpv_set_option_string(m_mpvHandle, "hwdec", "auto");
|
||||||
mpv_set_option_string(m_mpvHandle, "osd-playing-msg", "${media-title}");
|
mpv_set_option_string(m_mpvHandle, "osd-playing-msg", "${media-title}");
|
||||||
@ -121,6 +119,7 @@ void LibMpvBackend::loadSettings() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LibMpvBackend::~LibMpvBackend() {
|
LibMpvBackend::~LibMpvBackend() {
|
||||||
|
m_mpvContainer->destroyHandle();
|
||||||
destroyHandle();
|
destroyHandle();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -155,7 +154,7 @@ void LibMpvBackend::handleMpvEvent(mpv_event* event) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
case MPV_EVENT_SHUTDOWN: {
|
case MPV_EVENT_SHUTDOWN: {
|
||||||
destroyHandle();
|
// destroyHandle();
|
||||||
emit closed();
|
emit closed();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,10 @@
|
|||||||
|
|
||||||
#include <mpv/client.h>
|
#include <mpv/client.h>
|
||||||
|
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
#include <QOpenGLContext>
|
||||||
|
#endif
|
||||||
|
|
||||||
static void wakeup(void* ctx) {
|
static void wakeup(void* ctx) {
|
||||||
// This callback is invoked from any mpv thread (but possibly also
|
// This callback is invoked from any mpv thread (but possibly also
|
||||||
// recursively from a thread that is calling the mpv API). Just notify
|
// recursively from a thread that is calling the mpv API). Just notify
|
||||||
@ -13,13 +17,27 @@ static void wakeup(void* ctx) {
|
|||||||
emit backend->launchMpvEvents();
|
emit backend->launchMpvEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
LibMpvWidget::LibMpvWidget(mpv_handle* mpv_handle, QWidget* parent) : QWidget(parent), m_mpvHandle(mpv_handle) {
|
LibMpvWidget::LibMpvWidget(mpv_handle* mpv_handle, QWidget* parent)
|
||||||
|
: BASE_WIDGET(parent), m_mpvHandle(mpv_handle)
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
,
|
||||||
|
m_mpvGl(nullptr)
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
#if !defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
setAttribute(Qt::WidgetAttribute::WA_DontCreateNativeAncestors);
|
setAttribute(Qt::WidgetAttribute::WA_DontCreateNativeAncestors);
|
||||||
setAttribute(Qt::WidgetAttribute::WA_NativeWindow);
|
setAttribute(Qt::WidgetAttribute::WA_NativeWindow);
|
||||||
|
#endif
|
||||||
|
|
||||||
setMouseTracking(true);
|
setMouseTracking(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LibMpvWidget::~LibMpvWidget() {
|
||||||
|
destroyHandle();
|
||||||
|
}
|
||||||
|
|
||||||
void LibMpvWidget::bind() {
|
void LibMpvWidget::bind() {
|
||||||
|
#if !defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
auto raw_wid = winId();
|
auto raw_wid = winId();
|
||||||
|
|
||||||
#if defined(Q_OS_WIN)
|
#if defined(Q_OS_WIN)
|
||||||
@ -31,6 +49,82 @@ void LibMpvWidget::bind() {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
mpv_set_option(m_mpvHandle, "wid", MPV_FORMAT_INT64, &wid);
|
mpv_set_option(m_mpvHandle, "wid", MPV_FORMAT_INT64, &wid);
|
||||||
|
#endif
|
||||||
|
|
||||||
mpv_set_wakeup_callback(m_mpvHandle, wakeup, this);
|
mpv_set_wakeup_callback(m_mpvHandle, wakeup, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LibMpvWidget::destroyHandle() {
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
makeCurrent();
|
||||||
|
|
||||||
|
if (m_mpvGl != nullptr) {
|
||||||
|
mpv_render_context_free(m_mpvGl);
|
||||||
|
m_mpvGl = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
doneCurrent();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
static void* get_proc_address(void* ctx, const char* name) {
|
||||||
|
Q_UNUSED(ctx);
|
||||||
|
|
||||||
|
QOpenGLContext* glctx = QOpenGLContext::currentContext();
|
||||||
|
|
||||||
|
if (!glctx) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return reinterpret_cast<void*>(glctx->getProcAddress(QByteArray(name)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibMpvWidget::maybeUpdate() {
|
||||||
|
// If the Qt window is not visible, Qt's update() will just skip rendering.
|
||||||
|
// This confuses mpv's render API, and may lead to small occasional
|
||||||
|
// freezes due to video rendering timing out.
|
||||||
|
// Handle this by manually redrawing.
|
||||||
|
// Note: Qt doesn't seem to provide a way to query whether update() will
|
||||||
|
// be skipped, and the following code still fails when e.g. switching
|
||||||
|
// to a different workspace with a reparenting window manager.
|
||||||
|
if (window()->isMinimized()) {
|
||||||
|
makeCurrent();
|
||||||
|
paintGL();
|
||||||
|
context()->swapBuffers(context()->surface());
|
||||||
|
doneCurrent();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibMpvWidget::on_update(void* ctx) {
|
||||||
|
QMetaObject::invokeMethod((LibMpvWidget*)ctx, "maybeUpdate");
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibMpvWidget::initializeGL() {
|
||||||
|
mpv_opengl_init_params gl_init_params[1] = {get_proc_address, nullptr};
|
||||||
|
mpv_render_param params[]{{MPV_RENDER_PARAM_API_TYPE, const_cast<char*>(MPV_RENDER_API_TYPE_OPENGL)},
|
||||||
|
{MPV_RENDER_PARAM_OPENGL_INIT_PARAMS, &gl_init_params},
|
||||||
|
{MPV_RENDER_PARAM_INVALID, nullptr}};
|
||||||
|
|
||||||
|
if (mpv_render_context_create(&m_mpvGl, m_mpvHandle, params) < 0) {
|
||||||
|
qFatal("failed to initialize mpv GL context");
|
||||||
|
}
|
||||||
|
|
||||||
|
mpv_render_context_set_update_callback(m_mpvGl, LibMpvWidget::on_update, reinterpret_cast<void*>(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
void LibMpvWidget::paintGL() {
|
||||||
|
mpv_opengl_fbo mpfbo{static_cast<int>(defaultFramebufferObject()), width(), height(), 0};
|
||||||
|
int flip_y{1};
|
||||||
|
|
||||||
|
mpv_render_param params[] = {{MPV_RENDER_PARAM_OPENGL_FBO, &mpfbo},
|
||||||
|
{MPV_RENDER_PARAM_FLIP_Y, &flip_y},
|
||||||
|
{MPV_RENDER_PARAM_INVALID, nullptr}};
|
||||||
|
// See render_gl.h on what OpenGL environment mpv expects, and
|
||||||
|
// other API details.
|
||||||
|
mpv_render_context_render(m_mpvGl, params);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -3,15 +3,27 @@
|
|||||||
#ifndef LIBMPVWIDGET_H
|
#ifndef LIBMPVWIDGET_H
|
||||||
#define LIBMPVWIDGET_H
|
#define LIBMPVWIDGET_H
|
||||||
|
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
#include <QOpenGLWidget>
|
||||||
|
|
||||||
|
#include <mpv/render_gl.h>
|
||||||
|
|
||||||
|
#define BASE_WIDGET QOpenGLWidget
|
||||||
|
#else
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
|
#define BASE_WIDGET QWidget
|
||||||
|
#endif
|
||||||
|
|
||||||
struct mpv_handle;
|
struct mpv_handle;
|
||||||
|
|
||||||
class LibMpvWidget : public QWidget {
|
class LibMpvWidget : public BASE_WIDGET {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
friend class LibMpvBackend;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
explicit LibMpvWidget(mpv_handle* mpv_handle, QWidget* parent = nullptr);
|
explicit LibMpvWidget(mpv_handle* mpv_handle, QWidget* parent = nullptr);
|
||||||
|
virtual ~LibMpvWidget();
|
||||||
|
|
||||||
void bind();
|
void bind();
|
||||||
|
|
||||||
@ -19,7 +31,23 @@ class LibMpvWidget : public QWidget {
|
|||||||
void launchMpvEvents();
|
void launchMpvEvents();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void destroyHandle();
|
||||||
|
|
||||||
mpv_handle* m_mpvHandle;
|
mpv_handle* m_mpvHandle;
|
||||||
|
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
protected:
|
||||||
|
virtual void initializeGL();
|
||||||
|
virtual void paintGL();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void maybeUpdate();
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void on_update(void* ctx);
|
||||||
|
|
||||||
|
mpv_render_context* m_mpvGl;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // LIBMPVWIDGET_H
|
#endif // LIBMPVWIDGET_H
|
||||||
|
@ -266,7 +266,7 @@ int TabWidget::addMediaPlayer(const QString& url, bool make_active) {
|
|||||||
player->setFocus(Qt::FocusReason::OtherFocusReason);
|
player->setFocus(Qt::FocusReason::OtherFocusReason);
|
||||||
}
|
}
|
||||||
|
|
||||||
QTimer::singleShot(500, player, [player, url]() {
|
QTimer::singleShot(3000, player, [player, url]() {
|
||||||
player->playUrl(url);
|
player->playUrl(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -44,6 +44,10 @@
|
|||||||
#include <QThreadPool>
|
#include <QThreadPool>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
#include <QQuickWindow>
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
#if defined(Q_OS_UNIX) && !defined(Q_OS_MACOS)
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#include <QDBusMessage>
|
#include <QDBusMessage>
|
||||||
@ -79,6 +83,16 @@
|
|||||||
|
|
||||||
Application::Application(const QString& id, int& argc, char** argv, const QStringList& raw_cli_args)
|
Application::Application(const QString& id, int& argc, char** argv, const QStringList& raw_cli_args)
|
||||||
: SingleApplication(id, argc, argv), m_rawCliArgs(raw_cli_args), m_updateFeedsLock(new Mutex()) {
|
: SingleApplication(id, argc, argv), m_rawCliArgs(raw_cli_args), m_updateFeedsLock(new Mutex()) {
|
||||||
|
|
||||||
|
#if defined(MEDIAPLAYER_LIBMPV_OPENGL)
|
||||||
|
// HACK: Force rendering system to use OpenGL backend.
|
||||||
|
#if QT_VERSION_MAJOR < 6
|
||||||
|
QQuickWindow::setSceneGraphBackend(QSGRendererInterface::GraphicsApi::OpenGL);
|
||||||
|
#else
|
||||||
|
QQuickWindow::setGraphicsApi(QSGRendererInterface::GraphicsApi::OpenGL);
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
QString custom_ua;
|
QString custom_ua;
|
||||||
|
|
||||||
parseCmdArgumentsFromMyInstance(raw_cli_args, custom_ua);
|
parseCmdArgumentsFromMyInstance(raw_cli_args, custom_ua);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user