rssguard/src/librssguard/gui/mediaplayer/mediaplayer.cpp
2023-11-30 08:15:03 +01:00

273 lines
8.8 KiB
C++

// For license of this file, see <project-root-folder>/LICENSE.md.
#include "gui/mediaplayer/mediaplayer.h"
#include "miscellaneous/iconfactory.h"
#if defined(ENABLE_MEDIAPLAYER_QTMULTIMEDIA)
#include "gui/mediaplayer/qtmultimedia/qtmultimediabackend.h"
#elif defined(ENABLE_MEDIAPLAYER_LIBMPV)
#include "gui/mediaplayer/libmpv/libmpvbackend.h"
#include <QKeyEvent>
#endif
MediaPlayer::MediaPlayer(QWidget* parent)
: TabContent(parent), m_backend(
#if defined(ENABLE_MEDIAPLAYER_QTMULTIMEDIA)
new QtMultimediaBackend(this)
#else
new LibMpvBackend(this)
#endif
),
m_muted(false) {
m_ui.setupUi(this);
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(0);
onSeekableChanged(true);
onSpeedChanged(100);
onVolumeChanged(50);
onStatusChanged(tr("Starting"));
}
MediaPlayer::~MediaPlayer() {
m_backend->deleteLater();
}
WebBrowser* MediaPlayer::webBrowser() const {
return nullptr;
}
void MediaPlayer::playUrl(const QString& url) {
if (m_muted) {
muteUnmute();
}
/*
else {
setVolume(m_ui.m_slidVolume->value());
}
*/
m_backend->playUrl(url);
}
void MediaPlayer::playPause() {
m_backend->playPause();
}
void MediaPlayer::stop() {
m_backend->stop();
}
void MediaPlayer::download() {
emit urlDownloadRequested(m_backend->url());
}
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) {
m_backend->setPlaybackSpeed(speed);
}
void MediaPlayer::setVolume(int volume) {
m_backend->setVolume(volume);
m_ui.m_btnVolume->setIcon(volume <= 0 ? m_iconMute : m_iconUnmute);
}
void MediaPlayer::seek(int position) {
m_backend->setPosition(position);
}
void MediaPlayer::onPositionChanged(int position) {
m_ui.m_slidProgress->blockSignals(true);
m_ui.m_slidProgress->setValue(position);
m_ui.m_slidProgress->blockSignals(false);
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);
m_ui.m_spinSpeed->blockSignals(false);
}
void MediaPlayer::onDurationChanged(int duration) {
m_ui.m_slidProgress->blockSignals(true);
m_ui.m_slidProgress->setMaximum(duration);
m_ui.m_slidProgress->blockSignals(false);
updateTimeAndProgress(m_backend->position(), duration);
}
void MediaPlayer::updateTimeAndProgress(int progress, int total) {
static QString format_hours = QSL("hh:mm:ss");
static QString format_minutes = QSL("mm:ss");
QString format = total >= 3600 ? format_hours : format_minutes;
m_ui.m_lblTime->setText(QSL("%1/%2").arg(QDateTime::fromSecsSinceEpoch(progress).toUTC().toString(format),
QDateTime::fromSecsSinceEpoch(total).toUTC().toString(format)));
}
void MediaPlayer::onErrorOccurred(const QString& error_string) {
m_ui.m_lblStatus->setStatus(WidgetWithStatus::StatusType::Error, error_string, error_string);
}
void MediaPlayer::onAudioAvailable(bool available) {
m_ui.m_slidVolume->setEnabled(available);
m_ui.m_btnVolume->setEnabled(available);
}
void MediaPlayer::onVideoAvailable(bool available) {
Q_UNUSED(available)
}
void MediaPlayer::onStatusChanged(const QString& status) {
m_ui.m_lblStatus->setStatus(WidgetWithStatus::StatusType::Information, status, status);
}
void MediaPlayer::onPlaybackStateChanged(PlayerBackend::PlaybackState state) {
switch (state) {
case PlayerBackend::PlaybackState::StoppedState:
m_ui.m_btnPlayPause->setIcon(m_iconPlay);
m_ui.m_btnStop->setEnabled(false);
break;
case PlayerBackend::PlaybackState::PlayingState:
m_ui.m_btnPlayPause->setIcon(m_iconPause);
m_ui.m_btnStop->setEnabled(true);
break;
case PlayerBackend::PlaybackState::PausedState:
m_ui.m_btnPlayPause->setIcon(m_iconPlay);
m_ui.m_btnStop->setEnabled(true);
break;
}
}
void MediaPlayer::onSeekableChanged(bool seekable) {
m_ui.m_slidProgress->setEnabled(seekable);
if (!seekable) {
onPositionChanged(0);
}
}
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::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);
connect(m_backend, &PlayerBackend::playbackStateChanged, this, &MediaPlayer::onPlaybackStateChanged);
connect(m_backend, &PlayerBackend::statusChanged, this, &MediaPlayer::onStatusChanged);
connect(m_backend, &PlayerBackend::audioAvailable, this, &MediaPlayer::onAudioAvailable);
connect(m_backend, &PlayerBackend::videoAvailable, this, &MediaPlayer::onVideoAvailable);
connect(m_backend, &PlayerBackend::seekableChanged, this, &MediaPlayer::onSeekableChanged);
}
void MediaPlayer::createConnections() {
connect(m_ui.m_btnPlayPause, &PlainToolButton::clicked, this, &MediaPlayer::playPause);
connect(m_ui.m_btnStop, &PlainToolButton::clicked, this, &MediaPlayer::stop);
connect(m_ui.m_btnDownload, &PlainToolButton::clicked, this, &MediaPlayer::download);
connect(m_ui.m_btnVolume, &PlainToolButton::clicked, this, &MediaPlayer::muteUnmute);
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<int>::of(&QSpinBox::valueChanged), this, &MediaPlayer::setSpeed);
connect(m_ui.m_btnFullscreen, &PlainToolButton::clicked, this, [this]() {
switchFullScreen(true);
});
}