[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.
This commit is contained in:
Bart De Vries 2023-05-11 10:51:14 +02:00
parent 52e074e266
commit eac74ecbbb
6 changed files with 72 additions and 9 deletions

View File

@ -224,12 +224,12 @@ qreal AudioManager::playbackRate() const
qreal AudioManager::minimumPlaybackRate() const qreal AudioManager::minimumPlaybackRate() const
{ {
return MIN_RATE; return d->m_player.minimumPlaybackRate();
} }
qreal AudioManager::maximumPlaybackRate() const qreal AudioManager::maximumPlaybackRate() const
{ {
return MAX_RATE; return d->m_player.maximumPlaybackRate();
} }
bool AudioManager::isStreaming() const bool AudioManager::isStreaming() const

View File

@ -51,8 +51,6 @@ class AudioManager : public QObject
Q_PROPERTY(bool isStreaming READ isStreaming NOTIFY isStreamingChanged) Q_PROPERTY(bool isStreaming READ isStreaming NOTIFY isStreamingChanged)
public: public:
const double MAX_RATE = 1.0;
const double MIN_RATE = 2.5;
const qint64 SKIP_TRACK_END = 15000; const qint64 SKIP_TRACK_END = 15000;
static AudioManager &instance() static AudioManager &instance()

View File

@ -193,6 +193,18 @@ qreal KMediaSession::playbackRate() const
return 1.0; 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 KMediaSession::Error KMediaSession::error() const
{ {
qCDebug(KMediaSessionLog) << "KMediaSession::error()"; qCDebug(KMediaSessionLog) << "KMediaSession::error()";
@ -404,6 +416,7 @@ void KMediaSession::setPosition(qint64 position)
d->m_player->setPosition(position); d->m_player->setPosition(position);
QTimer::singleShot(0, this, [this, position]() { QTimer::singleShot(0, this, [this, position]() {
Q_EMIT positionChanged(position); Q_EMIT positionChanged(position);
Q_EMIT positionJumped(position);
}); });
} }
} }
@ -412,7 +425,11 @@ void KMediaSession::setPlaybackRate(qreal rate)
{ {
qCDebug(KMediaSessionLog) << "KMediaSession::setPlaybackRate(" << rate << ")"; qCDebug(KMediaSessionLog) << "KMediaSession::setPlaybackRate(" << rate << ")";
if (d->m_player) { 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);
});
} }
} }

View File

