work on libmpv
This commit is contained in:
parent
88bf4e7a69
commit
061f422bb8
10 changed files with 458 additions and 192 deletions
|
@ -167,6 +167,7 @@
|
||||||
#define LOGSEC_JS "javascript: "
|
#define LOGSEC_JS "javascript: "
|
||||||
#define LOGSEC_GUI "gui: "
|
#define LOGSEC_GUI "gui: "
|
||||||
#define LOGSEC_MPV "libmpv: "
|
#define LOGSEC_MPV "libmpv: "
|
||||||
|
#define LOGSEC_QTMULTIMEDIA "qtmultimedia: "
|
||||||
#define LOGSEC_NOTIFICATIONS "notifications: "
|
#define LOGSEC_NOTIFICATIONS "notifications: "
|
||||||
#define LOGSEC_CORE "core: "
|
#define LOGSEC_CORE "core: "
|
||||||
#define LOGSEC_NODEJS "nodejs: "
|
#define LOGSEC_NODEJS "nodejs: "
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
#include "gui/mediaplayer/libmpv/libmpvbackend.h"
|
#include "gui/mediaplayer/libmpv/libmpvbackend.h"
|
||||||
|
|
||||||
|
#include "3rd-party/boolinq/boolinq.h"
|
||||||
#include "definitions/definitions.h"
|
#include "definitions/definitions.h"
|
||||||
#include "gui/mediaplayer/libmpv/qthelper.h"
|
#include "gui/mediaplayer/libmpv/qthelper.h"
|
||||||
|
|
||||||
|
@ -11,10 +12,20 @@
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <QJsonArray>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QKeyEvent>
|
#include <QKeyEvent>
|
||||||
#include <QLayout>
|
#include <QLayout>
|
||||||
|
|
||||||
|
#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) {
|
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
|
||||||
|
@ -26,6 +37,8 @@ static void wakeup(void* ctx) {
|
||||||
|
|
||||||
LibMpvBackend::LibMpvBackend(QWidget* parent)
|
LibMpvBackend::LibMpvBackend(QWidget* parent)
|
||||||
: PlayerBackend(parent), m_mpvContainer(new QWidget(this)), m_mpvHandle(nullptr) {
|
: PlayerBackend(parent), m_mpvContainer(new QWidget(this)), m_mpvHandle(nullptr) {
|
||||||
|
installEventFilter(this);
|
||||||
|
|
||||||
m_mpvHandle = mpv_create();
|
m_mpvHandle = mpv_create();
|
||||||
|
|
||||||
if (m_mpvHandle == nullptr) {
|
if (m_mpvHandle == nullptr) {
|
||||||
|
@ -80,11 +93,19 @@ LibMpvBackend::LibMpvBackend(QWidget* parent)
|
||||||
// mpv_set_option_string(mpv, "input-vo-keyboard", "yes");
|
// mpv_set_option_string(mpv, "input-vo-keyboard", "yes");
|
||||||
|
|
||||||
// Observe some properties.
|
// Observe some properties.
|
||||||
mpv_observe_property(m_mpvHandle, 0, "time-pos", MPV_FORMAT_DOUBLE);
|
// 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, "track-list", MPV_FORMAT_NODE);
|
||||||
mpv_observe_property(m_mpvHandle, 0, "chapter-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, "duration", MPV_FORMAT_NODE);
|
||||||
mpv_observe_property(m_mpvHandle, 0, "volume", 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
|
// From this point on, the wakeup function will be called. The callback
|
||||||
// can come from any thread, so we use the QueuedConnection mechanism to
|
// 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) {
|
if (m_mpvHandle != nullptr) {
|
||||||
mpv_terminate_destroy(m_mpvHandle);
|
mpv_terminate_destroy(m_mpvHandle);
|
||||||
|
m_mpvHandle = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LibMpvBackend::~LibMpvBackend() {
|
||||||
|
destroyHandle();
|
||||||
|
}
|
||||||
|
|
||||||
void LibMpvBackend::handleMpvEvent(mpv_event* event) {
|
void LibMpvBackend::handleMpvEvent(mpv_event* event) {
|
||||||
switch (event->event_id) {
|
switch (event->event_id) {
|
||||||
case MPV_EVENT_PROPERTY_CHANGE: {
|
case MPV_EVENT_PROPERTY_CHANGE: {
|
||||||
mpv_event_property* prop = (mpv_event_property*)event->data;
|
mpv_event_property* prop = (mpv_event_property*)event->data;
|
||||||
|
processPropertyChange(prop, event->reply_userdata);
|
||||||
processPropertyChange(prop);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPV_EVENT_VIDEO_RECONFIG: {
|
case MPV_EVENT_COMMAND_REPLY:
|
||||||
// Retrieve the new video size.
|
break;
|
||||||
int64_t w, h;
|
|
||||||
if (mpv_get_property(m_mpvHandle, "dwidth", MPV_FORMAT_INT64, &w) >= 0 &&
|
case MPV_EVENT_VIDEO_RECONFIG:
|
||||||
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()));
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
}
|
|
||||||
|
|
||||||
case MPV_EVENT_LOG_MESSAGE: {
|
case MPV_EVENT_LOG_MESSAGE: {
|
||||||
mpv_event_log_message* msg = (mpv_event_log_message*)event->data;
|
mpv_event_log_message* msg = (mpv_event_log_message*)event->data;
|
||||||
|
|
||||||
processLogMessage(msg);
|
processLogMessage(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
case MPV_EVENT_SHUTDOWN: {
|
case MPV_EVENT_SHUTDOWN: {
|
||||||
mpv_terminate_destroy(m_mpvHandle);
|
destroyHandle();
|
||||||
m_mpvHandle = nullptr;
|
emit closed();
|
||||||
break;
|
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() {
|
void LibMpvBackend::onMpvEvents() {
|
||||||
while (m_mpvHandle != nullptr) {
|
while (m_mpvHandle != nullptr) {
|
||||||
mpv_event* event = mpv_wait_event(m_mpvHandle, 0);
|
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 (strcmp(prop->name, "time-pos") == 0) {
|
||||||
if (prop->format == MPV_FORMAT_DOUBLE) {
|
if (prop->format == MPV_FORMAT_DOUBLE) {
|
||||||
double time = *(double*)prop->data;
|
double time = *(double*)prop->data;
|
||||||
|
@ -184,6 +285,7 @@ void LibMpvBackend::processPropertyChange(mpv_event_property* prop) {
|
||||||
appendLog(d.toJson().data());
|
appendLog(d.toJson().data());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibMpvBackend::processLogMessage(mpv_event_log_message* msg) {
|
void LibMpvBackend::processLogMessage(mpv_event_log_message* msg) {
|
||||||
|
@ -210,59 +312,35 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) {
|
||||||
if (m_mpvHandle != nullptr) {
|
if (m_mpvHandle != nullptr) {
|
||||||
if (event->type() == QEvent::Type::Wheel && watched == this) {
|
if (event->type() == QEvent::Type::Wheel && watched == this) {
|
||||||
auto* mouse_event = dynamic_cast<QWheelEvent*>(event);
|
auto* mouse_event = dynamic_cast<QWheelEvent*>(event);
|
||||||
|
|
||||||
bool is_up = mouse_event->angleDelta().y() >= 0;
|
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};
|
const char* args[] = {"keypress", is_up ? "MOUSE_BTN3" : "MOUSE_BTN4", nullptr};
|
||||||
|
|
||||||
mpv_command_async(m_mpvHandle, 0, args);
|
mpv_command_async(m_mpvHandle, 0, args);
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->type() == QEvent::Type::MouseButtonRelease && watched == this) {
|
if ((event->type() == QEvent::Type::MouseButtonRelease || event->type() == QEvent::Type::MouseButtonPress) &&
|
||||||
auto* mouse_event = dynamic_cast<QMouseEvent*>(event);
|
watched == this) {
|
||||||
auto position = mouse_event->pos();
|
bool press = event->type() == QEvent::Type::MouseButtonPress;
|
||||||
|
|
||||||
qDebugNN << "release";
|
qDebugNN << LOGSEC_MPV << "Mouse press/release.";
|
||||||
|
|
||||||
auto x_str = QString::number(position.x()).toLocal8Bit();
|
const char* args[] = {press ? "keydown" : "keyup", "MOUSE_BTN0", nullptr};
|
||||||
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<QMouseEvent*>(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};
|
|
||||||
|
|
||||||
mpv_command_async(m_mpvHandle, 0, args);
|
mpv_command_async(m_mpvHandle, 0, args);
|
||||||
|
event->accept();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (event->type() == QEvent::Type::MouseMove && watched == this) {
|
if (event->type() == QEvent::Type::MouseMove && watched == this) {
|
||||||
auto* mouse_event = dynamic_cast<QMouseEvent*>(event);
|
auto* mouse_event = dynamic_cast<QMouseEvent*>(event);
|
||||||
auto position = mouse_event->pos();
|
auto position = mouse_event->pos();
|
||||||
|
|
||||||
qDebugNN << "move";
|
|
||||||
|
|
||||||
auto x_str = QString::number(position.x()).toLocal8Bit();
|
auto x_str = QString::number(position.x()).toLocal8Bit();
|
||||||
auto y_str = QString::number(position.y()).toLocal8Bit();
|
auto y_str = QString::number(position.y()).toLocal8Bit();
|
||||||
|
|
||||||
const char* x = x_str.constData();
|
const char* x = x_str.constData();
|
||||||
const char* y = y_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) {
|
if (event->type() == QEvent::Type::KeyPress) {
|
||||||
// We catch all keypresses (even from surrounding widgets.
|
// We catch all keypresses (even from surrounding widgets).
|
||||||
char txt = (char)dynamic_cast<QKeyEvent*>(event)->key();
|
QKeyEvent* key_event = dynamic_cast<QKeyEvent*>(event);
|
||||||
|
char txt = (char)key_event->key();
|
||||||
char str[2];
|
char str[2];
|
||||||
|
|
||||||
str[0] = txt;
|
str[0] = txt;
|
||||||
|
@ -282,7 +361,6 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) {
|
||||||
const char* args[] = {"keypress", str, nullptr};
|
const char* args[] = {"keypress", str, nullptr};
|
||||||
|
|
||||||
mpv_command_async(m_mpvHandle, 0, args);
|
mpv_command_async(m_mpvHandle, 0, args);
|
||||||
|
|
||||||
event->accept();
|
event->accept();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -292,10 +370,12 @@ bool LibMpvBackend::eventFilter(QObject* watched, QEvent* event) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibMpvBackend::playUrl(const QUrl& url) {
|
void LibMpvBackend::playUrl(const QUrl& url) {
|
||||||
auto eb = url.toString().toLocal8Bit();
|
if (m_mpvHandle != nullptr) {
|
||||||
const char* css = eb.data();
|
auto eb = url.toString().toLocal8Bit();
|
||||||
const char* args[] = {"loadfile", css, nullptr};
|
const char* css = eb.data();
|
||||||
mpv_command_async(m_mpvHandle, 0, args);
|
const char* args[] = {"loadfile", css, nullptr};
|
||||||
|
mpv_command_async(m_mpvHandle, 0, args);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibMpvBackend::playPause() {}
|
void LibMpvBackend::playPause() {}
|
||||||
|
@ -306,18 +386,43 @@ void LibMpvBackend::stop() {}
|
||||||
|
|
||||||
void LibMpvBackend::setPlaybackSpeed(int speed) {}
|
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::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 {
|
QUrl LibMpvBackend::url() const {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
int LibMpvBackend::position() const {
|
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 {
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ class LibMpvBackend : public PlayerBackend {
|
||||||
virtual int duration() const;
|
virtual int duration() const;
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
virtual void setMuted(bool muted);
|
||||||
virtual void playUrl(const QUrl& url);
|
virtual void playUrl(const QUrl& url);
|
||||||
virtual void playPause();
|
virtual void playPause();
|
||||||
virtual void pause();
|
virtual void pause();
|
||||||
|
@ -32,6 +33,7 @@ class LibMpvBackend : public PlayerBackend {
|
||||||
virtual void setPlaybackSpeed(int speed);
|
virtual void setPlaybackSpeed(int speed);
|
||||||
virtual void setVolume(int volume);
|
virtual void setVolume(int volume);
|
||||||
virtual void setPosition(int position);
|
virtual void setPosition(int position);
|
||||||
|
virtual void setFullscreen(bool fullscreen);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void onMpvEvents();
|
void onMpvEvents();
|
||||||
|
@ -40,12 +42,20 @@ class LibMpvBackend : public PlayerBackend {
|
||||||
void launchMpvEvents();
|
void launchMpvEvents();
|
||||||
|
|
||||||
private:
|
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 processLogMessage(mpv_event_log_message* msg);
|
||||||
void appendLog(const QString& text);
|
void appendLog(const QString& text);
|
||||||
void createPlayer();
|
void createPlayer();
|
||||||
void handleMpvEvent(mpv_event* event);
|
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:
|
private:
|
||||||
QWidget* m_mpvContainer;
|
QWidget* m_mpvContainer;
|
||||||
mpv_handle* m_mpvHandle;
|
mpv_handle* m_mpvHandle;
|
||||||
|
|
|
@ -8,6 +8,8 @@
|
||||||
#include "gui/mediaplayer/qtmultimedia/qtmultimediabackend.h"
|
#include "gui/mediaplayer/qtmultimedia/qtmultimediabackend.h"
|
||||||
#elif defined(ENABLE_MEDIAPLAYER_LIBMPV)
|
#elif defined(ENABLE_MEDIAPLAYER_LIBMPV)
|
||||||
#include "gui/mediaplayer/libmpv/libmpvbackend.h"
|
#include "gui/mediaplayer/libmpv/libmpvbackend.h"
|
||||||
|
|
||||||
|
#include <QKeyEvent>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MediaPlayer::MediaPlayer(QWidget* parent)
|
MediaPlayer::MediaPlayer(QWidget* parent)
|
||||||
|
@ -21,14 +23,29 @@ MediaPlayer::MediaPlayer(QWidget* parent)
|
||||||
m_muted(false) {
|
m_muted(false) {
|
||||||
m_ui.setupUi(this);
|
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();
|
setupIcons();
|
||||||
createBackendConnections();
|
createBackendConnections();
|
||||||
createConnections();
|
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 {
|
WebBrowser* MediaPlayer::webBrowser() const {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
|
@ -38,9 +55,11 @@ void MediaPlayer::playUrl(const QString& url) {
|
||||||
if (m_muted) {
|
if (m_muted) {
|
||||||
muteUnmute();
|
muteUnmute();
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
else {
|
else {
|
||||||
setVolume(m_ui.m_slidVolume->value());
|
setVolume(m_ui.m_slidVolume->value());
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
m_backend->playUrl(url);
|
m_backend->playUrl(url);
|
||||||
}
|
}
|
||||||
|
@ -57,11 +76,17 @@ void MediaPlayer::download() {
|
||||||
emit urlDownloadRequested(m_backend->url());
|
emit urlDownloadRequested(m_backend->url());
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPlayer::muteUnmute() {
|
void MediaPlayer::onMutedChanged(bool muted) {
|
||||||
m_ui.m_slidVolume->setEnabled(m_muted);
|
m_muted = muted;
|
||||||
setVolume(m_muted ? m_ui.m_slidVolume->value() : 0);
|
|
||||||
|
|
||||||
|
m_ui.m_slidVolume->setEnabled(!muted);
|
||||||
|
m_ui.m_btnVolume->setIcon(muted ? m_iconMute : m_iconUnmute);
|
||||||
|
}
|
||||||
|
|
||||||
|
void MediaPlayer::muteUnmute() {
|
||||||
m_muted = !m_muted;
|
m_muted = !m_muted;
|
||||||
|
|
||||||
|
m_backend->setMuted(m_muted);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPlayer::setSpeed(int speed) {
|
void MediaPlayer::setSpeed(int speed) {
|
||||||
|
@ -86,6 +111,12 @@ void MediaPlayer::onPositionChanged(int position) {
|
||||||
updateTimeAndProgress(position, m_backend->duration());
|
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) {
|
void MediaPlayer::onSpeedChanged(int speed) {
|
||||||
m_ui.m_spinSpeed->blockSignals(true);
|
m_ui.m_spinSpeed->blockSignals(true);
|
||||||
m_ui.m_spinSpeed->setValue(speed);
|
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() {
|
void MediaPlayer::setupIcons() {
|
||||||
m_iconPlay = qApp->icons()->fromTheme(QSL("media-playback-start"), QSL("player_play"));
|
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_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_iconMute = qApp->icons()->fromTheme(QSL("player-volume-muted"), QSL("audio-volume-muted"));
|
||||||
m_iconUnmute = qApp->icons()->fromTheme(QSL("player-volume"), QSL("stock_volume"));
|
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_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")));
|
m_ui.m_btnStop->setIcon(qApp->icons()->fromTheme(QSL("media-playback-stop"), QSL("player_stop")));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MediaPlayer::createBackendConnections() {
|
void MediaPlayer::showPlayerNormal() {
|
||||||
installEventFilter(m_backend);
|
m_ui.m_layoutMain->addWidget(m_ui.m_container);
|
||||||
m_backend->installEventFilter(m_backend);
|
}
|
||||||
|
|
||||||
|
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::speedChanged, this, &MediaPlayer::onSpeedChanged);
|
||||||
|
connect(m_backend, &PlayerBackend::volumeChanged, this, &MediaPlayer::onVolumeChanged);
|
||||||
connect(m_backend, &PlayerBackend::durationChanged, this, &MediaPlayer::onDurationChanged);
|
connect(m_backend, &PlayerBackend::durationChanged, this, &MediaPlayer::onDurationChanged);
|
||||||
connect(m_backend, &PlayerBackend::positionChanged, this, &MediaPlayer::onPositionChanged);
|
connect(m_backend, &PlayerBackend::positionChanged, this, &MediaPlayer::onPositionChanged);
|
||||||
connect(m_backend, &PlayerBackend::errorOccurred, this, &MediaPlayer::onErrorOccurred);
|
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_slidVolume, &QSlider::valueChanged, this, &MediaPlayer::setVolume);
|
||||||
connect(m_ui.m_slidProgress, &QSlider::valueChanged, this, &MediaPlayer::seek);
|
connect(m_ui.m_slidProgress, &QSlider::valueChanged, this, &MediaPlayer::seek);
|
||||||
connect(m_ui.m_spinSpeed, QOverload<int>::of(&QSpinBox::valueChanged), this, &MediaPlayer::setSpeed);
|
connect(m_ui.m_spinSpeed, QOverload<int>::of(&QSpinBox::valueChanged), this, &MediaPlayer::setSpeed);
|
||||||
|
connect(m_ui.m_btnFullscreen, &PlainToolButton::clicked, this, &MediaPlayer::switchFullScreen);
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,9 +37,17 @@ class MediaPlayer : public TabContent {
|
||||||
// NOTE: We seek by second.
|
// NOTE: We seek by second.
|
||||||
void seek(int position);
|
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 onSpeedChanged(int speed);
|
||||||
void onDurationChanged(int duration);
|
void onDurationChanged(int duration);
|
||||||
void onPositionChanged(int position);
|
void onPositionChanged(int position);
|
||||||
|
void onVolumeChanged(int volume);
|
||||||
void onErrorOccurred(const QString& error_string);
|
void onErrorOccurred(const QString& error_string);
|
||||||
void onStatusChanged(const QString& status);
|
void onStatusChanged(const QString& status);
|
||||||
void onPlaybackStateChanged(PlayerBackend::PlaybackState state);
|
void onPlaybackStateChanged(PlayerBackend::PlaybackState state);
|
||||||
|
@ -49,8 +57,11 @@ class MediaPlayer : public TabContent {
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void urlDownloadRequested(const QUrl& url);
|
void urlDownloadRequested(const QUrl& url);
|
||||||
|
void closed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
bool isFullScreen() const;
|
||||||
|
|
||||||
void updateTimeAndProgress(int progress, int total);
|
void updateTimeAndProgress(int progress, int total);
|
||||||
void setupIcons();
|
void setupIcons();
|
||||||
|
|
||||||
|
|
|
@ -14,110 +14,159 @@
|
||||||
<string>Form</string>
|
<string>Form</string>
|
||||||
</property>
|
</property>
|
||||||
<layout class="QVBoxLayout" name="m_layoutMain">
|
<layout class="QVBoxLayout" name="m_layoutMain">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
<item>
|
<item>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout">
|
<widget class="QWidget" name="m_container" native="true">
|
||||||
<item>
|
<layout class="QVBoxLayout" name="m_layoutContainer">
|
||||||
<widget class="LabelWithStatus" name="m_lblStatus" native="true"/>
|
<property name="leftMargin">
|
||||||
</item>
|
<number>0</number>
|
||||||
<item>
|
</property>
|
||||||
<widget class="PlainToolButton" name="m_btnPlayPause">
|
<property name="topMargin">
|
||||||
<property name="toolTip">
|
<number>0</number>
|
||||||
<string>Play/pause</string>
|
</property>
|
||||||
</property>
|
<property name="rightMargin">
|
||||||
</widget>
|
<number>0</number>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
<property name="bottomMargin">
|
||||||
<widget class="PlainToolButton" name="m_btnStop">
|
<number>0</number>
|
||||||
<property name="toolTip">
|
</property>
|
||||||
<string>Stop</string>
|
<item>
|
||||||
</property>
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
</widget>
|
<property name="leftMargin">
|
||||||
</item>
|
<number>9</number>
|
||||||
<item>
|
</property>
|
||||||
<widget class="QSpinBox" name="m_spinSpeed">
|
<property name="topMargin">
|
||||||
<property name="toolTip">
|
<number>9</number>
|
||||||
<string>Speed</string>
|
</property>
|
||||||
</property>
|
<property name="rightMargin">
|
||||||
<property name="accelerated">
|
<number>9</number>
|
||||||
<bool>true</bool>
|
</property>
|
||||||
</property>
|
<property name="bottomMargin">
|
||||||
<property name="correctionMode">
|
<number>9</number>
|
||||||
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
|
</property>
|
||||||
</property>
|
<item>
|
||||||
<property name="suffix">
|
<widget class="LabelWithStatus" name="m_lblStatus" native="true"/>
|
||||||
<string notr="true"> %</string>
|
</item>
|
||||||
</property>
|
<item>
|
||||||
<property name="minimum">
|
<widget class="PlainToolButton" name="m_btnPlayPause">
|
||||||
<number>1</number>
|
<property name="toolTip">
|
||||||
</property>
|
<string>Play/pause</string>
|
||||||
<property name="maximum">
|
</property>
|
||||||
<number>1000</number>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
<property name="singleStep">
|
<item>
|
||||||
<number>10</number>
|
<widget class="PlainToolButton" name="m_btnStop">
|
||||||
</property>
|
<property name="toolTip">
|
||||||
<property name="value">
|
<string>Stop</string>
|
||||||
<number>100</number>
|
</property>
|
||||||
</property>
|
</widget>
|
||||||
</widget>
|
</item>
|
||||||
</item>
|
<item>
|
||||||
<item>
|
<widget class="QSpinBox" name="m_spinSpeed">
|
||||||
<widget class="QSlider" name="m_slidProgress">
|
<property name="toolTip">
|
||||||
<property name="toolTip">
|
<string>Speed</string>
|
||||||
<string>Progress</string>
|
</property>
|
||||||
</property>
|
<property name="accelerated">
|
||||||
<property name="orientation">
|
<bool>true</bool>
|
||||||
<enum>Qt::Horizontal</enum>
|
</property>
|
||||||
</property>
|
<property name="correctionMode">
|
||||||
</widget>
|
<enum>QAbstractSpinBox::CorrectToNearestValue</enum>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
<property name="suffix">
|
||||||
<widget class="QLabel" name="m_lblTime">
|
<string notr="true"> %</string>
|
||||||
<property name="toolTip">
|
</property>
|
||||||
<string>Duration</string>
|
<property name="minimum">
|
||||||
</property>
|
<number>1</number>
|
||||||
</widget>
|
</property>
|
||||||
</item>
|
<property name="maximum">
|
||||||
<item>
|
<number>1000</number>
|
||||||
<widget class="PlainToolButton" name="m_btnVolume">
|
</property>
|
||||||
<property name="toolTip">
|
<property name="singleStep">
|
||||||
<string>Mute/unmute</string>
|
<number>10</number>
|
||||||
</property>
|
</property>
|
||||||
</widget>
|
<property name="value">
|
||||||
</item>
|
<number>100</number>
|
||||||
<item>
|
</property>
|
||||||
<widget class="QSlider" name="m_slidVolume">
|
</widget>
|
||||||
<property name="sizePolicy">
|
</item>
|
||||||
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
<item>
|
||||||
<horstretch>0</horstretch>
|
<widget class="QSlider" name="m_slidProgress">
|
||||||
<verstretch>0</verstretch>
|
<property name="toolTip">
|
||||||
</sizepolicy>
|
<string>Progress</string>
|
||||||
</property>
|
</property>
|
||||||
<property name="toolTip">
|
<property name="orientation">
|
||||||
<string>Volume</string>
|
<enum>Qt::Horizontal</enum>
|
||||||
</property>
|
</property>
|
||||||
<property name="maximum">
|
</widget>
|
||||||
<number>100</number>
|
</item>
|
||||||
</property>
|
<item>
|
||||||
<property name="value">
|
<widget class="QLabel" name="m_lblTime">
|
||||||
<number>50</number>
|
<property name="toolTip">
|
||||||
</property>
|
<string>Duration</string>
|
||||||
<property name="orientation">
|
</property>
|
||||||
<enum>Qt::Horizontal</enum>
|
</widget>
|
||||||
</property>
|
</item>
|
||||||
<property name="tickInterval">
|
<item>
|
||||||
<number>5</number>
|
<widget class="PlainToolButton" name="m_btnVolume">
|
||||||
</property>
|
<property name="toolTip">
|
||||||
</widget>
|
<string>Mute/unmute</string>
|
||||||
</item>
|
</property>
|
||||||
<item>
|
</widget>
|
||||||
<widget class="PlainToolButton" name="m_btnDownload">
|
</item>
|
||||||
<property name="toolTip">
|
<item>
|
||||||
<string>Download</string>
|
<widget class="QSlider" name="m_slidVolume">
|
||||||
</property>
|
<property name="sizePolicy">
|
||||||
</widget>
|
<sizepolicy hsizetype="Maximum" vsizetype="Fixed">
|
||||||
</item>
|
<horstretch>0</horstretch>
|
||||||
</layout>
|
<verstretch>0</verstretch>
|
||||||
|
</sizepolicy>
|
||||||
|
</property>
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Volume</string>
|
||||||
|
</property>
|
||||||
|
<property name="maximum">
|
||||||
|
<number>100</number>
|
||||||
|
</property>
|
||||||
|
<property name="value">
|
||||||
|
<number>50</number>
|
||||||
|
</property>
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Horizontal</enum>
|
||||||
|
</property>
|
||||||
|
<property name="tickInterval">
|
||||||
|
<number>5</number>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="PlainToolButton" name="m_btnDownload">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Download</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="PlainToolButton" name="m_btnFullscreen">
|
||||||
|
<property name="toolTip">
|
||||||
|
<string>Download</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
|
@ -141,7 +190,6 @@
|
||||||
<tabstop>m_slidProgress</tabstop>
|
<tabstop>m_slidProgress</tabstop>
|
||||||
<tabstop>m_btnVolume</tabstop>
|
<tabstop>m_btnVolume</tabstop>
|
||||||
<tabstop>m_slidVolume</tabstop>
|
<tabstop>m_slidVolume</tabstop>
|
||||||
<tabstop>m_btnDownload</tabstop>
|
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
|
|
@ -26,22 +26,29 @@ class PlayerBackend : public QWidget {
|
||||||
virtual int duration() const = 0;
|
virtual int duration() const = 0;
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
|
void closed();
|
||||||
|
void fullscreenChanged(bool fullscreen);
|
||||||
|
void mutedChanged(bool muted);
|
||||||
void speedChanged(int speed);
|
void speedChanged(int speed);
|
||||||
void durationChanged(int duration);
|
void durationChanged(int duration);
|
||||||
void positionChanged(int position);
|
void positionChanged(int position);
|
||||||
void errorOccurred(const QString& error_string);
|
void volumeChanged(int volume);
|
||||||
void statusChanged(const QString& status);
|
|
||||||
void playbackStateChanged(PlaybackState state);
|
|
||||||
void audioAvailable(bool available);
|
void audioAvailable(bool available);
|
||||||
void videoAvailable(bool available);
|
void videoAvailable(bool available);
|
||||||
void seekableChanged(bool seekable);
|
void seekableChanged(bool seekable);
|
||||||
|
|
||||||
|
void errorOccurred(const QString& error_string);
|
||||||
|
void statusChanged(const QString& status);
|
||||||
|
void playbackStateChanged(PlayerBackend::PlaybackState state);
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
virtual void playUrl(const QUrl& url) = 0;
|
virtual void playUrl(const QUrl& url) = 0;
|
||||||
virtual void playPause() = 0;
|
virtual void playPause() = 0;
|
||||||
virtual void pause() = 0;
|
virtual void pause() = 0;
|
||||||
virtual void stop() = 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 setPlaybackSpeed(int speed) = 0;
|
||||||
virtual void setVolume(int volume) = 0;
|
virtual void setVolume(int volume) = 0;
|
||||||
virtual void setPosition(int position) = 0;
|
virtual void setPosition(int position) = 0;
|
||||||
|
|
|
@ -173,8 +173,8 @@ bool TabWidget::closeTab(int index) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TabWidget::closeBrowserTab() {
|
void TabWidget::closeTabWithSender() {
|
||||||
auto idx = indexOf(qobject_cast<WebBrowser*>(sender()));
|
auto idx = indexOf(qobject_cast<QWidget*>(sender()));
|
||||||
|
|
||||||
if (idx >= 0) {
|
if (idx >= 0) {
|
||||||
closeTab(idx);
|
closeTab(idx);
|
||||||
|
@ -240,6 +240,8 @@ int TabWidget::addMediaPlayer(const QString& url, bool make_active) {
|
||||||
qApp->downloadManager(),
|
qApp->downloadManager(),
|
||||||
QOverload<const QUrl&>::of(&DownloadManager::download));
|
QOverload<const QUrl&>::of(&DownloadManager::download));
|
||||||
|
|
||||||
|
connect(player, &MediaPlayer::closed, this, &TabWidget::closeTabWithSender);
|
||||||
|
|
||||||
int index = addTab(player,
|
int index = addTab(player,
|
||||||
qApp->icons()->fromTheme(QSL("player_play"), QSL("media-playback-start")),
|
qApp->icons()->fromTheme(QSL("player_play"), QSL("media-playback-start")),
|
||||||
tr("Media player"),
|
tr("Media player"),
|
||||||
|
@ -291,7 +293,7 @@ int TabWidget::addBrowser(bool move_after_current, bool make_active, WebBrowser*
|
||||||
// Make connections.
|
// Make connections.
|
||||||
connect(browser, &WebBrowser::titleChanged, this, &TabWidget::changeTitle);
|
connect(browser, &WebBrowser::titleChanged, this, &TabWidget::changeTitle);
|
||||||
connect(browser, &WebBrowser::iconChanged, this, &TabWidget::changeIcon);
|
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.
|
// Setup the tab index.
|
||||||
browser->setIndex(final_index);
|
browser->setIndex(final_index);
|
||||||
|
|
|
@ -65,7 +65,7 @@ class TabWidget : public QTabWidget {
|
||||||
|
|
||||||
// Tab closing.
|
// Tab closing.
|
||||||
bool closeTab(int index);
|
bool closeTab(int index);
|
||||||
void closeBrowserTab();
|
void closeTabWithSender();
|
||||||
void closeAllTabsExceptCurrent();
|
void closeAllTabsExceptCurrent();
|
||||||
void closeAllTabs();
|
void closeAllTabs();
|
||||||
void closeCurrentTab();
|
void closeCurrentTab();
|
||||||
|
|
|
@ -100,7 +100,7 @@ void WebViewer::initializeCommonMenuItems() {
|
||||||
QObject::tr("Open in external browser")));
|
QObject::tr("Open in external browser")));
|
||||||
|
|
||||||
m_actionPlayLink.reset(new QAction(qApp->icons()->fromTheme(QSL("player_play"), QSL("media-playback-start")),
|
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)
|
#if !defined(ENABLE_MEDIAPLAYER)
|
||||||
m_actionPlayLink->setText(m_actionPlayLink->text() + QSL(" ") + QObject::tr("(not supported)"));
|
m_actionPlayLink->setText(m_actionPlayLink->text() + QSL(" ") + QObject::tr("(not supported)"));
|
||||||
|
|
Loading…
Add table
Reference in a new issue