42#define UNUSED __attribute__((unused))
49template<
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();
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);
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;
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;
363 art_uri =
"file:///usr/share/icons/suru/apps/scalable/music-app-symbolic.svg";
373 media::Track::MetaData metadata{md};
374 if (not metadata.is_set(media::Track::MetaData::TrackIdKey))
376 const std::string current_track =
track_list->current();
377 if (not current_track.empty())
379 const std::size_t last_slash = current_track.find_last_of(
"/");
380 const std::string track_id = current_track.substr(last_slash + 1);
381 if (not track_id.empty())
382 metadata.set_track_id(
"/org/mpris/MediaPlayer2/Track/" + track_id);
384 MH_WARNING(
"Failed to set MPRIS track id since the id value is NULL");
387 MH_WARNING(
"Failed to set MPRIS track id since the id value is NULL");
390 if (not metadata.is_set(media::Track::MetaData::TrackArtlUrlKey))
391 metadata.set_art_url(get_uri_for_album_artwork(uri, metadata));
393 if (not metadata.is_set(media::Track::MetaData::TrackLengthKey))
396 metadata.set_track_length(std::to_string(engine->duration().get() / 1000));
399 parent->meta_data_for_current_track().set(metadata);
404 if (not config.parent.player_service)
416 if (not config.parent.player_service)
428 if (not config.parent.player_service)
439 return parent->audio_stream_role() == media::Player::AudioStreamRole::multimedia;
444 if (not config.parent.player_service)
474template<
typename Parent>
477 d{std::make_shared<
Private>(this, config)}
480 Parent::can_play().set(
false);
481 Parent::can_pause().set(
false);
482 Parent::can_seek().set(
true);
483 Parent::can_go_previous().set(
false);
484 Parent::can_go_next().set(
false);
485 Parent::is_video_source().set(
false);
486 Parent::is_audio_source().set(
false);
487 Parent::shuffle().set(
false);
488 Parent::playback_rate().set(1.f);
489 Parent::playback_status().set(Player::PlaybackStatus::null);
491 Parent::loop_status().set(Player::LoopStatus::none);
492 Parent::position().set(0);
493 Parent::duration().set(0);
494 Parent::audio_stream_role().set(Player::AudioStreamRole::multimedia);
495 d->
engine->audio_stream_role().set(Player::AudioStreamRole::multimedia);
496 Parent::orientation().set(Player::Orientation::rotate0);
497 Parent::lifetime().set(Player::Lifetime::normal);
498 d->engine->lifetime().set(Player::Lifetime::normal);
502 std::function<uint64_t()> position_getter = [
this]()
504 return d->engine->position().get();
506 Parent::position().install(position_getter);
508 d->engine->position().changed().connect([
this](uint64_t position)
510 d->track_list->on_position_changed(position);
515 std::function<uint64_t()> duration_getter = [
this]()
517 return d->engine->duration().get();
519 Parent::duration().install(duration_getter);
521 std::function<bool()> video_type_getter = [
this]()
523 return d->engine->is_video_source().get();
525 Parent::is_video_source().install(video_type_getter);
527 std::function<bool()> audio_type_getter = [
this]()
529 return d->engine->is_audio_source().get();
531 Parent::is_audio_source().install(audio_type_getter);
533 std::function<bool()> can_go_next_getter = [
this]()
536 return d->track_list->has_next() or Parent::loop_status() != Player::LoopStatus::none;
538 Parent::can_go_next().install(can_go_next_getter);
540 std::function<bool()> can_go_previous_getter = [
this]()
543 return d->track_list->has_previous() or Parent::loop_status() != Player::LoopStatus::none;
545 Parent::can_go_previous().install(can_go_previous_getter);
550 MH_INFO(
"LoopStatus: %s", loop_status);
551 d->track_list->on_loop_status_changed(loop_status);
555 Parent::shuffle().changed().connect([
this](
bool shuffle)
557 d->track_list->on_shuffle_changed(shuffle);
564 d->engine->audio_stream_role().set(new_role);
571 Parent::orientation().set(o);
576 d->engine->lifetime().set(lifetime);
579 d->engine->track_meta_data().changed().connect([
this,
config](
580 const std::tuple<media::Track::UriType, media::Track::MetaData>& md)
582 d->update_mpris_metadata(std::get<0>(md), std::get<1>(md));
585 d->engine->about_to_finish_signal().connect([
this]()
587 if (d->doing_abandon)
593 d->doing_go_to_track.lock();
595 Parent::about_to_finish()();
600 const Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
601 if (prev_track_id != d->track_list->current() && !uri.empty())
603 MH_INFO(
"Advancing to next track on playbin: %s", uri);
604 static const bool do_pipeline_reset = false;
605 d->engine->open_resource_for_uri(uri, do_pipeline_reset);
608 d->doing_go_to_track.unlock();
611 d->engine->client_disconnected_signal().connect([
this]()
615 d->clear_wakelocks();
616 d->track_list->reset();
620 if (d->is_multimedia_role() and d->is_current_player())
622 MH_DEBUG(
"==== Resetting current player");
623 if (not d->reset_current_player())
624 MH_WARNING(
"Failed to reset current player");
628 d->on_client_disconnected();
631 d->engine->seeked_to_signal().connect([
this](uint64_t value)
633 Parent::seeked_to()(value);
636 d->engine->on_buffering_changed_signal().connect([
this](
int value)
638 Parent::buffering_changed()(value);
641 d->engine->end_of_stream_signal().connect([
this]()
643 Parent::end_of_stream()();
648 Parent::video_dimension_changed()(dimensions);
651 d->engine->error_signal().connect([
this](
const Player::Error& e)
656 d->track_list->on_end_of_tracklist().connect([
this]()
661 MH_INFO(
"End of tracklist reached, stopping playback");
662 const constexpr bool use_main_thread =
true;
663 d->engine->stop(use_main_thread);
670 const bool locked = d->doing_go_to_track.try_lock();
679 const Track::UriType uri = d->track_list->query_uri_for_track(
id);
682 MH_INFO(
"Setting next track on playbin (on_go_to_track signal): %s", uri);
683 MH_INFO(
"\twith a Track::Id: %s",
id);
684 static const bool do_pipeline_reset =
true;
685 d->engine->open_resource_for_uri(uri, do_pipeline_reset);
690 MH_DEBUG(
"Restoring playing state");
694 d->doing_go_to_track.unlock();
699 MH_TRACE(
"** Track was added, handling in PlayerImplementation");
700 if (d->track_list->tracks()->size() == 1)
701 d->open_first_track_from_tracklist(
id);
703 d->update_mpris_properties();
708 MH_TRACE(
"** Track was added, handling in PlayerImplementation");
712 if (tracks.size() >= 1 and d->track_list->tracks()->size() == tracks.size())
713 d->open_first_track_from_tracklist(tracks.front());
715 d->update_mpris_properties();
720 d->update_mpris_properties();
723 d->track_list->on_track_list_reset().connect([
this](
void)
725 d->update_mpris_properties();
730 d->update_mpris_properties();
733 d->track_list->on_track_list_replaced().connect(
736 d->update_mpris_properties();
741 std::weak_ptr<Private> wp{d};
743 d->config.client_death_observer->register_for_death_notifications_with_key(
config.key);
746 if (
auto sp = wp.lock())
748 if (sp->doing_abandon)
751 if (died != sp->config.key)
754 static const std::chrono::milliseconds timeout{1000};
757 if (auto sp = wp.lock())
758 sp->on_client_died();
764template<
typename Parent>
770 std::function<uint64_t()> position_getter = [
this]()
772 return static_cast<uint64_t
>(0);
774 Parent::position().install(position_getter);
776 std::function<uint64_t()> duration_getter = [
this]()
778 return static_cast<uint64_t
>(0);
780 Parent::duration().install(duration_getter);
782 std::function<bool()> video_type_getter = [
this]()
786 Parent::is_video_source().install(video_type_getter);
788 std::function<bool()> audio_type_getter = [
this]()
792 Parent::is_audio_source().install(audio_type_getter);
795template<
typename Parent>
799 return std::string{};
802template<
typename Parent>
805 d->config.client_death_observer->register_for_death_notifications_with_key(d->config.key);
808template<
typename Parent>
812 d->doing_abandon =
true;
816template<
typename Parent>
819 return d->track_list;
823template<
typename Parent>
826 return d->config.key;
829template<
typename Parent>
832 d->engine->create_video_sink(texture_id);
833 return media::video::Sink::Ptr{};
836template<
typename Parent>
839 d->track_list->reset();
844 MH_DEBUG(
"Resetting current media");
848 static const bool do_pipeline_reset =
false;
849 const bool ret = d->engine->open_resource_for_uri(uri, do_pipeline_reset);
851 static const bool make_current =
false;
857template<
typename Parent>
860 return d->engine->open_resource_for_uri(uri, headers);
863template<
typename Parent>
866 d->track_list->next();
869template<
typename Parent>
872 d->track_list->previous();
875template<
typename Parent>
879 if (d->is_multimedia_role())
881 MH_DEBUG(
"==== Pausing all other multimedia player sessions");
882 if (not d->pause_other_players(d->config.key))
883 MH_WARNING(
"Failed to pause other player sessions");
885 MH_DEBUG(
"==== Updating the current player");
888 if (not d->update_current_player(d->config.key))
889 MH_WARNING(
"Failed to update current player");
895template<
typename Parent>
902template<
typename Parent>
909template<
typename Parent>
912 d->engine->seek_to(ms);
915template<
typename Parent>
918 return d->on_client_disconnected;
921template<
typename Parent>
924 Parent::playback_status_changed()(status);