42 #define UNUSED __attribute__((unused)) 49 template<
typename Parent>
51 public std::enable_shared_from_this<Private>
55 WAKELOCK_CLEAR_INACTIVE,
56 WAKELOCK_CLEAR_DISPLAY,
57 WAKELOCK_CLEAR_SYSTEM,
58 WAKELOCK_CLEAR_INVALID
64 display_state_lock(config.power_state_controller->display_state_lock()),
65 system_state_lock(config.power_state_controller->system_state_lock()),
67 track_list(
std::make_shared<TrackListImplementation>(
69 config.parent.service->add_object_for_path(
70 dbus::types::ObjectPath(config.parent.session->path().as_string() +
"/TrackList")),
71 engine->meta_data_extractor(),
72 config.parent.request_context_resolver,
73 config.parent.request_authenticator)),
74 system_wakelock_count(0),
75 display_wakelock_count(0),
76 previous_state(Engine::State::stopped),
77 engine_state_change_connection(engine->state().changed().connect(make_state_change_handler())),
78 engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler())),
84 MH_INFO(
"Acquired new display state: %s", state);
89 MH_INFO(
"Released display state: %s", state);
94 MH_INFO(
"Acquired new system state: %s", state);
99 MH_INFO(
"Released system state: %s", state);
112 engine_state_change_connection.disconnect();
117 engine_playback_status_change_connection.disconnect();
127 return [
this](
const Engine::State& state)
129 MH_DEBUG(
"Setting state for parent: %s", parent);
132 case Engine::State::ready:
135 if (previous_state == Engine::State::playing)
137 timeout(4000,
true, make_clear_wakelock_functor());
141 case Engine::State::playing:
147 char buf[
sizeof(
"2011-10-08T07:07:09Z")];
148 strftime(buf,
sizeof(buf),
"%FT%TZ", gmtime(&now));
149 media::Track::MetaData metadata{std::get<1>(engine->track_meta_data().get())};
153 metadata.set_last_used(std::string{buf});
154 update_mpris_metadata(std::get<0>(engine->track_meta_data().get()), metadata);
158 MH_INFO(
"Requesting power state");
159 request_power_state();
162 case Engine::State::stopped:
165 if (previous_state == Engine::State::playing)
167 timeout(4000,
true, make_clear_wakelock_functor());
171 case Engine::State::paused:
174 if (previous_state == Engine::State::playing)
176 timeout(4000,
true, make_clear_wakelock_functor());
185 previous_state = state;
193 MH_INFO(
"Emiting playback_status_changed signal: %s", status);
194 parent->emit_playback_status_changed(status);
203 if (parent->is_video_source())
205 if (++display_wakelock_count == 1)
207 MH_INFO(
"Requesting new display wakelock.");
208 display_state_lock->request_acquire(media::power::DisplayState::on);
209 MH_INFO(
"Requested new display wakelock.");
214 if (++system_wakelock_count == 1)
216 MH_INFO(
"Requesting new system wakelock.");
217 system_state_lock->request_acquire(media::power::SystemState::active);
218 MH_INFO(
"Requested new system wakelock.");
222 catch(
const std::exception& e)
224 MH_WARNING(
"Failed to request power state: %s", e.what());
235 case wakelock_clear_t::WAKELOCK_CLEAR_INACTIVE:
237 case wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM:
239 if (--system_wakelock_count == 0)
241 MH_INFO(
"Clearing system wakelock.");
242 system_state_lock->request_release(media::power::SystemState::active);
245 case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:
247 if (--display_wakelock_count == 0)
249 MH_INFO(
"Clearing display wakelock.");
250 display_state_lock->request_release(media::power::DisplayState::on);
253 case wakelock_clear_t::WAKELOCK_CLEAR_INVALID:
255 MH_WARNING(
"Can't clear invalid wakelock type");
258 catch(
const std::exception& e)
260 MH_WARNING(
"Failed to request clear power state: %s", e.what());
266 return (parent->is_video_source()) ?
267 wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY : wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM;
273 if (system_wakelock_count.load() > 0)
275 system_wakelock_count = 1;
276 clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM);
278 if (display_wakelock_count.load() > 0)
280 display_wakelock_count = 1;
281 clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY);
291 std::weak_ptr<Private> weak_self{this->shared_from_this()};
292 auto wakelock_type = current_wakelock_type();
293 return [weak_self, wakelock_type] {
294 if (
auto self = weak_self.lock())
295 self->clear_wakelock(wakelock_type);
306 const Track::UriType uri = track_list->query_uri_for_track(
id);
311 MH_INFO(
"Calling d->engine->open_resource_for_uri() for first track added only: %s",
313 MH_INFO(
"\twith a Track::Id: %s",
id);
314 static const bool do_pipeline_reset =
false;
315 engine->open_resource_for_uri(uri, do_pipeline_reset);
321 const bool has_previous = track_list->has_previous()
322 or parent->Parent::loop_status() != Player::LoopStatus::none;
323 const bool has_next = track_list->has_next()
324 or parent->Parent::loop_status() != Player::LoopStatus::none;
325 const auto n_tracks = track_list->tracks()->size();
326 const bool has_tracks = (n_tracks > 0) ?
true :
false;
328 MH_INFO(
"Updating MPRIS TrackList properties:");
329 MH_INFO(
"\tTracks: %d", n_tracks);
330 MH_INFO(
"\thas_previous: %d", has_previous);
331 MH_INFO(
"\thas_next: %d", has_next);
334 parent->can_play().set(has_tracks);
335 parent->can_pause().set(has_tracks);
336 parent->can_go_previous().set(has_previous);
337 parent->can_go_next().set(has_next);
341 const media::Track::MetaData& metadata)
344 static const std::string file_uri_prefix{
"file://"};
345 bool is_local_file =
false;
347 is_local_file = (uri.substr(0, 7) == file_uri_prefix or uri.at(0) ==
'/');
351 if ( (( metadata.count(tags::PreviewImage::name) > 0
352 and metadata.get(tags::PreviewImage::name) ==
"true")
353 or ( metadata.count(tags::Image::name) > 0
354 and metadata.get(tags::Image::name) ==
"true")
355 or parent->is_video_source().get())
358 art_uri =
"image://thumbnailer/" + uri;
362 else if (metadata.is_set(xesam::Album::name) or metadata.is_set(xesam::Artist::name))
364 if (metadata.is_set(xesam::Album::name) and metadata.is_set(xesam::Artist::name))
366 art_uri =
"image://albumart/artist=" + metadata.encode(xesam::Artist::name)
367 +
"&album=" + metadata.encode(xesam::Album::name);
371 art_uri =
"image://albumart/";
372 if (metadata.is_set(xesam::Artist::name))
373 art_uri +=
"artist=" + metadata.encode(xesam::Artist::name);
374 else if (metadata.is_set(xesam::Album::name))
375 art_uri +=
"album=" + metadata.encode(xesam::Album::name);
381 art_uri =
"file:///usr/lib/arm-linux-gnueabihf/unity-scopes/mediascanner-music/album_missing.svg";
391 media::Track::MetaData metadata{md};
392 if (not metadata.is_set(media::Track::MetaData::TrackIdKey))
394 const std::string current_track = track_list->current();
395 if (not current_track.empty())
397 const std::size_t last_slash = current_track.find_last_of(
"/");
398 const std::string track_id = current_track.substr(last_slash + 1);
399 if (not track_id.empty())
400 metadata.set_track_id(
"/org/mpris/MediaPlayer2/Track/" + track_id);
402 MH_WARNING(
"Failed to set MPRIS track id since the id value is NULL");
405 MH_WARNING(
"Failed to set MPRIS track id since the id value is NULL");
408 if (not metadata.is_set(media::Track::MetaData::TrackArtlUrlKey))
409 metadata.set_art_url(get_uri_for_album_artwork(uri, metadata));
411 if (not metadata.is_set(media::Track::MetaData::TrackLengthKey))
414 metadata.set_track_length(std::to_string(engine->duration().get() / 1000));
417 parent->meta_data_for_current_track().set(metadata);
422 if (not config.parent.player_service)
428 skel->pause_other_sessions(key);
434 if (not config.parent.player_service)
440 skel->set_current_player(key);
446 if (not config.parent.player_service)
452 return skel->is_current_player(parent->key());
457 return parent->audio_stream_role() == media::Player::AudioStreamRole::multimedia;
462 if (not config.parent.player_service)
468 skel->reset_current_player();
492 template<
typename Parent>
495 d{std::make_shared<Private>(
this,
config)}
498 Parent::can_play().set(
false);
499 Parent::can_pause().set(
false);
500 Parent::can_seek().set(
true);
501 Parent::can_go_previous().set(
false);
502 Parent::can_go_next().set(
false);
503 Parent::is_video_source().set(
false);
504 Parent::is_audio_source().set(
false);
505 Parent::shuffle().set(
false);
506 Parent::playback_rate().set(1.f);
507 Parent::playback_status().set(Player::PlaybackStatus::null);
509 Parent::loop_status().set(Player::LoopStatus::none);
510 Parent::position().set(0);
511 Parent::duration().set(0);
512 Parent::audio_stream_role().set(Player::AudioStreamRole::multimedia);
513 d->engine->audio_stream_role().set(Player::AudioStreamRole::multimedia);
514 Parent::orientation().set(Player::Orientation::rotate0);
515 Parent::lifetime().set(Player::Lifetime::normal);
516 d->engine->lifetime().set(Player::Lifetime::normal);
520 std::function<uint64_t()> position_getter = [
this]()
522 return d->engine->position().get();
524 Parent::position().install(position_getter);
526 d->engine->position().changed().connect([
this](uint64_t position)
528 d->track_list->on_position_changed(position);
533 std::function<uint64_t()> duration_getter = [
this]()
535 return d->engine->duration().get();
537 Parent::duration().install(duration_getter);
539 std::function<bool()> video_type_getter = [
this]()
541 return d->engine->is_video_source().get();
543 Parent::is_video_source().install(video_type_getter);
545 std::function<bool()> audio_type_getter = [
this]()
547 return d->engine->is_audio_source().get();
549 Parent::is_audio_source().install(audio_type_getter);
551 std::function<bool()> can_go_next_getter = [
this]()
554 return d->
track_list->has_next() or Parent::loop_status() != Player::LoopStatus::none;
556 Parent::can_go_next().install(can_go_next_getter);
558 std::function<bool()> can_go_previous_getter = [
this]()
561 return d->track_list->has_previous() or Parent::loop_status() != Player::LoopStatus::none;
563 Parent::can_go_previous().install(can_go_previous_getter);
568 MH_INFO(
"LoopStatus: %s", loop_status);
569 d->track_list->on_loop_status_changed(loop_status);
573 Parent::shuffle().changed().connect([
this](
bool shuffle)
575 d->track_list->on_shuffle_changed(shuffle);
582 d->engine->audio_stream_role().set(new_role);
589 Parent::orientation().set(o);
594 d->engine->lifetime().set(lifetime);
597 d->engine->track_meta_data().changed().connect([
this,
config](
598 const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
600 d->update_mpris_metadata(std::get<0>(md), std::get<1>(md));
603 d->engine->about_to_finish_signal().connect([
this]()
605 if (d->doing_abandon)
611 d->doing_go_to_track.lock();
613 Parent::about_to_finish()();
618 const Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
619 if (prev_track_id != d->track_list->current() && !uri.empty())
621 MH_INFO(
"Advancing to next track on playbin: %s", uri);
622 static const bool do_pipeline_reset =
false;
623 d->engine->open_resource_for_uri(uri, do_pipeline_reset);
626 d->doing_go_to_track.unlock();
629 d->engine->client_disconnected_signal().connect([
this]()
633 d->clear_wakelocks();
634 d->track_list->reset();
638 if (d->is_multimedia_role() and d->is_current_player())
640 MH_DEBUG(
"==== Resetting current player");
641 if (not d->reset_current_player())
646 d->on_client_disconnected();
649 d->engine->seeked_to_signal().connect([
this](uint64_t value)
651 Parent::seeked_to()(value);
654 d->engine->on_buffering_changed_signal().connect([
this](
int value)
656 Parent::buffering_changed()(value);
659 d->engine->end_of_stream_signal().connect([
this]()
661 Parent::end_of_stream()();
666 Parent::video_dimension_changed()(dimensions);
669 d->engine->error_signal().connect([
this](
const Player::Error& e)
674 d->track_list->on_end_of_tracklist().connect([
this]()
679 MH_INFO(
"End of tracklist reached, stopping playback");
680 const constexpr
bool use_main_thread =
true;
681 d->engine->stop(use_main_thread);
688 const bool locked = d->doing_go_to_track.try_lock();
697 const Track::UriType uri = d->track_list->query_uri_for_track(
id);
700 MH_INFO(
"Setting next track on playbin (on_go_to_track signal): %s", uri);
701 MH_INFO(
"\twith a Track::Id: %s",
id);
702 static const bool do_pipeline_reset =
true;
703 d->engine->open_resource_for_uri(uri, do_pipeline_reset);
708 MH_DEBUG(
"Restoring playing state");
712 d->doing_go_to_track.unlock();
717 MH_TRACE(
"** Track was added, handling in PlayerImplementation");
718 if (d->track_list->tracks()->size() == 1)
719 d->open_first_track_from_tracklist(
id);
721 d->update_mpris_properties();
726 MH_TRACE(
"** Track was added, handling in PlayerImplementation");
730 if (tracks.size() >= 1 and d->track_list->tracks()->size() == tracks.size())
731 d->open_first_track_from_tracklist(tracks.front());
733 d->update_mpris_properties();
738 d->update_mpris_properties();
741 d->track_list->on_track_list_reset().connect([
this](
void)
743 d->update_mpris_properties();
748 d->update_mpris_properties();
751 d->track_list->on_track_list_replaced().connect(
754 d->update_mpris_properties();
759 std::weak_ptr<Private> wp{d};
761 d->config.client_death_observer->register_for_death_notifications_with_key(
config.key);
764 if (
auto sp = wp.lock())
766 if (sp->doing_abandon)
769 if (died != sp->config.key)
772 static const std::chrono::milliseconds timeout{1000};
775 if (
auto sp = wp.lock())
776 sp->on_client_died();
782 template<
typename Parent>
788 std::function<uint64_t()> position_getter = [
this]()
790 return static_cast<uint64_t
>(0);
792 Parent::position().install(position_getter);
794 std::function<uint64_t()> duration_getter = [
this]()
796 return static_cast<uint64_t
>(0);
798 Parent::duration().install(duration_getter);
800 std::function<bool()> video_type_getter = [
this]()
804 Parent::is_video_source().install(video_type_getter);
806 std::function<bool()> audio_type_getter = [
this]()
810 Parent::is_audio_source().install(audio_type_getter);
813 template<
typename Parent>
817 return std::string{};
820 template<
typename Parent>
823 d->config.client_death_observer->register_for_death_notifications_with_key(d->config.key);
826 template<
typename Parent>
830 d->doing_abandon =
true;
834 template<
typename Parent>
837 return d->track_list;
841 template<
typename Parent>
844 return d->config.key;
847 template<
typename Parent>
850 d->engine->create_video_sink(texture_id);
851 return media::video::Sink::Ptr{};
854 template<
typename Parent>
857 d->track_list->reset();
862 MH_DEBUG(
"Resetting current media");
866 static const bool do_pipeline_reset =
false;
867 const bool ret = d->engine->open_resource_for_uri(uri, do_pipeline_reset);
869 static const bool make_current =
false;
875 template<
typename Parent>
878 return d->engine->open_resource_for_uri(uri, headers);
881 template<
typename Parent>
884 d->track_list->next();
887 template<
typename Parent>
890 d->track_list->previous();
893 template<
typename Parent>
897 if (d->is_multimedia_role())
899 MH_DEBUG(
"==== Pausing all other multimedia player sessions");
900 if (not d->pause_other_players(d->config.key))
901 MH_WARNING(
"Failed to pause other player sessions");
903 MH_DEBUG(
"==== Updating the current player");
906 if (not d->update_current_player(d->config.key))
907 MH_WARNING(
"Failed to update current player");
913 template<
typename Parent>
920 template<
typename Parent>
927 template<
typename Parent>
930 d->engine->seek_to(ms);
933 template<
typename Parent>
936 return d->on_client_disconnected;
939 template<
typename Parent>
942 Parent::playback_status_changed()(status);