From 061f422bb8cd2b1cc3dbe5440eb796d534ed1f98 Mon Sep 17 00:00:00 2001 From: Martin Rotter Date: Wed, 29 Nov 2023 12:45:05 +0100 Subject: [PATCH] work on libmpv --- src/librssguard/definitions/definitions.h | 1 + .../gui/mediaplayer/libmpv/libmpvbackend.cpp | 247 ++++++++++++----- .../gui/mediaplayer/libmpv/libmpvbackend.h | 12 +- .../gui/mediaplayer/mediaplayer.cpp | 98 ++++++- src/librssguard/gui/mediaplayer/mediaplayer.h | 11 + .../gui/mediaplayer/mediaplayer.ui | 256 +++++++++++------- .../gui/mediaplayer/playerbackend.h | 13 +- src/librssguard/gui/tabwidget.cpp | 8 +- src/librssguard/gui/tabwidget.h | 2 +- src/librssguard/gui/webviewers/webviewer.cpp | 2 +- 10 files changed, 458 insertions(+), 192 deletions(-) diff --git a/src/librssguard/definitions/definitions.h b/src/librssguard/definitions/definitions.h index d2ff82979..aae5aa890 100644 --- a/src/librssguard/definitions/definitions.h +++ b/src/librssguard/definitions/definitions.h @@ -167,6 +167,7 @@ #define LOGSEC_JS "javascript: " #define LOGSEC_GUI "gui: " #define LOGSEC_MPV "libmpv: " +#define LOGSEC_QTMULTIMEDIA "qtmultimedia: " #define LOGSEC_NOTIFICATIONS "notifications: " #define LOGSEC_CORE "core: " #define LOGSEC_NODEJS "nodejs: " diff --git a/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.cpp b/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.cpp index 49433c73e..fb01421de 100644 --- a/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.cpp +++ b/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.cpp @@ -2,6 +2,7 @@ #include "gui/mediaplayer/libmpv/libmpvbackend.h" +#include "3rd-party/boolinq/boolinq.h" #include "definitions/definitions.h" #include "gui/mediaplayer/libmpv/qthelper.h" @@ -11,10 +12,20 @@ #include #include +#include #include #include #include +#define EVENT_CODE_FS 2 +#define EVENT_CODE_VOLUME 3 +#define EVENT_CODE_DURATION 4 +#define EVENT_CODE_MUTE 5 +#define EVENT_CODE_POSITION 6 +#define EVENT_CODE_SPEED 7 +#define EVENT_CODE_SEEKABLE 8 +#define EVENT_CODE_TRACKS 9 + static void wakeup(void* ctx) { // This callback is invoked from any mpv thread (but possibly also // recursively from a thread that is calling the mpv API). Just notify @@ -26,6 +37,8 @@ static void wakeup(void* ctx) { LibMpvBackend::LibMpvBackend(QWidget* parent) : PlayerBackend(parent), m_mpvContainer(new QWidget(this)), m_mpvHandle(nullptr) { + installEventFilter(this); + m_mpvHandle = mpv_create(); if (m_mpvHandle == nullptr) { @@ -80,11 +93,19 @@ LibMpvBackend::LibMpvBackend(QWidget* parent) // mpv_set_option_string(mpv, "input-vo-keyboard", "yes"); // Observe some properties. - mpv_observe_property(m_mpvHandle, 0, "time-pos", MPV_FORMAT_DOUBLE); - mpv_observe_property(m_mpvHandle, 0, "track-list", MPV_FORMAT_NODE); - mpv_observe_property(m_mpvHandle, 0, "chapter-list", MPV_FORMAT_NODE); - mpv_observe_property(m_mpvHandle, 0, "duration", MPV_FORMAT_NODE); - mpv_observe_property(m_mpvHandle, 0, "volume", MPV_FORMAT_NODE); + // mpv_observe_property(m_mpvHandle, 0, "time-pos", MPV_FORMAT_DOUBLE); + // mpv_observe_property(m_mpvHandle, 0, "track-list", MPV_FORMAT_NODE); + // mpv_observe_property(m_mpvHandle, 0, "chapter-list", MPV_FORMAT_NODE); + // mpv_observe_property(m_mpvHandle, 0, "duration", MPV_FORMAT_NODE); + // mpv_observe_property(m_mpvHandle, 0, "volume", MPV_FORMAT_NODE); + mpv_observe_property(m_mpvHandle, EVENT_CODE_FS, "fullscreen", MPV_FORMAT_FLAG); + mpv_observe_property(m_mpvHandle, EVENT_CODE_VOLUME, "volume", MPV_FORMAT_INT64); + mpv_observe_property(m_mpvHandle, EVENT_CODE_DURATION, "duration", MPV_FORMAT_INT64); + mpv_observe_property(m_mpvHandle, EVENT_CODE_MUTE, "mute", MPV_FORMAT_FLAG); + mpv_observe_property(m_mpvHandle, EVENT_CODE_POSITION, "time-pos", MPV_FORMAT_INT64); + mpv_observe_property(m_mpvHandle, EVENT_CODE_SPEED, "speed", MPV_FORMAT_DOUBLE); + mpv_observe_property(m_mpvHandle, EVENT_CODE_SEEKABLE, "seekable", MPV_FORMAT_FLAG); + mpv_observe_property(m_mpvHandle, EVENT_CODE_TRACKS, "track-list", MPV_FORMAT_NODE); // From this point on, the wakeup function will be called. The callback // can come from any thread, so we use the QueuedConnection mechanism to @@ -102,47 +123,39 @@ LibMpvBackend::LibMpvBackend(QWidget* parent) } } -LibMpvBackend::~LibMpvBackend() { +void LibMpvBackend::destroyHandle() { if (m_mpvHandle != nullptr) { mpv_terminate_destroy(m_mpvHandle); + m_mpvHandle = nullptr; } } +LibMpvBackend::~LibMpvBackend() { + destroyHandle(); +} + void LibMpvBackend::handleMpvEvent(mpv_event* event) { switch (event->event_id) { case MPV_EVENT_PROPERTY_CHANGE: { mpv_event_property* prop = (mpv_event_property*)event->data; - - processPropertyChange(prop); + processPropertyChange(prop, event->reply_userdata); break; } - case MPV_EVENT_VIDEO_RECONFIG: { - // Retrieve the new video size. - int64_t w, h; - if (mpv_get_property(m_mpvHandle, "dwidth", MPV_FORMAT_INT64, &w) >= 0 && - mpv_get_property(m_mpvHandle, "dheight", MPV_FORMAT_INT64, &h) >= 0 && w > 0 && h > 0) { - // Note that the MPV_EVENT_VIDEO_RECONFIG event doesn't necessarily - // imply a resize, and you should check yourself if the video - // dimensions really changed. - // mpv itself will scale/letter box the video to the container size - // if the video doesn't fit. - std::stringstream ss; - ss << "Reconfig: " << w << " " << h; - // statusBar()->showMessage(QString::fromStdString(ss.str())); - } + case MPV_EVENT_COMMAND_REPLY: + break; + + case MPV_EVENT_VIDEO_RECONFIG: break; - } case MPV_EVENT_LOG_MESSAGE: { mpv_event_log_message* msg = (mpv_event_log_message*)event->data; - processLogMessage(msg); } case MPV_EVENT_SHUTDOWN: { - mpv_terminate_destroy(m_mpvHandle); - m_mpvHandle = nullptr; + destroyHandle(); + emit closed(); break; } @@ -152,6 +165,22 @@ void LibMpvBackend::handleMpvEvent(mpv_event* event) { } } +const char* LibMpvBackend::mpvDecodeString(void* data) const { + return *(char**)data; +} + +bool LibMpvBackend::mpvDecodeBool(void* data) const { + return mpvDecodeInt(data) != 0; +} + +int LibMpvBackend::mpvDecodeInt(void* data) const { + return *(int*)data; +} + +double LibMpvBackend::mpvDecodeDouble(void* data) const { + return *(double*)data; +} + void LibMpvBackend::onMpvEvents() { while (m_mpvHandle != nullptr) { mpv_event* event = mpv_wait_event(m_mpvHandle, 0); @@ -164,7 +193,79 @@ void LibMpvBackend::onMpvEvents() { } } -void LibMpvBackend::processPropertyChange(mpv_event_property* prop) { +void LibMpvBackend::processTracks(const QJsonDocument& json) { + QVariantList vars = json.array().toVariantList(); + auto linq = boolinq::from(vars); + + bool any_audio_track = linq.any([](const QVariant& var) { + return var.toHash().value("type") == QSL("audio"); + }); + + bool any_video_track = linq.any([](const QVariant& var) { + return var.toHash().value("type") == QSL("video"); + }); + + int a = 7; +} + +void LibMpvBackend::processPropertyChange(mpv_event_property* prop, uint64_t property_code) { + if (prop == nullptr || prop->data == nullptr) { + return; + } + + switch (property_code) { + case EVENT_CODE_FS: + emit fullscreenChanged(mpvDecodeBool(prop->data)); + break; + + case EVENT_CODE_VOLUME: { + int volume = mpvDecodeInt(prop->data); + emit volumeChanged(volume); + break; + } + + case EVENT_CODE_POSITION: { + int pos = mpvDecodeInt(prop->data); + emit positionChanged(pos); + break; + } + + case EVENT_CODE_DURATION: { + int dr = mpvDecodeInt(prop->data); + emit durationChanged(dr); + break; + } + + case EVENT_CODE_SEEKABLE: + emit seekableChanged(mpvDecodeBool(prop->data)); + break; + + case EVENT_CODE_SPEED: { + double sp = mpvDecodeDouble(prop->data); + emit speedChanged(std::min(100, int(sp * 100))); + break; + } + + case EVENT_CODE_TRACKS: { + if (prop->format == MPV_FORMAT_NODE) { + QVariant v = mpv::qt::node_to_variant((mpv_node*)prop->data); + QJsonDocument d = QJsonDocument::fromVariant(v); + + processTracks(d); + } + + break; + } + + case EVENT_CODE_MUTE: + emit mutedChanged(mpvDecodeBool(prop->data)); + break; + + default: + break; + } + + /* if (strcmp(prop->name, "time-pos") == 0) { if (prop->format == MPV_FORMAT_DOUBLE) { double time = *(double*)prop->data; @@ -184,6 +285,7 @@ void LibMpvBackend::processPropertyChange(mpv_event_property* prop) { appendLog(d.toJson().data()); } } + */ } void LibMpvBackend::processLogMessage(mpv_event_log_message* msg) { @@ -210,59 +312,35 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) { if (m_mpvHandle != nullptr) { if (event->type() == QEvent::Type::Wheel && watched == this) { auto* mouse_event = dynamic_cast(event); - bool is_up = mouse_event->angleDelta().y() >= 0; - qDebugNN << "scroll: " << is_up; + qDebugNN << LOGSEC_MPV << "Wheel:" << QUOTE_W_SPACE_DOT(is_up); const char* args[] = {"keypress", is_up ? "MOUSE_BTN3" : "MOUSE_BTN4", nullptr}; mpv_command_async(m_mpvHandle, 0, args); + event->accept(); + return true; } - if (event->type() == QEvent::Type::MouseButtonRelease && watched == this) { - auto* mouse_event = dynamic_cast(event); - auto position = mouse_event->pos(); + if ((event->type() == QEvent::Type::MouseButtonRelease || event->type() == QEvent::Type::MouseButtonPress) && + watched == this) { + bool press = event->type() == QEvent::Type::MouseButtonPress; - qDebugNN << "release"; + qDebugNN << LOGSEC_MPV << "Mouse press/release."; - auto x_str = QString::number(position.x()).toLocal8Bit(); - auto y_str = QString::number(position.y()).toLocal8Bit(); - - const char* x = x_str.constData(); - const char* y = y_str.constData(); - - const char* args[] = {"keyup", "MOUSE_BTN0", nullptr}; - - mpv_command_async(m_mpvHandle, 0, args); - } - - if (event->type() == QEvent::Type::MouseButtonPress && watched == this) { - auto* mouse_event = dynamic_cast(event); - auto position = mouse_event->pos(); - - qDebugNN << "press"; - - auto x_str = QString::number(position.x()).toLocal8Bit(); - auto y_str = QString::number(position.y()).toLocal8Bit(); - - const char* x = x_str.constData(); - const char* y = y_str.constData(); - - const char* args[] = {"keydown", "MOUSE_BTN0", nullptr}; + const char* args[] = {press ? "keydown" : "keyup", "MOUSE_BTN0", nullptr}; mpv_command_async(m_mpvHandle, 0, args); + event->accept(); + return true; } if (event->type() == QEvent::Type::MouseMove && watched == this) { auto* mouse_event = dynamic_cast(event); auto position = mouse_event->pos(); - - qDebugNN << "move"; - auto x_str = QString::number(position.x()).toLocal8Bit(); auto y_str = QString::number(position.y()).toLocal8Bit(); - const char* x = x_str.constData(); const char* y = y_str.constData(); @@ -272,8 +350,9 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) { } if (event->type() == QEvent::Type::KeyPress) { - // We catch all keypresses (even from surrounding widgets. - char txt = (char)dynamic_cast(event)->key(); + // We catch all keypresses (even from surrounding widgets). + QKeyEvent* key_event = dynamic_cast(event); + char txt = (char)key_event->key(); char str[2]; str[0] = txt; @@ -282,7 +361,6 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) { const char* args[] = {"keypress", str, nullptr}; mpv_command_async(m_mpvHandle, 0, args); - event->accept(); return true; } @@ -292,10 +370,12 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) { } void LibMpvBackend::playUrl(const QUrl& url) { - auto eb = url.toString().toLocal8Bit(); - const char* css = eb.data(); - const char* args[] = {"loadfile", css, nullptr}; - mpv_command_async(m_mpvHandle, 0, args); + if (m_mpvHandle != nullptr) { + auto eb = url.toString().toLocal8Bit(); + const char* css = eb.data(); + const char* args[] = {"loadfile", css, nullptr}; + mpv_command_async(m_mpvHandle, 0, args); + } } void LibMpvBackend::playPause() {} @@ -306,18 +386,43 @@ void LibMpvBackend::stop() {} void LibMpvBackend::setPlaybackSpeed(int speed) {} -void LibMpvBackend::setVolume(int volume) {} +void LibMpvBackend::setVolume(int volume) { + if (m_mpvHandle != nullptr) { + uint64_t vol = volume; + mpv_set_property_async(m_mpvHandle, EVENT_CODE_VOLUME, "volume", MPV_FORMAT_INT64, &vol); + } +} void LibMpvBackend::setPosition(int position) {} +void LibMpvBackend::setFullscreen(bool fullscreen) { + if (m_mpvHandle != nullptr) { + const char* fs = fullscreen ? "yes" : "no"; + mpv_set_property_async(m_mpvHandle, EVENT_CODE_FS, "fullscreen", MPV_FORMAT_STRING, &fs); + } +} + QUrl LibMpvBackend::url() const { return {}; } int LibMpvBackend::position() const { - return 0; + uint64_t out; + mpv_get_property(m_mpvHandle, "time-pos", MPV_FORMAT_INT64, &out); + + return out; } int LibMpvBackend::duration() const { - return 0; + uint64_t out; + mpv_get_property(m_mpvHandle, "duration", MPV_FORMAT_INT64, &out); + + return out; +} + +void LibMpvBackend::setMuted(bool muted) { + if (m_mpvHandle != nullptr) { + const char* mtd = muted ? "yes" : "no"; + mpv_set_property_async(m_mpvHandle, EVENT_CODE_MUTE, "mute", MPV_FORMAT_STRING, &mtd); + } } diff --git a/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.h b/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.h index 6451dbaec..e9f13d570 100644 --- a/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.h +++ b/src/librssguard/gui/mediaplayer/libmpv/libmpvbackend.h @@ -25,6 +25,7 @@ class LibMpvBackend : public PlayerBackend { virtual int duration() const; public slots: + virtual void setMuted(bool muted); virtual void playUrl(const QUrl& url); virtual void playPause(); virtual void pause(); @@ -32,6 +33,7 @@ class LibMpvBackend : public PlayerBackend { virtual void setPlaybackSpeed(int speed); virtual void setVolume(int volume); virtual void setPosition(int position); + virtual void setFullscreen(bool fullscreen); private slots: void onMpvEvents(); @@ -40,12 +42,20 @@ class LibMpvBackend : public PlayerBackend { void launchMpvEvents(); private: - void processPropertyChange(mpv_event_property* prop); + void processTracks(const QJsonDocument& json); + void processPropertyChange(mpv_event_property* prop, uint64_t property_code); void processLogMessage(mpv_event_log_message* msg); void appendLog(const QString& text); void createPlayer(); void handleMpvEvent(mpv_event* event); + const char* mpvDecodeString(void* data) const; + bool mpvDecodeBool(void* data) const; + int mpvDecodeInt(void* data) const; + double mpvDecodeDouble(void* data) const; + + void destroyHandle(); + private: QWidget* m_mpvContainer; mpv_handle* m_mpvHandle; diff --git a/src/librssguard/gui/mediaplayer/mediaplayer.cpp b/src/librssguard/gui/mediaplayer/mediaplayer.cpp index efa693c86..76d720ab2 100644 --- a/src/librssguard/gui/mediaplayer/mediaplayer.cpp +++ b/src/librssguard/gui/mediaplayer/mediaplayer.cpp @@ -8,6 +8,8 @@ #include "gui/mediaplayer/qtmultimedia/qtmultimediabackend.h" #elif defined(ENABLE_MEDIAPLAYER_LIBMPV) #include "gui/mediaplayer/libmpv/libmpvbackend.h" + +#include #endif MediaPlayer::MediaPlayer(QWidget* parent) @@ -21,14 +23,29 @@ MediaPlayer::MediaPlayer(QWidget* parent) m_muted(false) { m_ui.setupUi(this); - m_ui.m_layoutMain->insertWidget(0, m_backend, 1); + m_ui.m_container->setWindowFlags(Qt::WindowType::Widget | Qt::WindowType::FramelessWindowHint); + m_ui.m_layoutContainer->insertWidget(0, m_backend, 1); + showPlayerNormal(); setupIcons(); createBackendConnections(); createConnections(); + + // Create default state. + onAudioAvailable(true); + onVideoAvailable(true); + onMutedChanged(false); + onPositionChanged(0); + onDurationChanged(100); + onSeekableChanged(true); + onSpeedChanged(100); + onVolumeChanged(50); + onStatusChanged(tr("Starting")); } -MediaPlayer::~MediaPlayer() {} +MediaPlayer::~MediaPlayer() { + m_backend->deleteLater(); +} WebBrowser* MediaPlayer::webBrowser() const { return nullptr; @@ -38,9 +55,11 @@ void MediaPlayer::playUrl(const QString& url) { if (m_muted) { muteUnmute(); } + /* else { setVolume(m_ui.m_slidVolume->value()); } + */ m_backend->playUrl(url); } @@ -57,11 +76,17 @@ void MediaPlayer::download() { emit urlDownloadRequested(m_backend->url()); } -void MediaPlayer::muteUnmute() { - m_ui.m_slidVolume->setEnabled(m_muted); - setVolume(m_muted ? m_ui.m_slidVolume->value() : 0); +void MediaPlayer::onMutedChanged(bool muted) { + m_muted = muted; + m_ui.m_slidVolume->setEnabled(!muted); + m_ui.m_btnVolume->setIcon(muted ? m_iconMute : m_iconUnmute); +} + +void MediaPlayer::muteUnmute() { m_muted = !m_muted; + + m_backend->setMuted(m_muted); } void MediaPlayer::setSpeed(int speed) { @@ -86,6 +111,12 @@ void MediaPlayer::onPositionChanged(int position) { updateTimeAndProgress(position, m_backend->duration()); } +void MediaPlayer::onVolumeChanged(int volume) { + m_ui.m_slidVolume->blockSignals(true); + m_ui.m_slidVolume->setValue(volume); + m_ui.m_slidVolume->blockSignals(false); +} + void MediaPlayer::onSpeedChanged(int speed) { m_ui.m_spinSpeed->blockSignals(true); m_ui.m_spinSpeed->setValue(speed); @@ -149,21 +180,71 @@ void MediaPlayer::onSeekableChanged(bool seekable) { } } +bool MediaPlayer::isFullScreen() const { + return m_ui.m_container->parent() == nullptr; +} + void MediaPlayer::setupIcons() { m_iconPlay = qApp->icons()->fromTheme(QSL("media-playback-start"), QSL("player_play")); m_iconPause = qApp->icons()->fromTheme(QSL("media-playback-pause"), QSL("player_pause")); m_iconMute = qApp->icons()->fromTheme(QSL("player-volume-muted"), QSL("audio-volume-muted")); m_iconUnmute = qApp->icons()->fromTheme(QSL("player-volume"), QSL("stock_volume")); + m_ui.m_btnFullscreen->setIcon(qApp->icons()->fromTheme(QSL("view-fullscreen"))); m_ui.m_btnDownload->setIcon(qApp->icons()->fromTheme(QSL("download"), QSL("browser-download"))); m_ui.m_btnStop->setIcon(qApp->icons()->fromTheme(QSL("media-playback-stop"), QSL("player_stop"))); } -void MediaPlayer::createBackendConnections() { - installEventFilter(m_backend); - m_backend->installEventFilter(m_backend); +void MediaPlayer::showPlayerNormal() { + m_ui.m_layoutMain->addWidget(m_ui.m_container); +} +void MediaPlayer::showPlayerFullscreen() { + m_ui.m_layoutMain->removeWidget(m_ui.m_container); + + m_ui.m_container->setParent(nullptr); + m_ui.m_container->showFullScreen(); +} + +void MediaPlayer::escapeFromFullscreen() { + m_ui.m_container->showNormal(); + m_ui.m_container->setParent(this); +} + +void MediaPlayer::switchFullScreen(bool send_event_to_backend) { + bool is_fullscreen = isFullScreen(); + + if (is_fullscreen) { + escapeFromFullscreen(); + showPlayerNormal(); + } + else { + showPlayerFullscreen(); + } + + if (send_event_to_backend) { + m_backend->setFullscreen(!is_fullscreen); + } +} + +void MediaPlayer::onFullscreenChanged(bool fullscreen) { + if (isFullScreen() != fullscreen) { + // Fullscreen was changed via OSC directly from backend. + // therefore we need to change it visually to, but + // we do not want to re-trigger the information back to backend. + switchFullScreen(false); + } +} + +void MediaPlayer::createBackendConnections() { + // This is used so backend can catch all keypresses done on player controls. + installEventFilter(m_backend); + + connect(m_backend, &PlayerBackend::mutedChanged, this, &MediaPlayer::onMutedChanged); + connect(m_backend, &PlayerBackend::closed, this, &MediaPlayer::closed); + connect(m_backend, &PlayerBackend::fullscreenChanged, this, &MediaPlayer::onFullscreenChanged); connect(m_backend, &PlayerBackend::speedChanged, this, &MediaPlayer::onSpeedChanged); + connect(m_backend, &PlayerBackend::volumeChanged, this, &MediaPlayer::onVolumeChanged); connect(m_backend, &PlayerBackend::durationChanged, this, &MediaPlayer::onDurationChanged); connect(m_backend, &PlayerBackend::positionChanged, this, &MediaPlayer::onPositionChanged); connect(m_backend, &PlayerBackend::errorOccurred, this, &MediaPlayer::onErrorOccurred); @@ -182,4 +263,5 @@ void MediaPlayer::createConnections() { connect(m_ui.m_slidVolume, &QSlider::valueChanged, this, &MediaPlayer::setVolume); connect(m_ui.m_slidProgress, &QSlider::valueChanged, this, &MediaPlayer::seek); connect(m_ui.m_spinSpeed, QOverload::of(&QSpinBox::valueChanged), this, &MediaPlayer::setSpeed); + connect(m_ui.m_btnFullscreen, &PlainToolButton::clicked, this, &MediaPlayer::switchFullScreen); } diff --git a/src/librssguard/gui/mediaplayer/mediaplayer.h b/src/librssguard/gui/mediaplayer/mediaplayer.h index 22bd81d1e..e49a7fa52 100644 --- a/src/librssguard/gui/mediaplayer/mediaplayer.h +++ b/src/librssguard/gui/mediaplayer/mediaplayer.h @@ -37,9 +37,17 @@ class MediaPlayer : public TabContent { // NOTE: We seek by second. void seek(int position); + void showPlayerNormal(); + void showPlayerFullscreen(); + void escapeFromFullscreen(); + void switchFullScreen(bool send_event_to_backend = true); + + void onFullscreenChanged(bool fullscreen); + void onMutedChanged(bool muted); void onSpeedChanged(int speed); void onDurationChanged(int duration); void onPositionChanged(int position); + void onVolumeChanged(int volume); void onErrorOccurred(const QString& error_string); void onStatusChanged(const QString& status); void onPlaybackStateChanged(PlayerBackend::PlaybackState state); @@ -49,8 +57,11 @@ class MediaPlayer : public TabContent { signals: void urlDownloadRequested(const QUrl& url); + void closed(); private: + bool isFullScreen() const; + void updateTimeAndProgress(int progress, int total); void setupIcons(); diff --git a/src/librssguard/gui/mediaplayer/mediaplayer.ui b/src/librssguard/gui/mediaplayer/mediaplayer.ui index bbabd034d..459be3cb3 100644 --- a/src/librssguard/gui/mediaplayer/mediaplayer.ui +++ b/src/librssguard/gui/mediaplayer/mediaplayer.ui @@ -14,110 +14,159 @@ Form + + 0 + + + 0 + + + 0 + + + 0 + - - - - - - - - Play/pause - - - - - - - Stop - - - - - - - Speed - - - true - - - QAbstractSpinBox::CorrectToNearestValue - - - % - - - 1 - - - 1000 - - - 10 - - - 100 - - - - - - - Progress - - - Qt::Horizontal - - - - - - - Duration - - - - - - - Mute/unmute - - - - - - - - 0 - 0 - - - - Volume - - - 100 - - - 50 - - - Qt::Horizontal - - - 5 - - - - - - - Download - - - - + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + + + Play/pause + + + + + + + Stop + + + + + + + Speed + + + true + + + QAbstractSpinBox::CorrectToNearestValue + + + % + + + 1 + + + 1000 + + + 10 + + + 100 + + + + + + + Progress + + + Qt::Horizontal + + + + + + + Duration + + + + + + + Mute/unmute + + + + + + + + 0 + 0 + + + + Volume + + + 100 + + + 50 + + + Qt::Horizontal + + + 5 + + + + + + + Download + + + + + + + Download + + + + + + + @@ -141,7 +190,6 @@ m_slidProgress m_btnVolume m_slidVolume - m_btnDownload diff --git a/src/librssguard/gui/mediaplayer/playerbackend.h b/src/librssguard/gui/mediaplayer/playerbackend.h index aec232e97..4df9a5107 100644 --- a/src/librssguard/gui/mediaplayer/playerbackend.h +++ b/src/librssguard/gui/mediaplayer/playerbackend.h @@ -26,22 +26,29 @@ class PlayerBackend : public QWidget { virtual int duration() const = 0; signals: + void closed(); + void fullscreenChanged(bool fullscreen); + void mutedChanged(bool muted); void speedChanged(int speed); void durationChanged(int duration); void positionChanged(int position); - void errorOccurred(const QString& error_string); - void statusChanged(const QString& status); - void playbackStateChanged(PlaybackState state); + void volumeChanged(int volume); void audioAvailable(bool available); void videoAvailable(bool available); void seekableChanged(bool seekable); + void errorOccurred(const QString& error_string); + void statusChanged(const QString& status); + void playbackStateChanged(PlayerBackend::PlaybackState state); + public slots: virtual void playUrl(const QUrl& url) = 0; virtual void playPause() = 0; virtual void pause() = 0; virtual void stop() = 0; + virtual void setFullscreen(bool fullscreen) = 0; + virtual void setMuted(bool muted) = 0; virtual void setPlaybackSpeed(int speed) = 0; virtual void setVolume(int volume) = 0; virtual void setPosition(int position) = 0; diff --git a/src/librssguard/gui/tabwidget.cpp b/src/librssguard/gui/tabwidget.cpp index e9c50b834..5cd3db648 100644 --- a/src/librssguard/gui/tabwidget.cpp +++ b/src/librssguard/gui/tabwidget.cpp @@ -173,8 +173,8 @@ bool TabWidget::closeTab(int index) { } } -void TabWidget::closeBrowserTab() { - auto idx = indexOf(qobject_cast(sender())); +void TabWidget::closeTabWithSender() { + auto idx = indexOf(qobject_cast(sender())); if (idx >= 0) { closeTab(idx); @@ -240,6 +240,8 @@ int TabWidget::addMediaPlayer(const QString& url, bool make_active) { qApp->downloadManager(), QOverload::of(&DownloadManager::download)); + connect(player, &MediaPlayer::closed, this, &TabWidget::closeTabWithSender); + int index = addTab(player, qApp->icons()->fromTheme(QSL("player_play"), QSL("media-playback-start")), tr("Media player"), @@ -291,7 +293,7 @@ int TabWidget::addBrowser(bool move_after_current, bool make_active, WebBrowser* // Make connections. connect(browser, &WebBrowser::titleChanged, this, &TabWidget::changeTitle); connect(browser, &WebBrowser::iconChanged, this, &TabWidget::changeIcon); - connect(browser, &WebBrowser::windowCloseRequested, this, &TabWidget::closeBrowserTab); + connect(browser, &WebBrowser::windowCloseRequested, this, &TabWidget::closeTabWithSender); // Setup the tab index. browser->setIndex(final_index); diff --git a/src/librssguard/gui/tabwidget.h b/src/librssguard/gui/tabwidget.h index e519c47a1..32ea4c0af 100644 --- a/src/librssguard/gui/tabwidget.h +++ b/src/librssguard/gui/tabwidget.h @@ -65,7 +65,7 @@ class TabWidget : public QTabWidget { // Tab closing. bool closeTab(int index); - void closeBrowserTab(); + void closeTabWithSender(); void closeAllTabsExceptCurrent(); void closeAllTabs(); void closeCurrentTab(); diff --git a/src/librssguard/gui/webviewers/webviewer.cpp b/src/librssguard/gui/webviewers/webviewer.cpp index 6943c6a5e..2a50315e0 100644 --- a/src/librssguard/gui/webviewers/webviewer.cpp +++ b/src/librssguard/gui/webviewers/webviewer.cpp @@ -100,7 +100,7 @@ void WebViewer::initializeCommonMenuItems() { QObject::tr("Open in external browser"))); m_actionPlayLink.reset(new QAction(qApp->icons()->fromTheme(QSL("player_play"), QSL("media-playback-start")), - QObject::tr("Play link as audio/video"))); + QObject::tr("Play in media player"))); #if !defined(ENABLE_MEDIAPLAYER) m_actionPlayLink->setText(m_actionPlayLink->text() + QSL(" ") + QObject::tr("(not supported)"));