47 using namespace media;
50 namespace MediaHubService {
59 WAKELOCK_CLEAR_INACTIVE,
60 WAKELOCK_CLEAR_DISPLAY,
61 WAKELOCK_CLEAR_SYSTEM,
62 WAKELOCK_CLEAR_INVALID
76 MH_DEBUG() <<
"Setting state:" << state;
79 case Engine::State::ready:
81 if (previous_state == Engine::State::playing)
83 m_wakeLockTimer.start();
87 case Engine::State::playing:
91 const auto trackMetadata = m_engine->trackMetadata();
92 media::Track::MetaData metadata = trackMetadata.second;
96 metadata.setLastUsed(QDateTime::currentDateTimeUtc().toString(Qt::ISODate));
97 update_mpris_metadata(trackMetadata.first, metadata);
99 MH_INFO(
"Requesting power state");
100 request_power_state();
104 m_wakeLockTimer.stop();
107 case Engine::State::stopped:
109 if (previous_state == Engine::State::playing)
111 m_wakeLockTimer.start();
115 case Engine::State::paused:
117 if (previous_state == Engine::State::playing)
119 m_wakeLockTimer.start();
128 previous_state = state;
135 if (q->isVideoSource())
137 MH_INFO(
"Request display on.");
138 if (!m_holdsDisplayOn) {
139 power_state_controller->requestDisplayOn();
140 m_holdsDisplayOn =
true;
145 MH_INFO(
"Request system state active.");
146 if (!m_holdsSystemActive) {
147 power_state_controller->requestSystemState(media::power::SystemState::active);
148 m_holdsSystemActive =
true;
158 case wakelock_clear_t::WAKELOCK_CLEAR_INACTIVE:
160 case wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM:
161 MH_INFO(
"Release system state active.");
162 if (m_holdsSystemActive) {
163 power_state_controller->releaseSystemState(media::power::SystemState::active);
164 m_holdsSystemActive =
false;
167 case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:
168 MH_INFO(
"Release display on.");
169 if (m_holdsDisplayOn) {
170 power_state_controller->releaseDisplayOn();
171 m_holdsDisplayOn =
false;
174 case wakelock_clear_t::WAKELOCK_CLEAR_INVALID:
176 MH_WARNING(
"Can't clear invalid wakelock type");
183 clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM);
184 clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY);
195 m_trackList->reset();
198 Q_EMIT q->clientDisconnected();
203 const QUrl uri = m_trackList->query_uri_for_track(
id);
208 MH_INFO(
"Calling d->m_engine->open_resource_for_uri() for first track added only: %s",
209 qUtf8Printable(uri.toString()));
210 MH_INFO(
"\twith a Track::Id: %s", qUtf8Printable(
id));
211 static const bool do_pipeline_reset =
true;
212 m_engine->open_resource_for_uri(uri, do_pipeline_reset);
219 const bool has_previous = m_trackList->hasPrevious()
220 or m_trackList->loopStatus() != Player::LoopStatus::none;
221 const bool has_next = m_trackList->hasNext()
222 or m_trackList->loopStatus() != Player::LoopStatus::none;
223 const auto n_tracks = m_trackList->tracks().count();
224 const bool has_tracks = (n_tracks > 0) ?
true :
false;
226 MH_INFO(
"Updating MPRIS TrackList properties:");
227 MH_INFO(
"\tTracks: %d", n_tracks);
228 MH_INFO(
"\thas_previous: %d", has_previous);
229 MH_INFO(
"\thas_next: %d", has_next);
232 m_canPlay = has_tracks;
233 m_canPause = has_tracks;
234 m_canGoPrevious = has_previous;
235 m_canGoNext = has_next;
236 Q_EMIT q->mprisPropertiesChanged();
240 const media::Track::MetaData& metadata)
243 bool is_local_file =
false;
244 if (not uri.isEmpty())
245 is_local_file = uri.isLocalFile();
249 if ( (metadata.value(tags::PreviewImage::name).toBool()
250 or (metadata.value(tags::Image::name).toBool())
251 or m_engine->isVideoSource())
254 art_uri =
"image://thumbnailer/" + uri.path();
259 art_uri =
"file:///usr/share/icons/suru/apps/scalable/music-app-symbolic.svg";
270 media::Track::MetaData metadata{md};
271 if (not metadata.isSet(media::Track::MetaData::TrackIdKey))
273 const QString current_track = m_trackList->current();
274 if (not current_track.isEmpty())
276 const int last_slash = current_track.lastIndexOf(
'/');
277 const QStringRef track_id = current_track.midRef(last_slash + 1);
278 if (not track_id.isEmpty())
279 metadata.setTrackId(
"/org/mpris/MediaPlayer2/Track/" + track_id);
281 MH_WARNING(
"Failed to set MPRIS track id since the id value is NULL");
284 MH_WARNING(
"Failed to set MPRIS track id since the id value is NULL");
287 if (not metadata.isSet(media::Track::MetaData::TrackArtlUrlKey))
288 metadata.setArtUrl(get_uri_for_album_artwork(uri, metadata));
290 if (not metadata.isSet(media::Track::MetaData::TrackLengthKey))
293 metadata.setTrackLength(m_engine->duration() / 1000);
297 metadata.remove(QStringLiteral(
"bitrate"));
298 metadata.remove(QStringLiteral(
"minimum-bitrate"));
299 metadata.remove(QStringLiteral(
"maximum-bitrate"));
301 m_metadataForCurrentTrack = metadata;
302 Q_EMIT q->metadataForCurrentTrackChanged();
307 return m_engine->audioStreamRole() == media::Player::AudioStreamRole::multimedia;
316 bool m_holdsSystemActive =
false;
317 bool m_holdsDisplayOn =
false;
321 bool m_canPlay =
false;
322 bool m_canPause =
false;
323 bool m_canGoPrevious =
false;
324 bool m_canGoNext =
false;
325 bool m_shuffle =
false;
326 double m_playbackRate = 1.f;
328 int64_t m_position = 0;
329 int64_t m_duration = 0;
330 bool m_doingOpenUri =
false;
341 PlayerImplementationPrivate::PlayerImplementationPrivate(
342 const media::PlayerImplementation::Configuration &config,
344 m_client(config.client),
345 m_clientDeathObserver(config.client_death_observer),
346 power_state_controller(
media::power::StateController::instance()),
351 m_engine->metadataExtractor())),
353 doing_abandon(false),
360 MH_INFO(
"Acquired display ON state");
366 MH_INFO(
"Released display ON state");
373 MH_INFO() <<
"Acquired new system state:" << state;
380 MH_INFO() <<
"Released system state:" << state;
385 onStateChanged(m_engine->state());
395 if (m_engine->state() == Engine::State::playing) {
396 MH_INFO(
"Streams changed, updating power/display locks");
397 request_power_state();
405 m_engine->setAudioStreamRole(Player::AudioStreamRole::multimedia);
406 m_engine->setLifetime(Player::Lifetime::normal);
412 m_trackList->setCurrentPosition(m_engine->position());
413 Q_EMIT q->positionChanged();
428 const auto md = m_engine->trackMetadata();
429 update_mpris_metadata(md.first, md.second);
438 Q_EMIT q->endOfStream();
467 if (m_engine->state() != gstreamer::Engine::State::stopped)
469 MH_INFO(
"End of tracklist reached, stopping playback");
474 QObject::connect(m_trackList.data(), &TrackListImplementation::onGoToTrack,
478 const bool auto_play = m_engine->playbackStatus() == media::Player::playing;
480 const QUrl uri = m_trackList->query_uri_for_track(id);
483 MH_INFO(
"Setting next track on playbin (on_go_to_track signal): %s",
484 qUtf8Printable(uri.toString()));
485 MH_INFO(
"\twith a Track::Id: %s", qUtf8Printable(id));
486 static const bool do_pipeline_reset = true;
487 m_engine->open_resource_for_uri(uri, do_pipeline_reset);
492 MH_DEBUG(
"Restoring playing state");
497 QObject::connect(m_trackList.data(), &TrackListImplementation::trackAdded,
500 MH_TRACE(
"** Track was added, handling in PlayerImplementation");
501 if (!m_doingOpenUri && m_trackList->tracks().count() == 1)
502 open_first_track_from_tracklist(id);
504 update_mpris_properties();
507 QObject::connect(m_trackList.data(), &TrackListImplementation::tracksAdded,
508 q, [
this](
const QVector<QUrl> &tracks)
510 MH_TRACE(
"** Track was added, handling in PlayerImplementation");
517 if (not tracks.isEmpty() and m_trackList->tracks().count() == tracks.count())
518 open_first_track_from_tracklist(tracks.front().toString());
520 update_mpris_properties();
523 QObject::connect(m_trackList.data(),
524 &TrackListImplementation::trackRemoved,
525 q, [
this]() { update_mpris_properties(); });
527 QObject::connect(m_trackList.data(),
528 &TrackListImplementation::trackListReset,
529 q, [
this]() { update_mpris_properties(); });
531 QObject::connect(m_trackList.data(),
532 &TrackListImplementation::trackChanged,
533 q, [
this]() { update_mpris_properties(); });
535 QObject::connect(m_trackList.data(),
536 &TrackListImplementation::trackListReplaced,
537 q, [
this]() { update_mpris_properties(); });
540 m_clientDeathObserver->registerForDeathNotifications(m_client);
541 QObject::connect(m_clientDeathObserver.data(),
542 &ClientDeathObserver::clientDied,
543 q, [
this](
const media::Player::Client &died)
548 if (died.key != m_client.key)
551 m_abandonTimer.start();
554 m_abandonTimer.setSingleShot(
true);
555 m_abandonTimer.setInterval(1000);
556 m_abandonTimer.callOnTimeout(q, [
this]() { on_client_died(); });
558 m_wakeLockTimer.setSingleShot(
true);
559 int wakelockTimeout =
560 qEnvironmentVariableIsSet(
"MEDIA_HUB_WAKELOCK_TIMEOUT") ?
561 qEnvironmentVariableIntValue(
"MEDIA_HUB_WAKELOCK_TIMEOUT") : 4000;
562 m_wakeLockTimer.setInterval(wakelockTimeout);
563 m_wakeLockTimer.setTimerType(Qt::VeryCoarseTimer);
564 m_wakeLockTimer.callOnTimeout(q, [
this]() {
569 PlayerImplementationPrivate::~PlayerImplementationPrivate()
581 QObject::connect(
this, &QObject::objectNameChanged,
582 this, [
this](
const QString &name) {
584 d->m_trackList->setObjectName(name +
"/TrackList");
612 return d->m_canPause;
624 return d->m_canGoPrevious;
630 return d->m_canGoNext;
636 MH_WARNING(
"Setting playback rate not implemented");
657 MH_INFO() <<
"LoopStatus:" << status;
658 d->m_trackList->setLoopStatus(status);
664 return d->m_trackList->loopStatus();
670 d->m_trackList->setShuffle(
shuffle);
676 return d->m_trackList->shuffle();
682 d->m_engine->setVolume(
volume);
689 return d->m_engine->volume();
695 return d->m_engine->playbackStatus();
701 return d->m_engine->isVideoSource();
707 return d->m_engine->isAudioSource();
713 return d->m_engine->videoDimension();
719 return d->m_engine->orientation();
725 return d->m_metadataForCurrentTrack;
731 return d->m_engine->position();
737 return d->m_engine->duration();
743 d->m_engine->setAudioStreamRole(role);
749 return d->m_engine->audioStreamRole();
761 return d->m_engine->lifetime();
767 d->m_clientDeathObserver->registerForDeathNotifications(d->m_client);
774 d->doing_abandon =
true;
781 return d->m_trackList;
787 return d->m_client.key;
793 d->m_engine->create_video_sink(texture_id);
804 d->m_doingOpenUri =
true;
805 d->m_trackList->reset();
810 MH_DEBUG(
"Resetting current media");
814 const bool ret = d->m_engine->open_resource_for_uri(uri, headers);
816 static const bool make_current =
false;
818 d->m_doingOpenUri =
false;
826 d->m_trackList->next();
832 d->m_trackList->previous();
839 if (d->is_multimedia_role())
851 d->m_engine->pause();
864 d->m_engine->seek_to(ms);