From eac74ecbbb4dc4573777b9988ec47ac52fb1201e Mon Sep 17 00:00:00 2001 From: Bart De Vries Date: Thu, 11 May 2023 10:51:14 +0200 Subject: [PATCH] [KMediaSession] Only send MRPIS2 positionChanged signal on seek Stop spamming the DBus with positionChanged signals if playback is progressing at the nominal playback rate. This brings the implementation in line with the MPRIS2 player spec. The previous implementation was causing android battery drain issues with KDEConnect constantly updating the position (and player details) on the notification widget. This implementation will still send the current playback position every 10 seconds to ensure that clients that don't implement the standard properly or did not receive previous messages, will still get regularly updated. This also fixes correct signaling of playback rate changes over DBus. --- src/audiomanager.cpp | 4 +- src/audiomanager.h | 2 - src/kmediasession/kmediasession.cpp | 19 ++++++++- src/kmediasession/kmediasession.h | 8 +++- .../mpris2/mediaplayer2player.cpp | 39 ++++++++++++++++++- src/kmediasession/mpris2/mediaplayer2player.h | 9 ++++- 6 files changed, 72 insertions(+), 9 deletions(-) diff --git a/src/audiomanager.cpp b/src/audiomanager.cpp index 855ee5fa..80e8e19b 100644 --- a/src/audiomanager.cpp +++ b/src/audiomanager.cpp @@ -224,12 +224,12 @@ qreal AudioManager::playbackRate() const qreal AudioManager::minimumPlaybackRate() const { - return MIN_RATE; + return d->m_player.minimumPlaybackRate(); } qreal AudioManager::maximumPlaybackRate() const { - return MAX_RATE; + return d->m_player.maximumPlaybackRate(); } bool AudioManager::isStreaming() const diff --git a/src/audiomanager.h b/src/audiomanager.h index 936a1b9e..5d54611f 100644 --- a/src/audiomanager.h +++ b/src/audiomanager.h @@ -51,8 +51,6 @@ class AudioManager : public QObject Q_PROPERTY(bool isStreaming READ isStreaming NOTIFY isStreamingChanged) public: - const double MAX_RATE = 1.0; - const double MIN_RATE = 2.5; const qint64 SKIP_TRACK_END = 15000; static AudioManager &instance() diff --git a/src/kmediasession/kmediasession.cpp b/src/kmediasession/kmediasession.cpp index 4e7a6fe6..01e91bc3 100644 --- a/src/kmediasession/kmediasession.cpp +++ b/src/kmediasession/kmediasession.cpp @@ -193,6 +193,18 @@ qreal KMediaSession::playbackRate() const return 1.0; } +qreal KMediaSession::minimumPlaybackRate() const +{ + qCDebug(KMediaSessionLog) << "KMediaSession::minimumPlayBackRate()"; + return MIN_RATE; +} + +qreal KMediaSession::maximumPlaybackRate() const +{ + qCDebug(KMediaSessionLog) << "KMediaSession::maximumPlayBackRate()"; + return MAX_RATE; +} + KMediaSession::Error KMediaSession::error() const { qCDebug(KMediaSessionLog) << "KMediaSession::error()"; @@ -404,6 +416,7 @@ void KMediaSession::setPosition(qint64 position) d->m_player->setPosition(position); QTimer::singleShot(0, this, [this, position]() { Q_EMIT positionChanged(position); + Q_EMIT positionJumped(position); }); } } @@ -412,7 +425,11 @@ void KMediaSession::setPlaybackRate(qreal rate) { qCDebug(KMediaSessionLog) << "KMediaSession::setPlaybackRate(" << rate << ")"; if (d->m_player) { - d->m_player->setPlaybackRate(rate); + qreal clippedRate = rate > MAX_RATE ? MAX_RATE : (rate < MIN_RATE ? MIN_RATE : rate); + d->m_player->setPlaybackRate(clippedRate); + QTimer::singleShot(0, this, [this, clippedRate]() { + Q_EMIT playbackRateChanged(clippedRate); + }); } } diff --git a/src/kmediasession/kmediasession.h b/src/kmediasession/kmediasession.h index 2ca6038a..924803c3 100644 --- a/src/kmediasession/kmediasession.h +++ b/src/kmediasession/kmediasession.h @@ -49,6 +49,9 @@ class KMEDIASESSION_EXPORT KMediaSession : public QObject Q_PROPERTY(bool canGoPrevious READ canGoPrevious WRITE setCanGoPrevious NOTIFY canGoPreviousChanged) public: + const double MAX_RATE = 3.0; + const double MIN_RATE = 0.1; + enum MediaBackends { Qt = 0, Vlc = 1, @@ -103,6 +106,8 @@ public: [[nodiscard]] KMediaSession::MediaStatus mediaStatus() const; [[nodiscard]] KMediaSession::PlaybackState playbackState() const; [[nodiscard]] qreal playbackRate() const; + [[nodiscard]] qreal minimumPlaybackRate() const; + [[nodiscard]] qreal maximumPlaybackRate() const; [[nodiscard]] KMediaSession::Error error() const; [[nodiscard]] qint64 duration() const; [[nodiscard]] qint64 position() const; @@ -130,7 +135,8 @@ Q_SIGNALS: void playbackRateChanged(qreal rate); void errorChanged(KMediaSession::Error error); void durationChanged(qint64 duration); - void positionChanged(qint64 position); + void positionChanged(qint64 position); // emitted constantly while playing + void positionJumped(qint64 position); // emitted only if position jumps after explicit seek operation void seekableChanged(bool seekable); void metaDataChanged(MetaData *metadata); diff --git a/src/kmediasession/mpris2/mediaplayer2player.cpp b/src/kmediasession/mpris2/mediaplayer2player.cpp index 6de7d725..6a406ac9 100644 --- a/src/kmediasession/mpris2/mediaplayer2player.cpp +++ b/src/kmediasession/mpris2/mediaplayer2player.cpp @@ -34,7 +34,6 @@ MediaPlayer2Player::MediaPlayer2Player(KMediaSession *audioPlayer, bool showProg connect(m_audioPlayer, &KMediaSession::sourceChanged, this, &MediaPlayer2Player::setSource); // Signals from KMediaSession which are directly forwarded - connect(m_audioPlayer, &KMediaSession::playbackRateChanged, this, &MediaPlayer2Player::rateChanged); // TODO: implement this in KMediaSession, such that it can be forwarded // connect(m_audioPlayer, &KMediaSession::minimumRateChanged, // this, &MediaPlayer2Player::mimimumRateChanged); @@ -44,8 +43,9 @@ MediaPlayer2Player::MediaPlayer2Player(KMediaSession *audioPlayer, bool showProg // Signals which are semi-wrapped signals from KMediaSession connect(m_audioPlayer, &KMediaSession::playbackStateChanged, this, &MediaPlayer2Player::playerPlaybackStateChanged); + connect(m_audioPlayer, &KMediaSession::playbackRateChanged, this, &MediaPlayer2Player::playerPlaybackRateChanged); connect(m_audioPlayer, &KMediaSession::volumeChanged, this, &MediaPlayer2Player::playerVolumeChanged); - connect(m_audioPlayer, &KMediaSession::positionChanged, this, &MediaPlayer2Player::playerSeeked); // Implement Seeked signal + connect(m_audioPlayer, &KMediaSession::positionJumped, this, &MediaPlayer2Player::playerSeeked); // Implement Seeked signal connect(m_audioPlayer, &KMediaSession::canPlayChanged, this, &MediaPlayer2Player::playerCanPlayChanged); connect(m_audioPlayer, &KMediaSession::canPauseChanged, this, &MediaPlayer2Player::playerCanPauseChanged); @@ -252,6 +252,24 @@ double MediaPlayer2Player::Rate() const return 1.0; } +double MediaPlayer2Player::MinimumRate() const +{ + qCDebug(Mpris2Log) << "MediaPlayer2Player::MinimumRate()"; + if (m_audioPlayer) + return m_audioPlayer->minimumPlaybackRate(); + else + return 1.0; +} + +double MediaPlayer2Player::MaximumRate() const +{ + qCDebug(Mpris2Log) << "MediaPlayer2Player::MaximumRate()"; + if (m_audioPlayer) + return m_audioPlayer->maximumPlaybackRate(); + else + return 1.0; +} + void MediaPlayer2Player::setRate(double newRate) { qCDebug(Mpris2Log) << "MediaPlayer2Player::setRate(" << newRate << ")"; @@ -311,6 +329,13 @@ void MediaPlayer2Player::playerPlaybackStateChanged() Q_EMIT playbackStatusChanged(); } +void MediaPlayer2Player::playerPlaybackRateChanged() +{ + qCDebug(Mpris2Log) << "MediaPlayer2Player::playerPlaybackRateChanged()"; + signalPropertiesChange(QStringLiteral("Rate"), Rate()); + // Q_EMIT rateChanged(Rate()); +} + void MediaPlayer2Player::playerSeeked(qint64 position) { qCDebug(Mpris2Log) << "MediaPlayer2Player::playerSeeked(" << position << ")"; @@ -323,6 +348,16 @@ void MediaPlayer2Player::audioPositionChanged() // for progress indicator on taskbar if (m_audioPlayer) setPropertyPosition(static_cast(m_audioPlayer->position())); + + // Occasionally send updated position through MPRIS to make sure that + // audio position is still correct if playing without seeking for a long + // time. This will also guarantee correct playback position if the MPRIS + // client does not support non-standard playback rates + qlonglong position = Position(); + if (abs(position - m_lastSentPosition) > 10000000) { // every 10 seconds + m_lastSentPosition = position; + Q_EMIT Seeked(position); + } } void MediaPlayer2Player::audioDurationChanged() diff --git a/src/kmediasession/mpris2/mediaplayer2player.h b/src/kmediasession/mpris2/mediaplayer2player.h index 64be8953..4053aaea 100644 --- a/src/kmediasession/mpris2/mediaplayer2player.h +++ b/src/kmediasession/mpris2/mediaplayer2player.h @@ -24,6 +24,8 @@ class MediaPlayer2Player : public QDBusAbstractAdaptor Q_PROPERTY(QString PlaybackStatus READ PlaybackStatus NOTIFY playbackStatusChanged) Q_PROPERTY(double Rate READ Rate WRITE setRate NOTIFY rateChanged) + Q_PROPERTY(double MinimumRate READ MinimumRate NOTIFY minimumRateChanged) + Q_PROPERTY(double MaximumRate READ MaximumRate NOTIFY maximumRateChanged) Q_PROPERTY(QVariantMap Metadata READ Metadata NOTIFY playbackStatusChanged) Q_PROPERTY(double Volume READ Volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(qlonglong Position READ Position WRITE setPropertyPosition NOTIFY playbackStatusChanged) @@ -40,6 +42,8 @@ public: QString PlaybackStatus() const; double Rate() const; + double MinimumRate() const; + double MaximumRate() const; QVariantMap Metadata() const; double Volume() const; qlonglong Position() const; @@ -56,6 +60,8 @@ Q_SIGNALS: void Seeked(qlonglong Position); void rateChanged(double newRate); + void minimumRateChanged(double minRate); + void maximumRateChanged(double maxRate); void volumeChanged(double newVol); void playbackStatusChanged(); void canGoNextChanged(); @@ -84,8 +90,8 @@ public Q_SLOTS: void OpenUri(const QString &uri); private Q_SLOTS: - void playerPlaybackStateChanged(); + void playerPlaybackRateChanged(); void playerSeeked(qint64 position); void playerVolumeChanged(); void playerCanGoNextChanged(); @@ -118,4 +124,5 @@ private: int mPreviousProgressPosition = 0; bool mShowProgressOnTaskBar = true; qlonglong m_position = 0; + qlonglong m_lastSentPosition = 0; };