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; };