@ -49,6 +49,9 @@ class KMEDIASESSION_EXPORT KMediaSession : public QObject
Q_PROPERTY(bool canGoPrevious READ canGoPrevious WRITE setCanGoPrevious NOTIFY canGoPreviousChanged) Q_PROPERTY(bool canGoPrevious READ canGoPrevious WRITE setCanGoPrevious NOTIFY canGoPreviousChanged)
public: public:
const double MAX_RATE = 3.0;
const double MIN_RATE = 0.1;
enum MediaBackends { enum MediaBackends {
Qt = 0, Qt = 0,
Vlc = 1, Vlc = 1,
@ -103,6 +106,8 @@ public:
[[nodiscard]] KMediaSession::MediaStatus mediaStatus() const; [[nodiscard]] KMediaSession::MediaStatus mediaStatus() const;
[[nodiscard]] KMediaSession::PlaybackState playbackState() const; [[nodiscard]] KMediaSession::PlaybackState playbackState() const;
[[nodiscard]] qreal playbackRate() const; [[nodiscard]] qreal playbackRate() const;
[[nodiscard]] qreal minimumPlaybackRate() const;
[[nodiscard]] qreal maximumPlaybackRate() const;
[[nodiscard]] KMediaSession::Error error() const; [[nodiscard]] KMediaSession::Error error() const;
[[nodiscard]] qint64 duration() const; [[nodiscard]] qint64 duration() const;
[[nodiscard]] qint64 position() const; [[nodiscard]] qint64 position() const;
@ -130,7 +135,8 @@ Q_SIGNALS:
void playbackRateChanged(qreal rate); void playbackRateChanged(qreal rate);
void errorChanged(KMediaSession::Error error); void errorChanged(KMediaSession::Error error);
void durationChanged(qint64 duration); 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 seekableChanged(bool seekable);
void metaDataChanged(MetaData *metadata); void metaDataChanged(MetaData *metadata);

View File

@ -34,7 +34,6 @@ MediaPlayer2Player::MediaPlayer2Player(KMediaSession *audioPlayer, bool showProg
connect(m_audioPlayer, &KMediaSession::sourceChanged, this, &MediaPlayer2Player::setSource); connect(m_audioPlayer, &KMediaSession::sourceChanged, this, &MediaPlayer2Player::setSource);
// Signals from KMediaSession which are directly forwarded // 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 // TODO: implement this in KMediaSession, such that it can be forwarded
// connect(m_audioPlayer, &KMediaSession::minimumRateChanged, // connect(m_audioPlayer, &KMediaSession::minimumRateChanged,
// this, &MediaPlayer2Player::mimimumRateChanged); // this, &MediaPlayer2Player::mimimumRateChanged);
@ -44,8 +43,9 @@ MediaPlayer2Player::MediaPlayer2Player(KMediaSession *audioPlayer, bool showProg
// Signals which are semi-wrapped signals from KMediaSession // Signals which are semi-wrapped signals from KMediaSession
connect(m_audioPlayer, &KMediaSession::playbackStateChanged, this, &MediaPlayer2Player::playerPlaybackStateChanged); 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::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::canPlayChanged, this, &MediaPlayer2Player::playerCanPlayChanged);
connect(m_audioPlayer, &KMediaSession::canPauseChanged, this, &MediaPlayer2Player::playerCanPauseChanged); connect(m_audioPlayer, &KMediaSession::canPauseChanged, this, &MediaPlayer2Player::playerCanPauseChanged);
@ -252,6 +252,24 @@ double MediaPlayer2Player::Rate() const
return 1.0; 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) void MediaPlayer2Player::setRate(double newRate)
{ {
qCDebug(Mpris2Log) << "MediaPlayer2Player::setRate(" << newRate << ")"; qCDebug(Mpris2Log) << "MediaPlayer2Player::setRate(" << newRate << ")";
@ -311,6 +329,13 @@ void MediaPlayer2Player::playerPlaybackStateChanged()
Q_EMIT playbackStatusChanged(); Q_EMIT playbackStatusChanged();
} }
void MediaPlayer2Player::playerPlaybackRateChanged()
{
qCDebug(Mpris2Log) << "MediaPlayer2Player::playerPlaybackRateChanged()";
signalPropertiesChange(QStringLiteral("Rate"), Rate());
// Q_EMIT rateChanged(Rate());
}
void MediaPlayer2Player::playerSeeked(qint64 position) void MediaPlayer2Player::playerSeeked(qint64 position)
{ {
qCDebug(Mpris2Log) << "MediaPlayer2Player::playerSeeked(" << position << ")"; qCDebug(Mpris2Log) << "MediaPlayer2Player::playerSeeked(" << position << ")";
@ -323,6 +348,16 @@ void MediaPlayer2Player::audioPositionChanged()
// for progress indicator on taskbar // for progress indicator on taskbar
if (m_audioPlayer) if (m_audioPlayer)
setPropertyPosition(static_cast<int>(m_audioPlayer->position())); setPropertyPosition(static_cast<int>(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() void MediaPlayer2Player::audioDurationChanged()

View File

@ -24,6 +24,8 @@ class MediaPlayer2Player : public QDBusAbstractAdaptor
Q_PROPERTY(QString PlaybackStatus READ PlaybackStatus NOTIFY playbackStatusChanged) Q_PROPERTY(QString PlaybackStatus READ PlaybackStatus NOTIFY playbackStatusChanged)
Q_PROPERTY(double Rate READ Rate WRITE setRate NOTIFY rateChanged) 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(QVariantMap Metadata READ Metadata NOTIFY playbackStatusChanged)
Q_PROPERTY(double Volume READ Volume WRITE setVolume NOTIFY volumeChanged) Q_PROPERTY(double Volume READ Volume WRITE setVolume NOTIFY volumeChanged)
Q_PROPERTY(qlonglong Position READ Position WRITE setPropertyPosition NOTIFY playbackStatusChanged) Q_PROPERTY(qlonglong Position READ Position WRITE setPropertyPosition NOTIFY playbackStatusChanged)
@ -40,6 +42,8 @@ public:
QString PlaybackStatus() const; QString PlaybackStatus() const;
double Rate() const; double Rate() const;
double MinimumRate() const;
double MaximumRate() const;
QVariantMap Metadata() const; QVariantMap Metadata() const;
double Volume() const; double Volume() const;
qlonglong Position() const; qlonglong Position() const;
@ -56,6 +60,8 @@ Q_SIGNALS:
void Seeked(qlonglong Position); void Seeked(qlonglong Position);
void rateChanged(double newRate); void rateChanged(double newRate);
void minimumRateChanged(double minRate);
void maximumRateChanged(double maxRate);
void volumeChanged(double newVol); void volumeChanged(double newVol);
void playbackStatusChanged(); void playbackStatusChanged();
void canGoNextChanged(); void canGoNextChanged();
@ -84,8 +90,8 @@ public Q_SLOTS:
void OpenUri(const QString &uri); void OpenUri(const QString &uri);
private Q_SLOTS: private Q_SLOTS:
void playerPlaybackStateChanged(); void playerPlaybackStateChanged();
void playerPlaybackRateChanged();
void playerSeeked(qint64 position); void playerSeeked(qint64 position);
void playerVolumeChanged(); void playerVolumeChanged();
void playerCanGoNextChanged(); void playerCanGoNextChanged();
@ -118,4 +124,5 @@ private:
int mPreviousProgressPosition = 0; int mPreviousProgressPosition = 0;
bool mShowProgressOnTaskBar = true; bool mShowProgressOnTaskBar = true;
qlonglong m_position = 0; qlonglong m_position = 0;
qlonglong m_lastSentPosition = 0;
}; };