Music Hub ..
A session-wide music playback service
 
Loading...
Searching...
No Matches
player.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 "player.h"
20
21#include "dbus_constants.h"
22#include "dbus_utils.h"
23#include "error_p.h"
24#include "track_list_p.h"
25#include "video_sink_p.h"
26
27#include <QDBusAbstractInterface>
28#include <QDBusMessage>
29#include <QDBusMetaType>
30#include <QDBusPendingCall>
31#include <QDBusPendingCallWatcher>
32#include <QDBusPendingReply>
33#include <QDBusReply>
34#include <QDBusServiceWatcher>
35#include <QDBusVariant>
36#include <QHash>
37#include <QMetaEnum>
38#include <QVariantMap>
39#include <QDebug>
40
41#include <functional>
42#include <stdexcept>
43
44using namespace lomiri::MediaHub;
45
46typedef std::function<void(const QDBusMessage &)> MethodCb;
47typedef std::function<void()> VoidMethodCb;
48
50{
51 Q_OBJECT
52
53public:
55
56 QDBusMessage createSession();
57 void destroySession(const QString &uuid);
58};
59
61{
62 Q_OBJECT
63
64public:
65 DBusPlayer(const QDBusConnection &conn, const QString &path,
66 PlayerPrivate *d);
67
68public Q_SLOTS:
69 void onPropertiesChanged(const QString &interface,
70 const QVariantMap &changed,
71 const QStringList &invalidated);
72 void onVideoDimensionChanged(quint32 height, quint32 width);
73 void onError(quint16 code);
74
75private:
76 PlayerPrivate *d;
77};
78
79namespace lomiri {
80namespace MediaHub {
81
82class PlayerPrivate
83{
84 Q_DECLARE_PUBLIC(Player)
85
86public:
87 PlayerPrivate(Player *q);
88 ~PlayerPrivate();
89
90 // Internal methods
91 void updateProperties(const QVariantMap &properties);
92 void onVideoDimensionChanged(quint32 height, quint32 width);
93 void onError(quint16 dbusCode);
94 void watchErrors(const QDBusPendingCall &call);
95 void onSuccessfulCompletion(const QDBusPendingCall &call,
96 MethodCb callback);
97 void setProperty(const QString &name, const QVariant &value,
98 VoidMethodCb callback = [](){});
99 QVariant getProperty(const QString &name) const;
100
101 // Implementation of public methods
102 void setTrackList(TrackList *trackList);
103 void call(const QString &method,
104 const QVariant &arg1 = QVariant(),
105 const QVariant &arg2 = QVariant());
106 void blockingCall(const QString &method,
107 const QVariant &arg1 = QVariant(),
108 const QVariant &arg2 = QVariant());
109 VideoSink &createGLTextureVideoSink(uint32_t textureId);
110
111private:
112 bool m_canPlay = false;
113 bool m_canPause = false;
114 bool m_canSeek = false;
115 bool m_canGoPrevious = false;
116 bool m_canGoNext = false;
117 bool m_isVideoSource = false;
118 bool m_isAudioSource = false;
119 bool m_shuffle = false;
120 Player::Volume m_volume = 1.0;
121 Player::Orientation m_orientation = Player::Rotate0;
122 Track::MetaData m_metaData;
123
124 Player::PlaybackRate m_playbackRate = 1.0;
125 Player::PlaybackRate m_minimumPlaybackRate = 1.0;
126 Player::PlaybackRate m_maximumPlaybackRate = 1.0;
127
128 Player::PlaybackStatus m_playbackStatus = Player::Null;
129
131 QHash<quint32, VideoSink*> m_videoSinks;
132
133 QString m_uuid;
134 DBusService m_service;
135 QDBusServiceWatcher m_serviceWatcher;
136 QScopedPointer<DBusPlayer> m_proxy;
137 TrackList *m_trackList = nullptr;
138 VideoSinkFactory m_videoSinkFactory;
139 Player *q_ptr;
140};
141
142} // namespace MediaHub
143} // namespace lomiri
144
147 QStringLiteral(MEDIAHUB_SERVICE_PATH),
149 QDBusConnection::sessionBus(),
150 nullptr)
151{
152 setTimeout(1000 /* ms*/);
153}
154
156{
157 return call(QStringLiteral("CreateSession"));
158}
159
160void DBusService::destroySession(const QString &uuid)
161{
162 call(QDBus::NoBlock, QStringLiteral("DestroySession"), uuid);
163}
164
165DBusPlayer::DBusPlayer(const QDBusConnection &conn,
166 const QString &objectPath,
167 PlayerPrivate *d):
169 objectPath,
171 conn,
172 nullptr),
173 d(d)
174{
175}
176
177void DBusPlayer::onPropertiesChanged(const QString &interface,
178 const QVariantMap &changed,
179 const QStringList &invalidated)
180{
181 if (interface == QStringLiteral(MPRIS_PLAYER_INTERFACE)) {
182 d->updateProperties(changed);
183 // we know that our service will never invalidate properties
184 Q_UNUSED(invalidated);
185 }
186}
187
188void DBusPlayer::onVideoDimensionChanged(quint32 height, quint32 width)
189{
190 d->onVideoDimensionChanged(height, width);
191}
192
193void DBusPlayer::onError(quint16 code)
194{
195 d->onError(code);
196}
197
198PlayerPrivate::PlayerPrivate(Player *q):
199 m_serviceWatcher(m_service.service(), m_service.connection()),
200 q_ptr(q)
201{
202 {
203 QDBusMessage reply = m_service.createSession();
204 if (Q_UNLIKELY(reply.type() == QDBusMessage::ErrorMessage)) {
205 throw std::runtime_error(
206 (QStringLiteral("Failed to create session: ") + reply.errorMessage())
207 .toLocal8Bit().constData());
208 }
209 const QString path = reply.arguments()[0].value<QDBusObjectPath>().path();
210 m_uuid = reply.arguments()[1].toString();
211
212 m_proxy.reset(new DBusPlayer(m_service.connection(), path, this));
213 }
214
215 // We'll connect the result later down
216 QDBusPendingReply<quint32> keyCall =
217 m_proxy->asyncCall(QStringLiteral("Key"));
218
219 QObject::connect(&m_serviceWatcher,
220 &QDBusServiceWatcher::serviceRegistered,
222 QObject::connect(&m_serviceWatcher,
223 &QDBusServiceWatcher::serviceUnregistered,
225
226 QDBusConnection c(m_service.connection());
227
228 const QString service = m_proxy->service();
229 const QString path = m_proxy->path();
230 const QString interface = m_proxy->interface();
231
232 c.connect(service, path, QStringLiteral(FDO_PROPERTIES_INTERFACE), QStringLiteral("PropertiesChanged"),
233 m_proxy.data(), SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
234
235 c.connect(service, path, interface, QStringLiteral("Seeked"),
236 q, SLOT(seekedTo(quint64)));
237 c.connect(service, path, interface, QStringLiteral("AboutToFinish"),
238 q, SLOT(aboutToFinish()));
239 c.connect(service, path, interface, QStringLiteral("EndOfStream"),
240 q, SLOT(endOfStream()));
241 c.connect(service, path, interface, QStringLiteral("VideoDimensionChanged"),
242 m_proxy.data(), SLOT(onVideoDimensionChanged(quint32,quint32)));
243 c.connect(service, path, interface, QStringLiteral("Error"),
244 m_proxy.data(), SLOT(onError(quint16)));
245 c.connect(service, path, interface, QStringLiteral("Buffering"),
246 q, SLOT(bufferingChanged(int)));
247
248 // Blocking call to get the initial properties
249 QDBusMessage msg = QDBusMessage::createMethodCall(
250 service, path,
251 QStringLiteral(FDO_PROPERTIES_INTERFACE),
252 QStringLiteral("GetAll"));
253 msg.setArguments({ interface });
254 QDBusMessage reply = c.call(msg);
255 if (Q_UNLIKELY(reply.type()) == QDBusMessage::ErrorMessage) {
256 qWarning() << "Cannot get player properties:" <<
257 reply.errorMessage();
258 } else {
259 QDBusArgument arg = reply.arguments().first().value<QDBusArgument>();
260 updateProperties(qdbus_cast<QVariantMap>(arg));
261 }
262
263 // Collect the result from the Key() call
264 keyCall.waitForFinished();
265 if (Q_UNLIKELY(keyCall.isError())) {
266 qWarning() << "Key() call failed:" << keyCall.error().message();
267 } else {
268 PlayerKey key = keyCall.value();
269 m_videoSinkFactory = createVideoSinkFactory(key, m_backend);
270 }
271}
272
273PlayerPrivate::~PlayerPrivate()
274{
275 m_service.destroySession(m_uuid);
276}
277
278void PlayerPrivate::updateProperties(const QVariantMap &properties)
279{
280 Q_Q(Player);
281
282 bool controlsChanged = false;
283 bool sourceTypeChanged = false;
284
285 for (auto i = properties.begin(); i != properties.end(); i++) {
286 const QString &name = i.key();
287 if (name == "CanPlay") {
288 m_canPlay = i.value().toBool();
289 controlsChanged = true;
290 } else if (name == "CanPause") {
291 m_canPause = i.value().toBool();
292 controlsChanged = true;
293 } else if (name == "CanSeek") {
294 m_canSeek = i.value().toBool();
295 controlsChanged = true;
296 } else if (name == "CanGoPrevious") {
297 m_canGoPrevious = i.value().toBool();
298 controlsChanged = true;
299 } else if (name == "CanGoNext") {
300 m_canGoNext = i.value().toBool();
301 controlsChanged = true;
302 } else if (name == "IsVideoSource") {
303 m_isVideoSource = i.value().toBool();
304 sourceTypeChanged = true;
305 } else if (name == "IsAudioSource") {
306 m_isAudioSource = i.value().toBool();
307 sourceTypeChanged = true;
308 } else if (name == "PlaybackStatus") {
309 bool ok = false;
310 const QString status = i.value().toString();
311 int v = QMetaEnum::fromType<Player::PlaybackStatus>().
312 keyToValue(status.toUtf8().constData(), &ok);
313 if (Q_UNLIKELY(!ok)) {
314 qWarning() << "Unknown player status" << status;
315 } else {
316 m_playbackStatus = static_cast<Player::PlaybackStatus>(v);
317 Q_EMIT q->playbackStatusChanged();
318 }
319 } else if (name == "Metadata") {
320 QDBusArgument arg = i.value().value<QDBusArgument>();
321 // strip the mpris:trackid
322 const QVariantMap dbusMap = qdbus_cast<QVariantMap>(arg);
323 m_metaData.clear();
324 for (auto i = dbusMap.begin(); i != dbusMap.end(); i++) {
325 if (i.key() == QStringLiteral("mpris:trackid")) continue;
326 m_metaData.insert(i.key(), i.value());
327 }
329 } else if (name == "Orientation") {
330 m_orientation =
331 static_cast<Player::Orientation>(i.value().toInt());
332 Q_EMIT q->orientationChanged();
333 } else if (name == "TypedBackend") {
334 m_backend =
335 static_cast<AVBackend::Backend>(i.value().toInt());
336 }
337 }
338
339 if (controlsChanged) {
340 Q_EMIT q->controlsChanged();
341 }
342 if (sourceTypeChanged) {
343 Q_EMIT q->sourceTypeChanged();
344 }
345}
346
347void PlayerPrivate::onVideoDimensionChanged(quint32 height, quint32 width)
348{
349 Q_Q(Player);
350 Q_EMIT q->videoDimensionChanged(QSize(width, height));
351}
352
353void PlayerPrivate::onError(quint16 dbusCode)
354{
355 Q_Q(Player);
356 Error error = errorFromApiCode(dbusCode);
357 if (error.isError()) {
358 Q_EMIT q->errorOccurred(error);
359 }
360}
361
362void PlayerPrivate::watchErrors(const QDBusPendingCall &call)
363{
364 Q_Q(Player);
365 auto watcher = new QDBusPendingCallWatcher(call);
366 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
367 q, [this](QDBusPendingCallWatcher *call) {
368 Q_Q(Player);
369 call->deleteLater();
370 QDBusPendingReply<void> reply(*call);
371 if (reply.isError()) {
372 Q_EMIT q->errorOccurred(errorFromDBus(reply.reply()));
373 }
374 });
375}
376
377void PlayerPrivate::onSuccessfulCompletion(const QDBusPendingCall &call,
378 MethodCb callback)
379{
380 Q_Q(Player);
381 auto watcher = new QDBusPendingCallWatcher(call);
382 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
383 q, [this, callback](QDBusPendingCallWatcher *call) {
384 Q_Q(Player);
385 call->deleteLater();
386 const QDBusMessage reply = QDBusPendingReply<void>(*call).reply();
387 if (Q_UNLIKELY(reply.type()) == QDBusMessage::ErrorMessage) {
388 Q_EMIT q->errorOccurred(errorFromDBus(reply));
389 } else if (callback) {
390 callback(reply);
391 }
392 });
393}
394
395void PlayerPrivate::setProperty(const QString &name, const QVariant &value,
396 VoidMethodCb callback)
397{
398 QDBusMessage msg = QDBusMessage::createMethodCall(
399 m_proxy->service(),
400 m_proxy->path(),
401 QStringLiteral(FDO_PROPERTIES_INTERFACE),
402 QStringLiteral("Set"));
403 msg.setArguments({
404 QStringLiteral(MPRIS_PLAYER_INTERFACE),
405 name,
406 QVariant::fromValue(QDBusVariant(value)),
407 });
408 QDBusPendingCall call = m_proxy->connection().asyncCall(msg);
409 onSuccessfulCompletion(call, [callback](const QDBusMessage &) {
410 callback();
411 });
412}
413
414/* This makes a blocking call: use sparingly! */
415QVariant PlayerPrivate::getProperty(const QString &name) const
416{
417 QDBusMessage msg = QDBusMessage::createMethodCall(
418 m_proxy->service(),
419 m_proxy->path(),
420 QStringLiteral(FDO_PROPERTIES_INTERFACE),
421 QStringLiteral("Get"));
422 msg.setArguments({
423 QStringLiteral(MPRIS_PLAYER_INTERFACE),
424 name,
425 });
426 QVariant ret;
427 QDBusReply<QVariant> reply = m_proxy->connection().call(msg);
428 if (Q_LIKELY(reply.isValid())) {
429 ret = reply.value();
430 } else {
431 qWarning() << "Error getting property" << name << reply.error();
432 }
433 return ret;
434}
435
436void PlayerPrivate::setTrackList(TrackList *trackList)
437{
438 if (trackList != m_trackList) {
439 if (trackList) {
440 trackList->d_ptr->createProxy(m_proxy->connection(),
441 m_proxy->path() + "/TrackList");
442 }
443 m_trackList = trackList;
444 }
445}
446
447void PlayerPrivate::call(const QString &method,
448 const QVariant &arg1, const QVariant &arg2)
449{
450 /*
451 * Qt before 5.14, QDBusAbstractInterface::asyncCall() was simply a function
452 * with predefined 8 arguments. Calls with fewer than that had the rest of
453 * arguments defaulted to invalid QVariant's, and then the code filtered out
454 * invalid QVariants for us.
455 *
456 * On Qt >= 5.14, QDBusAbstractInterface::asyncCall is a variadic function,
457 * and those are resolved at complile time. The code then do no further
458 * validation and send the all passed arguments to the marshaller. This
459 * means we cannot send the default QVariant() straight to asyncCall,
460 * because the DBUS marshaller will refuse it and error out.
461 *
462 * See https://gitlab.com/ubports/development/core/media-hub/-/issues/75
463 * for details.
464 */
465
466 QList<QVariant> args;
467
468 /*
469 * This assumes that no one will call with an invalid arg1 and a valid arg2,
470 * and I'm personnally happy with that assumption.
471 */
472 if (arg1.isValid()) args.append(arg1);
473 if (arg2.isValid()) args.append(arg2);
474
475 QDBusPendingCall call = m_proxy->asyncCallWithArgumentList(method, args);
476 watchErrors(call);
477}
478
479void PlayerPrivate::blockingCall(const QString &method,
480 const QVariant &arg1, const QVariant &arg2)
481{
482 Q_Q(Player);
483 QDBusPendingCall call = m_proxy->asyncCall(method, arg1, arg2);
484 bool ok = DBusUtils::waitForFinished(call);
485 if (Q_UNLIKELY(!ok)) {
486 Q_EMIT q->errorOccurred(errorFromDBus(call.reply()));
487 }
488}
489
490VideoSink &PlayerPrivate::createGLTextureVideoSink(uint32_t textureId)
491{
492 Q_Q(Player);
493 auto i = m_videoSinks.find(textureId);
494 if (i == m_videoSinks.end()) {
495 VideoSink *videoSink = m_videoSinkFactory(textureId, q);
496 i = m_videoSinks.insert(textureId, videoSink);
497 }
498 QDBusMessage reply =
499 m_proxy->call(QStringLiteral("CreateVideoSink"), textureId);
500 if (Q_UNLIKELY(reply.type() == QDBusMessage::ErrorMessage)) {
501 Q_EMIT q->errorOccurred(errorFromDBus(reply));
502 }
503 return *i.value();
504}
505
507 QObject(parent),
508 d_ptr(new PlayerPrivate(this))
509{
510 qRegisterMetaType<Error>("Error");
511 QMetaType::registerConverter(&Error::toString);
512 QMetaType::registerEqualsComparator<Error>();
513 qRegisterMetaType<Volume>("Volume");
514 qRegisterMetaType<QVariantMap>("Track::MetaData");
515 qRegisterMetaType<PlaybackRate>("PlaybackRate");
516 qDBusRegisterMetaType<Headers>();
517}
518
519Player::~Player() = default;
520
521QString Player::uuid() const
522{
523 Q_D(const Player);
524 return d->m_uuid;
525}
526
528{
529 Q_D(Player);
530 d->setTrackList(trackList);
531}
532
534{
535 Q_D(const Player);
536 return d->m_trackList;
537}
538
540{
541 Q_D(Player);
542 return d->createGLTextureVideoSink(textureId);
543}
544
545void Player::openUri(const QUrl &uri, const Headers &headers)
546{
547 Q_D(Player);
548 d->blockingCall(QStringLiteral("OpenUriExtended"),
549 uri.toString(), QVariant::fromValue(headers));
550}
551
553{
554 Q_D(Player);
555 d->call(QStringLiteral("Next"));
556}
557
559{
560 Q_D(Player);
561 d->call(QStringLiteral("Previous"));
562}
563
565{
566 Q_D(Player);
567 d->call(QStringLiteral("Play"));
568}
569
571{
572 Q_D(Player);
573 d->call(QStringLiteral("Pause"));
574}
575
577{
578 Q_D(Player);
579 d->call(QStringLiteral("Stop"));
580}
581
582void Player::seekTo(uint64_t microseconds)
583{
584 Q_D(Player);
585 d->call(QStringLiteral("Seek"), quint64(microseconds));
586}
587
588bool Player::canPlay() const
589{
590 Q_D(const Player);
591 return d->m_canPlay;
592}
593
595{
596 Q_D(const Player);
597 return d->m_canPause;
598}
599
600bool Player::canSeek() const
601{
602 Q_D(const Player);
603 return d->m_canSeek;
604}
605
607{
608 Q_D(const Player);
609 return d->m_canGoPrevious;
610}
611
613{
614 Q_D(const Player);
615 return d->m_canGoNext;
616}
617
619{
620 Q_D(const Player);
621 return d->m_isVideoSource;
622}
623
625{
626 Q_D(const Player);
627 return d->m_isAudioSource;
628}
629
631{
632 Q_D(const Player);
633 return d->m_playbackStatus;
634}
635
637{
638 Q_D(Player);
639 d->setProperty(QStringLiteral("PlaybackRate"), rate,
640 [=]() { d->m_playbackRate = rate; });
641}
642
644{
645 Q_D(const Player);
646 return d->m_playbackRate;
647}
648
649void Player::setShuffle(bool shuffle)
650{
651 Q_D(Player);
652 d->setProperty(QStringLiteral("Shuffle"), shuffle,
653 [=]() { d->m_shuffle = shuffle; });
654}
655
656bool Player::shuffle() const
657{
658 Q_D(const Player);
659 return d->m_shuffle;
660}
661
663{
664 Q_D(Player);
665 d->setProperty(QStringLiteral("Volume"), volume,
666 // TODO: rewrite when the service gains signals
667 [=]() { d->m_volume = volume; });
668}
669
671{
672 Q_D(const Player);
673 return d->m_volume;
674}
675
677{
678 Q_D(const Player);
679 return d->m_metaData;
680}
681
683{
684 Q_D(const Player);
685 return d->m_minimumPlaybackRate;
686}
687
689{
690 Q_D(const Player);
691 return d->m_maximumPlaybackRate;
692}
693
694quint64 Player::position() const
695{
696 Q_D(const Player);
697 return d->getProperty(QStringLiteral("Position")).toULongLong();
698}
699
700quint64 Player::duration() const
701{
702 Q_D(const Player);
703 return d->getProperty(QStringLiteral("Duration")).toULongLong();
704}
705
707{
708 Q_D(const Player);
709 return d->m_orientation;
710}
711
713{
714 Q_D(Player);
715 QString status;
716 switch (loopStatus) {
717 case Player::LoopNone: status = QStringLiteral("None"); break;
718 case Player::LoopTrack: status = QStringLiteral("Track"); break;
719 case Player::LoopPlaylist: status = QStringLiteral("Playlist"); break;
720 default:
721 qWarning() << "Unknown loop status enum:" << loopStatus;
722 return;
723 }
724 d->setProperty(QStringLiteral("LoopStatus"), status);
725}
726
728{
729 Q_D(const Player);
730 // TODO: add to PropertiesChanged
731 const QString status =
732 d->getProperty(QStringLiteral("LoopStatus")).toString();
733 if (status == "None") return Player::LoopNone;
734 if (status == "Track") return Player::LoopTrack;
735 if (status == "Playlist") return Player::LoopPlaylist;
736 qWarning() << "Unknown player status" << status;
737 return Player::LoopNone;
738}
739
741{
742 Q_D(Player);
743 d->setProperty(QStringLiteral("AudioStreamRole"), role);
744}
745
747{
748 Q_D(const Player);
749 // TODO: add to PropertiesChanged
750 return static_cast<AudioStreamRole>(
751 d->getProperty(QStringLiteral("AudioStreamRole")).toInt());
752}
753
754#include "player.moc"
void onError(quint16 code)
Definition player.cpp:193
void onPropertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
Definition player.cpp:177
void onVideoDimensionChanged(quint32 height, quint32 width)
Definition player.cpp:188
DBusPlayer(const QDBusConnection &conn, const QString &path, PlayerPrivate *d)
Definition player.cpp:165
QDBusMessage createSession()
Definition player.cpp:155
void destroySession(const QString &uuid)
Definition player.cpp:160
bool isError() const
Definition error.h:51
QString toString() const
Definition error.h:55
PlaybackRate minimumPlaybackRate
Definition player.h:65
Player(QObject *parent=nullptr)
Definition player.cpp:506
void setLoopStatus(LoopStatus loopStatus)
Definition player.cpp:712
AudioStreamRole audioStreamRole
Definition player.h:78
QMap< QString, QString > Headers
Definition player.h:83
void openUri(const QUrl &uri, const Headers &headers={})
Definition player.cpp:545
PlaybackStatus playbackStatus
Definition player.h:53
void seekTo(uint64_t microseconds)
Definition player.cpp:582
LoopStatus loopStatus
Definition player.h:75
void videoDimensionChanged(const QSize &size)
void errorOccurred(const Error &error)
Track::MetaData metaDataForCurrentTrack
Definition player.h:59
void setPlaybackRate(PlaybackRate rate)
Definition player.cpp:636
Orientation orientation
Definition player.h:72
void setVolume(Volume volume)
Definition player.cpp:662
QString uuid() const
Definition player.cpp:521
TrackList * trackList() const
Definition player.cpp:533
PlaybackRate playbackRate
Definition player.h:63
void setAudioStreamRole(AudioStreamRole role)
Definition player.cpp:740
void setTrackList(TrackList *trackList)
Definition player.cpp:527
PlaybackRate maximumPlaybackRate
Definition player.h:67
VideoSink & createGLTextureVideoSink(uint32_t textureId)
Definition player.cpp:539
void setShuffle(bool shuffle)
Definition player.cpp:649
A video sink abstracts a queue of buffers, that receives a stream of decoded video buffers from an ar...
Definition video_sink.h:35
#define MEDIAHUB_SERVICE_NAME
#define MPRIS_PLAYER_INTERFACE
#define MEDIAHUB_SERVICE_INTERFACE
#define MEDIAHUB_SERVICE_PATH
#define FDO_PROPERTIES_INTERFACE
VideoSinkFactory createVideoSinkFactory(PlayerKey key, AVBackend::Backend backend)
Error errorFromDBus(const QDBusMessage &msg)
Definition error.cpp:29
std::function< VideoSink *(uint32_t textureId, QObject *parent)> VideoSinkFactory
Error errorFromApiCode(quint16 apiCode)
Definition error.cpp:72
std::function< void(const QDBusMessage &)> MethodCb
Definition player.cpp:46
std::function< void()> VoidMethodCb
Definition player.cpp:47
static bool waitForFinished(const QDBusPendingCall &call)