27#include <QDBusAbstractInterface>
28#include <QDBusMessage>
29#include <QDBusMetaType>
30#include <QDBusPendingCall>
31#include <QDBusPendingCallWatcher>
32#include <QDBusPendingReply>
34#include <QDBusServiceWatcher>
35#include <QDBusVariant>
46typedef std::function<void(
const QDBusMessage &)>
MethodCb;
65 DBusPlayer(
const QDBusConnection &conn,
const QString &path,
70 const QVariantMap &changed,
71 const QStringList &invalidated);
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,
97 void setProperty(
const QString &name,
const QVariant &value,
99 QVariant getProperty(
const QString &name)
const;
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);
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;
131 QHash<quint32, VideoSink*> m_videoSinks;
135 QDBusServiceWatcher m_serviceWatcher;
136 QScopedPointer<DBusPlayer> m_proxy;
149 QDBusConnection::sessionBus(),
157 return call(QStringLiteral(
"CreateSession"));
162 call(QDBus::NoBlock, QStringLiteral(
"DestroySession"), uuid);
166 const QString &objectPath,
178 const QVariantMap &changed,
179 const QStringList &invalidated)
182 d->updateProperties(changed);
184 Q_UNUSED(invalidated);
190 d->onVideoDimensionChanged(height, width);
198PlayerPrivate::PlayerPrivate(
Player *q):
199 m_serviceWatcher(m_service.service(), m_service.connection()),
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());
209 const QString path = reply.arguments()[0].value<QDBusObjectPath>().path();
210 m_uuid = reply.arguments()[1].toString();
212 m_proxy.reset(
new DBusPlayer(m_service.connection(), path,
this));
216 QDBusPendingReply<quint32> keyCall =
217 m_proxy->asyncCall(QStringLiteral(
"Key"));
219 QObject::connect(&m_serviceWatcher,
220 &QDBusServiceWatcher::serviceRegistered,
222 QObject::connect(&m_serviceWatcher,
223 &QDBusServiceWatcher::serviceUnregistered,
226 QDBusConnection c(m_service.connection());
228 const QString service = m_proxy->service();
229 const QString path = m_proxy->path();
230 const QString
interface = m_proxy->interface();
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"),
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)));
249 QDBusMessage msg = QDBusMessage::createMethodCall(
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();
259 QDBusArgument arg = reply.arguments().first().value<QDBusArgument>();
260 updateProperties(qdbus_cast<QVariantMap>(arg));
264 keyCall.waitForFinished();
265 if (Q_UNLIKELY(keyCall.isError())) {
266 qWarning() <<
"Key() call failed:" << keyCall.error().message();
273PlayerPrivate::~PlayerPrivate()
275 m_service.destroySession(m_uuid);
278void PlayerPrivate::updateProperties(
const QVariantMap &properties)
282 bool controlsChanged =
false;
283 bool sourceTypeChanged =
false;
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") {
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;
319 }
else if (name ==
"Metadata") {
320 QDBusArgument arg = i.value().value<QDBusArgument>();
322 const QVariantMap dbusMap = qdbus_cast<QVariantMap>(arg);
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());
329 }
else if (name ==
"Orientation") {
333 }
else if (name ==
"TypedBackend") {
339 if (controlsChanged) {
342 if (sourceTypeChanged) {
347void PlayerPrivate::onVideoDimensionChanged(quint32 height, quint32 width)
353void PlayerPrivate::onError(quint16 dbusCode)
362void PlayerPrivate::watchErrors(
const QDBusPendingCall &call)
365 auto watcher =
new QDBusPendingCallWatcher(call);
366 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
367 q, [
this](QDBusPendingCallWatcher *call) {
370 QDBusPendingReply<void> reply(*call);
371 if (reply.isError()) {
377void PlayerPrivate::onSuccessfulCompletion(
const QDBusPendingCall &call,
381 auto watcher =
new QDBusPendingCallWatcher(call);
382 QObject::connect(watcher, &QDBusPendingCallWatcher::finished,
383 q, [
this, callback](QDBusPendingCallWatcher *call) {
386 const QDBusMessage reply = QDBusPendingReply<void>(*call).reply();
387 if (Q_UNLIKELY(reply.type()) == QDBusMessage::ErrorMessage) {
389 }
else if (callback) {
395void PlayerPrivate::setProperty(
const QString &name,
const QVariant &value,
398 QDBusMessage msg = QDBusMessage::createMethodCall(
402 QStringLiteral(
"Set"));
406 QVariant::fromValue(QDBusVariant(value)),
408 QDBusPendingCall call = m_proxy->connection().asyncCall(msg);
409 onSuccessfulCompletion(call, [callback](
const QDBusMessage &) {
415QVariant PlayerPrivate::getProperty(
const QString &name)
const
417 QDBusMessage msg = QDBusMessage::createMethodCall(
421 QStringLiteral(
"Get"));
427 QDBusReply<QVariant> reply = m_proxy->connection().call(msg);
428 if (Q_LIKELY(reply.isValid())) {
431 qWarning() <<
"Error getting property" << name << reply.error();
436void PlayerPrivate::setTrackList(
TrackList *trackList)
438 if (trackList != m_trackList) {
440 trackList->d_ptr->createProxy(m_proxy->connection(),
441 m_proxy->path() +
"/TrackList");
443 m_trackList = trackList;
447void PlayerPrivate::call(
const QString &method,
448 const QVariant &arg1,
const QVariant &arg2)
466 QList<QVariant> args;
472 if (arg1.isValid()) args.append(arg1);
473 if (arg2.isValid()) args.append(arg2);
475 QDBusPendingCall call = m_proxy->asyncCallWithArgumentList(method, args);
479void PlayerPrivate::blockingCall(
const QString &method,
480 const QVariant &arg1,
const QVariant &arg2)
483 QDBusPendingCall call = m_proxy->asyncCall(method, arg1, arg2);
485 if (Q_UNLIKELY(!ok)) {
490VideoSink &PlayerPrivate::createGLTextureVideoSink(uint32_t textureId)
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);
499 m_proxy->call(QStringLiteral(
"CreateVideoSink"), textureId);
500 if (Q_UNLIKELY(reply.type() == QDBusMessage::ErrorMessage)) {
508 d_ptr(new PlayerPrivate(this))
510 qRegisterMetaType<Error>(
"Error");
512 QMetaType::registerEqualsComparator<Error>();
513 qRegisterMetaType<Volume>(
"Volume");
514 qRegisterMetaType<QVariantMap>(
"Track::MetaData");
515 qRegisterMetaType<PlaybackRate>(
"PlaybackRate");
516 qDBusRegisterMetaType<Headers>();
536 return d->m_trackList;
542 return d->createGLTextureVideoSink(textureId);
548 d->blockingCall(QStringLiteral(
"OpenUriExtended"),
549 uri.toString(), QVariant::fromValue(headers));
555 d->call(QStringLiteral(
"Next"));
561 d->call(QStringLiteral(
"Previous"));
567 d->call(QStringLiteral(
"Play"));
573 d->call(QStringLiteral(
"Pause"));
579 d->call(QStringLiteral(
"Stop"));
585 d->call(QStringLiteral(
"Seek"), quint64(microseconds));
597 return d->m_canPause;
609 return d->m_canGoPrevious;
615 return d->m_canGoNext;
621 return d->m_isVideoSource;
627 return d->m_isAudioSource;
633 return d->m_playbackStatus;
639 d->setProperty(QStringLiteral(
"PlaybackRate"), rate,
640 [=]() { d->m_playbackRate = rate; });
646 return d->m_playbackRate;
652 d->setProperty(QStringLiteral(
"Shuffle"),
shuffle,
653 [=]() { d->m_shuffle =
shuffle; });
665 d->setProperty(QStringLiteral(
"Volume"),
volume,
667 [=]() { d->m_volume =
volume; });
679 return d->m_metaData;
685 return d->m_minimumPlaybackRate;
691 return d->m_maximumPlaybackRate;
697 return d->getProperty(QStringLiteral(
"Position")).toULongLong();
703 return d->getProperty(QStringLiteral(
"Duration")).toULongLong();
709 return d->m_orientation;
721 qWarning() <<
"Unknown loop status enum:" <<
loopStatus;
724 d->setProperty(QStringLiteral(
"LoopStatus"), status);
731 const QString status =
732 d->getProperty(QStringLiteral(
"LoopStatus")).toString();
736 qWarning() <<
"Unknown player status" << status;
743 d->setProperty(QStringLiteral(
"AudioStreamRole"), role);
751 d->getProperty(QStringLiteral(
"AudioStreamRole")).toInt());
void onError(quint16 code)
void onPropertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated)
void onVideoDimensionChanged(quint32 height, quint32 width)
DBusPlayer(const QDBusConnection &conn, const QString &path, PlayerPrivate *d)
QDBusMessage createSession()
void destroySession(const QString &uuid)
#define MEDIAHUB_SERVICE_NAME
#define MPRIS_PLAYER_INTERFACE
#define MEDIAHUB_SERVICE_INTERFACE
#define MEDIAHUB_SERVICE_PATH
#define FDO_PROPERTIES_INTERFACE
std::function< void(const QDBusMessage &)> MethodCb
std::function< void()> VoidMethodCb