27 #include <QDBusAbstractInterface>
28 #include <QDBusMessage>
29 #include <QDBusMetaType>
30 #include <QDBusPendingCall>
31 #include <QDBusPendingCallWatcher>
32 #include <QDBusPendingReply>
34 #include <QDBusServiceWatcher>
35 #include <QDBusVariant>
38 #include <QVariantMap>
46 typedef std::function<void(
const QDBusMessage &)>
MethodCb;
56 QDBusMessage createSession();
57 void destroySession(
const QString &uuid);
65 DBusPlayer(
const QDBusConnection &conn,
const QString &path,
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);
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);
178 const QVariantMap &changed,
179 const QStringList &invalidated)
184 Q_UNUSED(invalidated);
199 m_serviceWatcher(m_service.service(), m_service.connection()),
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();
233 m_proxy.data(), SLOT(onPropertiesChanged(QString,QVariantMap,QStringList)));
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();
268 PlayerKey key = keyCall.value();
269 m_videoSinkFactory = createVideoSinkFactory(key, m_backend);
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) {
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()) {
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) {
398 QDBusMessage msg = QDBusMessage::createMethodCall(
402 QStringLiteral(
"Set"));
406 QVariant::fromValue(QDBusVariant(value)),
408 QDBusPendingCall
call = m_proxy->connection().asyncCall(msg);
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();
438 if (trackList != m_trackList) {
440 trackList->d_ptr->createProxy(m_proxy->connection(),
441 m_proxy->path() +
"/TrackList");
443 m_trackList = trackList;
448 const QVariant &arg1,
const QVariant &arg2)
450 QDBusPendingCall
call = m_proxy->asyncCall(method, arg1, arg2);
455 const QVariant &arg1,
const QVariant &arg2)
458 QDBusPendingCall
call = m_proxy->asyncCall(method, arg1, arg2);
460 if (Q_UNLIKELY(!ok)) {
468 auto i = m_videoSinks.find(textureId);
469 if (i == m_videoSinks.end()) {
470 VideoSink *videoSink = m_videoSinkFactory(textureId, q);
471 i = m_videoSinks.insert(textureId, videoSink);
474 m_proxy->call(QStringLiteral(
"CreateVideoSink"), textureId);
475 if (Q_UNLIKELY(reply.type() == QDBusMessage::ErrorMessage)) {
485 qRegisterMetaType<Error>(
"Error");
487 QMetaType::registerEqualsComparator<Error>();
488 qRegisterMetaType<Volume>(
"Volume");
489 qRegisterMetaType<QVariantMap>(
"Track::MetaData");
490 qRegisterMetaType<PlaybackRate>(
"PlaybackRate");
491 qDBusRegisterMetaType<Headers>();
511 return d->m_trackList;
517 return d->createGLTextureVideoSink(textureId);
523 d->blockingCall(QStringLiteral(
"OpenUriExtended"),
524 uri.toString(), QVariant::fromValue(headers));
530 d->call(QStringLiteral(
"Next"));
536 d->call(QStringLiteral(
"Previous"));
542 d->call(QStringLiteral(
"Play"));
548 d->call(QStringLiteral(
"Pause"));
554 d->call(QStringLiteral(
"Stop"));
560 d->call(QStringLiteral(
"Seek"), quint64(microseconds));
572 return d->m_canPause;
584 return d->m_canGoPrevious;
590 return d->m_canGoNext;
596 return d->m_isVideoSource;
602 return d->m_isAudioSource;
608 return d->m_playbackStatus;
614 d->setProperty(QStringLiteral(
"PlaybackRate"), rate,
615 [=]() { d->m_playbackRate = rate; });
621 return d->m_playbackRate;
627 d->setProperty(QStringLiteral(
"Shuffle"),
shuffle,
628 [=]() { d->m_shuffle =
shuffle; });
640 d->setProperty(QStringLiteral(
"Volume"),
volume,
642 [=]() { d->m_volume =
volume; });
654 return d->m_metaData;
660 return d->m_minimumPlaybackRate;
666 return d->m_maximumPlaybackRate;
672 return d->getProperty(QStringLiteral(
"Position")).toULongLong();
678 return d->getProperty(QStringLiteral(
"Duration")).toULongLong();
684 return d->m_orientation;
696 qWarning() <<
"Unknown loop status enum:" <<
loopStatus;
699 d->setProperty(QStringLiteral(
"LoopStatus"), status);
706 const QString status =
707 d->getProperty(QStringLiteral(
"LoopStatus")).toString();
711 qWarning() <<
"Unknown player status" << status;
718 d->setProperty(QStringLiteral(
"AudioStreamRole"), role);
726 d->getProperty(QStringLiteral(
"AudioStreamRole")).toInt());
729 #include "player.moc"