Music Hub ..
A session-wide music playback service
Loading...
Searching...
No Matches
media_player2.cpp
Go to the documentation of this file.
1/*
2 * Copyright © 2021-2022 UBports Foundation.
3 *
4 * Contact: Alberto Mardegan <mardy@users.sourceforge.net>
5 *
6 * This program is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU Lesser General Public License version 3,
8 * as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU Lesser General Public License for more details.
14 *
15 * You should have received a copy of the GNU Lesser General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "mpris/media_player2.h"
20
22#include "logging.h"
24
25#include <QDBusAbstractAdaptor>
26#include <QDBusConnection>
27#include <QDBusContext>
28#include <QDBusObjectPath>
29#include <QMetaEnum>
30#include <QString>
31#include <QStringList>
32
34
35using namespace mpris;
36
37namespace mpris {
38
39const QString objectPath = QStringLiteral("/org/mpris/MediaPlayer2");
40
41// Models interface org.mpris.MediaPlayer2, see:
42// http://specifications.freedesktop.org/mpris-spec/latest/Media_Player.html
43// for detailed documentation
45{
46 Q_OBJECT
47 Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2")
48 Q_PROPERTY(bool CanQuit READ falseProperty)
49 Q_PROPERTY(bool Fullscreen READ falseProperty)
50 Q_PROPERTY(bool CanSetFullscreen READ falseProperty)
51 Q_PROPERTY(bool CanRaise READ falseProperty)
52 Q_PROPERTY(bool HasTrackList READ falseProperty)
53 Q_PROPERTY(QString Identity READ identity)
54 Q_PROPERTY(QString DesktopEntry READ desktopEntry)
55 Q_PROPERTY(QStringList SupportedUriSchemes READ supportedUriSchemes)
56 Q_PROPERTY(QStringList SupportedMimeTypes READ supportedMimeTypes)
57
58public:
60 virtual ~RootAdaptor() = default;
61
62 bool falseProperty() const { return false; }
63 QString identity() const { return QStringLiteral("core::media::Hub"); }
64 // TODO: figure out desktop entry of our client instead of hardcoding
65 // Media Player app.
66 QString desktopEntry() const { return QStringLiteral("lomiri-mediaplayer-app"); }
67 QStringList supportedUriSchemes() const { return {}; }
68 QStringList supportedMimeTypes() const {
69 return {"audio/mpeg3", "video/mpeg4"};
70 }
71
72public Q_SLOTS:
73 void Raise() {}
74 void Quit() {}
75};
76
77// Models interface org.mpris.MediaPlayer2.Playlists, see:
78// http://specifications.freedesktop.org/mpris-spec/latest/Playlists_Interface.html
79// for detailed documentation
81{
82 Q_OBJECT
83 Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Playlists")
84 Q_PROPERTY(quint32 PlaylistCount READ playlistCount)
85 Q_PROPERTY(QStringList Orderings READ orderings)
86 // FIXME: There's also an ActivePlaylist property in the specs
87
88public:
90 virtual ~PlaylistsAdaptor() = default;
91
92 quint32 playlistCount() const { return 0; }
93 QStringList orderings() const { return {"Alphabetical"}; }
94
95public Q_SLOTS:
97 void GetPlaylists() {}
98};
99
101{
102 Q_OBJECT
103 Q_CLASSINFO("D-Bus Interface", "org.mpris.MediaPlayer2.Player")
104 Q_PROPERTY(bool CanPlay READ canPlay)
105 Q_PROPERTY(bool CanPause READ canPause)
106 Q_PROPERTY(bool CanSeek READ canSeek)
107 Q_PROPERTY(bool CanGoPrevious READ canGoPrevious)
108 Q_PROPERTY(bool CanGoNext READ canGoNext)
109 Q_PROPERTY(bool CanControl READ canControl)
110 Q_PROPERTY(bool IsVideoSource READ isVideoSource)
111 Q_PROPERTY(bool IsAudioSource READ isAudioSource)
112 Q_PROPERTY(QString PlaybackStatus READ playbackStatus)
113 Q_PROPERTY(QString LoopStatus READ loopStatus WRITE setLoopStatus)
114 Q_PROPERTY(double PlaybackRate READ playbackRate WRITE setPlaybackRate)
115 Q_PROPERTY(bool Shuffle READ shuffle WRITE setShuffle)
116 Q_PROPERTY(QVariantMap Metadata READ metadata)
117 Q_PROPERTY(double Volume READ volume WRITE setVolume)
118 Q_PROPERTY(double MinimumRate READ minimumRate)
119 Q_PROPERTY(double MaximumRate READ maximumRate)
120 Q_PROPERTY(qint64 Position READ position)
121 Q_PROPERTY(qint64 Duration READ duration)
122 Q_PROPERTY(qint16 TypedBackend READ backend)
123 Q_PROPERTY(qint16 Orientation READ orientation)
124 Q_PROPERTY(qint16 Lifetime READ lifetime)
125 Q_PROPERTY(qint16 AudioStreamRole READ audioStreamRole)
126 Q_PROPERTY(qint16 TypedLoopStatus READ typedLoopStatus
127 WRITE setTypedLoopStatus)
128
134 };
135 Q_ENUM(LoopStatus)
136
137 PlayerAdaptor(const QDBusConnection &connection, QObject *parent);
138
140 media::PlayerImplementation *player() { return m_player; }
141 const media::PlayerImplementation *player() const { return m_player; }
143 bool canPlay() const { return m_player ? m_player->canPlay() : false; }
144 bool canPause() const { return m_player ? m_player->canPause() : false; }
145 bool canSeek() const { return m_player ? m_player->canSeek() : false; }
146 bool canGoPrevious() const { return m_player ? m_player->canGoPrevious() : false; }
147 bool canGoNext() const { return m_player ? m_player->canGoNext() : false; }
148 bool canControl() const { return m_player ? true : false; }
149 bool isVideoSource() const { return m_player ? m_player->isVideoSource() : false; }
150 bool isAudioSource() const { return m_player ? m_player->isAudioSource() : false; }
151 QString playbackStatus() const;
152 void setLoopStatus(const QString &status);
153 QString loopStatus() const;
154 void setTypedLoopStatus(qint16 status);
155 qint16 typedLoopStatus() const;
156 void setPlaybackRate(double rate) { if (m_player) m_player->setPlaybackRate(rate); }
157 double playbackRate() const { return m_player ? m_player->playbackRate() : 1.0; }
158 void setShuffle(bool shuffle) { if (m_player) m_player->setShuffle(shuffle); }
159 bool shuffle() const { return m_player ? m_player->shuffle() : false; }
160 QVariantMap metadata() const { return m_player ? m_player->metadataForCurrentTrack() : QVariantMap(); }
161 void setVolume(double volume) { if (m_player) m_player->setVolume(volume); }
162 double volume() const { return m_player ? m_player->volume() : 1.0; }
163 double minimumRate() const { return m_player ? m_player->minimumRate() : 1.0; }
164 double maximumRate() const { return m_player ? m_player->maximumRate() : 1.0; }
165 qint64 position() const { return m_player ? m_player->position() : 0; }
166 qint64 duration() const { return m_player ? m_player->duration() : 0; }
167 qint16 backend() const { return m_player ? m_player->backend() : 0; }
168 qint16 orientation() const { return m_player ? m_player->orientation() : 0; }
169 qint16 lifetime() const { return m_player ? m_player->lifetime() : 0; }
170 qint16 audioStreamRole() const { return m_player ? m_player->audioStreamRole() : 0; }
171
172public Q_SLOTS:
173 void Next() { if (m_player) m_player->next(); }
174 void Previous() { if (m_player) m_player->previous(); }
175 void Pause() { if (m_player) m_player->pause(); }
176 void PlayPause();
177 void Stop() { if (m_player) m_player->stop(); }
178 void Play() { if (m_player) m_player->play(); }
179 void Seek(quint64 microSeconds);
180 void SetPosition(const QDBusObjectPath &, quint64) {} // Never implemented
181 /* The OpenUri should not return anything, but since the previous
182 * implementation was returning a boolean, let's keep doing that. */
183 void OpenUri(const QDBusMessage &);
184
185Q_SIGNALS:
186 void Seeked(quint64 microSeconds);
189 void PlaybackStatusChanged(quint16 status); // TODO: remove, we have PropertiesChanged already
190 void VideoDimensionChanged(quint32 height, quint32 width);
191 void Error(qint16 code);
192 void Buffering(int percent); // TODO: set a fixed type
193
194private:
196 DBusPropertyNotifier m_propertyNotifier;
197};
198
200public:
202
203private:
204 friend class MediaPlayer2;
205 QDBusConnection m_connection;
206 PlayerAdaptor *m_playerAdaptor;
207};
208
209} // namespace mpris
210
211PlayerAdaptor::PlayerAdaptor(const QDBusConnection &connection,
212 QObject *parent):
213 QDBusAbstractAdaptor(parent),
214 m_player(nullptr),
215 m_propertyNotifier(connection, objectPath, this)
216{
217}
218
220{
221 using PlayerImplementation = media::PlayerImplementation;
222
223 if (m_player == impl) return;
224
225 if (m_player) {
226 m_player->disconnect(this);
227 }
228
229 m_player = impl;
230 if (impl) {
231 QObject::connect(impl, &PlayerImplementation::seekedTo,
232 this, &PlayerAdaptor::Seeked);
233
234 /* Property signals */
235 QObject::connect(impl, &PlayerImplementation::mprisPropertiesChanged,
236 this, [this]() {
237 m_propertyNotifier.notify({
238 "CanPlay", "CanPause", "CanSeek",
239 "CanGoPrevious", "CanGoNext",
240 });
241 });
242 QObject::connect(impl, &PlayerImplementation::playbackStatusChanged,
243 this, [this]() {
244 m_propertyNotifier.notify({ "PlaybackStatus" });
245 });
246 QObject::connect(impl, &PlayerImplementation::metadataForCurrentTrackChanged,
247 this, [this]() {
248 m_propertyNotifier.notify({ "Metadata" });
249 });
250 }
251
252 m_propertyNotifier.notify();
253}
254
256{
257 using Player = media::Player;
258 const Player::PlaybackStatus s = player() ?
260 switch (s) {
262 return QStringLiteral("Playing");
264 return QStringLiteral("Paused");
265 default:
266 return QStringLiteral("Stopped");
267 }
268}
269
270void PlayerAdaptor::setLoopStatus(const QString &status)
271{
272 if (!player()) return;
273 bool ok;
274 int value = QMetaEnum::fromType<LoopStatus>().
275 keyToValue(status.toUtf8().constData(), &ok);
276 if (!ok) {
277 MH_ERROR("Invalid loop status: %s", qUtf8Printable(status));
278 return;
279 }
280 player()->setLoopStatus(static_cast<media::Player::LoopStatus>(value));
281}
282
284{
285 int value = player() ? static_cast<LoopStatus>(player()->loopStatus()) : None;
286 return QMetaEnum::fromType<LoopStatus>().valueToKey(value);
287}
288
290{
291 if (!player()) return;
292 player()->setLoopStatus(static_cast<media::Player::LoopStatus>(status));
293}
294
296{
297 return player() ? static_cast<LoopStatus>(player()->loopStatus()) : None;
298}
299
301{
302 if (!m_player) return;
303
304 switch (m_player->playbackStatus()) {
308 m_player->play();
309 break;
311 m_player->pause();
312 break;
313 default:
314 break;
315 }
316}
317
318void PlayerAdaptor::OpenUri(const QDBusMessage &)
319{
320 // FIXME: unused, but we should implement it anyway
321}
322
323void PlayerAdaptor::Seek(quint64 microSeconds)
324{
325 if (!player()) return;
326 player()->seek_to(std::chrono::microseconds(microSeconds));
327}
328
330 m_connection(QDBusConnection::sessionBus()),
331 m_playerAdaptor(new PlayerAdaptor(m_connection, q))
332{
333 new RootAdaptor(q);
334 new PlaylistsAdaptor(q);
335}
336
338 QObject(parent),
339 d_ptr(new MediaPlayer2Private(this))
340{
341}
342
344
346{
347 Q_D(MediaPlayer2);
348 return d->m_playerAdaptor->setPlayer(impl);
349}
350
352{
353 Q_D(MediaPlayer2);
354 return d->m_playerAdaptor->player();
355}
356
358{
359 Q_D(const MediaPlayer2);
360 return d->m_playerAdaptor->player();
361}
362
364{
365 Q_D(MediaPlayer2);
366 return d->m_connection.registerObject(objectPath, this);
367}
368
369#include "media_player2.moc"
void seek_to(const std::chrono::microseconds &offset)
MediaPlayer2Private(MediaPlayer2 *q)
virtual ~MediaPlayer2()
lomiri::MediaHubService::PlayerImplementation * player()
void setPlayer(lomiri::MediaHubService::PlayerImplementation *impl)
MediaPlayer2(QObject *parent=nullptr)
qint16 lifetime() const
qint16 orientation() const
media::PlayerImplementation * player()
qint64 position() const
double maximumRate() const
void setShuffle(bool shuffle)
double minimumRate() const
void setVolume(double volume)
void VideoDimensionChanged(quint32 height, quint32 width)
void OpenUri(const QDBusMessage &)
void setTypedLoopStatus(qint16 status)
void Error(qint16 code)
void setLoopStatus(const QString &status)
qint16 audioStreamRole() const
double playbackRate() const
QVariantMap metadata() const
void SetPosition(const QDBusObjectPath &, quint64)
void Buffering(int percent)
void setPlaybackRate(double rate)
void setPlayer(media::PlayerImplementation *impl)
void PlaybackStatusChanged(quint16 status)
void Seeked(quint64 microSeconds)
QString playbackStatus() const
PlayerAdaptor(const QDBusConnection &connection, QObject *parent)
qint16 typedLoopStatus() const
void Seek(quint64 microSeconds)
QString loopStatus() const
qint64 duration() const
const media::PlayerImplementation * player() const
virtual ~PlaylistsAdaptor()=default
PlaylistsAdaptor(QObject *parent)
quint32 playlistCount() const
QStringList orderings() const
QStringList supportedMimeTypes() const
RootAdaptor(QObject *parent)
virtual ~RootAdaptor()=default
QStringList SupportedMimeTypes
QStringList supportedUriSchemes() const
bool falseProperty() const
QStringList SupportedUriSchemes
QString desktopEntry() const
QString identity() const
#define MH_ERROR(...)
Definition logging.h:41
Definition mpris.h:28
const QString objectPath
static constexpr const char * paused
Definition mpris.h:182
static constexpr const char * playing
Definition mpris.h:181
static constexpr const char * stopped
Definition mpris.h:183