Music Hub  ..
A session-wide music playback service
player_implementation.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2013-2015 Canonical Ltd.
3  *
4  * This program is free software: you can redistribute it and/or modify it
5  * under the terms of the GNU Lesser General Public License version 3,
6  * as published by the Free Software Foundation.
7  *
8  * This program is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11  * GNU Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program. If not, see <http://www.gnu.org/licenses/>.
15  *
16  * Authored by: Thomas Voß <thomas.voss@canonical.com>
17  * Jim Hodapp <jim.hodapp@canonical.com>
18  */
19 
20 #include "player_implementation.h"
21 #include "util/timeout.h"
22 
23 #include <unistd.h>
24 
25 #include "client_death_observer.h"
26 #include "engine.h"
28 
29 #include "gstreamer/engine.h"
30 
31 #include <memory>
32 #include <exception>
33 #include <iostream>
34 #include <mutex>
35 
36 #define UNUSED __attribute__((unused))
37 
38 namespace media = core::ubuntu::media;
39 namespace dbus = core::dbus;
40 
41 using namespace std;
42 
43 template<typename Parent>
45  public std::enable_shared_from_this<Private>
46 {
47  enum class wakelock_clear_t
48  {
49  WAKELOCK_CLEAR_INACTIVE,
50  WAKELOCK_CLEAR_DISPLAY,
51  WAKELOCK_CLEAR_SYSTEM,
52  WAKELOCK_CLEAR_INVALID
53  };
54 
55  Private(PlayerImplementation* parent, const media::PlayerImplementation<Parent>::Configuration& config)
56  : parent(parent),
57  config(config),
58  display_state_lock(config.power_state_controller->display_state_lock()),
59  system_state_lock(config.power_state_controller->system_state_lock()),
60  engine(std::make_shared<gstreamer::Engine>()),
61  track_list(std::make_shared<TrackListImplementation>(
62  config.parent.bus,
63  config.parent.service->add_object_for_path(
64  dbus::types::ObjectPath(config.parent.session->path().as_string() + "/TrackList")),
65  engine->meta_data_extractor(),
66  config.parent.request_context_resolver,
67  config.parent.request_authenticator)),
68  system_wakelock_count(0),
69  display_wakelock_count(0),
70  previous_state(Engine::State::stopped),
71  engine_state_change_connection(engine->state().changed().connect(make_state_change_handler())),
72  engine_playback_status_change_connection(engine->playback_status_changed_signal().connect(make_playback_status_change_handler())),
73  doing_abandon(false)
74  {
75  // Poor man's logging of release/acquire events.
76  display_state_lock->acquired().connect([](media::power::DisplayState state)
77  {
78  std::cout << "Acquired new display state: " << state << std::endl;
79  });
80 
81  display_state_lock->released().connect([](media::power::DisplayState state)
82  {
83  std::cout << "Released display state: " << state << std::endl;
84  });
85 
86  system_state_lock->acquired().connect([](media::power::SystemState state)
87  {
88  std::cout << "Acquired new system state: " << state << std::endl;
89  });
90 
91  system_state_lock->released().connect([](media::power::SystemState state)
92  {
93  std::cout << "Released system state: " << state << std::endl;
94  });
95  }
96 
98  {
99  // Make sure that we don't hold on to the wakelocks if media-hub-server
100  // ever gets restarted manually or automatically
101  clear_wakelocks();
102 
103  // The engine destructor can lead to a stop change state which will
104  // trigger the state change handler. Ensure the handler is not called
105  // by disconnecting the state change signal
106  engine_state_change_connection.disconnect();
107 
108  // The engine destructor can lead to a playback status change which will
109  // trigger the playback status change handler. Ensure the handler is not called
110  // by disconnecting the playback status change signal
111  engine_playback_status_change_connection.disconnect();
112  }
113 
114  std::function<void(const Engine::State& state)> make_state_change_handler()
115  {
116  /*
117  * Wakelock state logic:
118  * PLAYING->READY or PLAYING->PAUSED or PLAYING->STOPPED: delay 4 seconds and try to clear current wakelock type
119  * ANY STATE->PLAYING: request a new wakelock (system or display)
120  */
121  return [this](const Engine::State& state)
122  {
123  std::cout << "Setting state for parent: " << parent << std::endl;
124  switch(state)
125  {
126  case Engine::State::ready:
127  {
128  parent->playback_status().set(media::Player::ready);
129  if (previous_state == Engine::State::playing)
130  {
131  timeout(4000, true, make_clear_wakelock_functor());
132  }
133  break;
134  }
135  case Engine::State::playing:
136  {
137  // We update the track meta data prior to updating the playback status.
138  // Some MPRIS clients expect this order of events.
139  parent->meta_data_for_current_track().set(std::get<1>(engine->track_meta_data().get()));
140  // And update our playback status.
141  parent->playback_status().set(media::Player::playing);
142  std::cout << "Requesting power state" << std::endl;
143  request_power_state();
144  break;
145  }
146  case Engine::State::stopped:
147  {
148  parent->playback_status().set(media::Player::stopped);
149  if (previous_state == Engine::State::playing)
150  {
151  timeout(4000, true, make_clear_wakelock_functor());
152  }
153  break;
154  }
155  case Engine::State::paused:
156  {
157  parent->playback_status().set(media::Player::paused);
158  if (previous_state == Engine::State::playing)
159  {
160  timeout(4000, true, make_clear_wakelock_functor());
161  }
162  break;
163  }
164  default:
165  break;
166  };
167 
168  // Keep track of the previous Engine playback state:
169  previous_state = state;
170  };
171  }
172 
173  std::function<void(const media::Player::PlaybackStatus& status)> make_playback_status_change_handler()
174  {
175  return [this](const media::Player::PlaybackStatus& status)
176  {
177  std::cout << "Emiting playback_status_changed signal: " << status << std::endl;
178  parent->emit_playback_status_changed(status);
179  };
180  }
181 
183  {
184  std::cout << __PRETTY_FUNCTION__ << std::endl;
185  try
186  {
187  if (parent->is_video_source())
188  {
189  if (++display_wakelock_count == 1)
190  {
191  std::cout << "Requesting new display wakelock." << std::endl;
192  display_state_lock->request_acquire(media::power::DisplayState::on);
193  std::cout << "Requested new display wakelock." << std::endl;
194  }
195  }
196  else
197  {
198  if (++system_wakelock_count == 1)
199  {
200  std::cout << "Requesting new system wakelock." << std::endl;
201  system_state_lock->request_acquire(media::power::SystemState::active);
202  std::cout << "Requested new system wakelock." << std::endl;
203  }
204  }
205  }
206  catch(const std::exception& e)
207  {
208  std::cerr << "Warning: failed to request power state: ";
209  std::cerr << e.what() << std::endl;
210  }
211  }
212 
213  void clear_wakelock(const wakelock_clear_t &wakelock)
214  {
215  cout << __PRETTY_FUNCTION__ << endl;
216  try
217  {
218  switch (wakelock)
219  {
220  case wakelock_clear_t::WAKELOCK_CLEAR_INACTIVE:
221  break;
222  case wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM:
223  // Only actually clear the system wakelock once the count reaches zero
224  if (--system_wakelock_count == 0)
225  {
226  std::cout << "Clearing system wakelock." << std::endl;
227  system_state_lock->request_release(media::power::SystemState::active);
228  }
229  break;
230  case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:
231  // Only actually clear the display wakelock once the count reaches zero
232  if (--display_wakelock_count == 0)
233  {
234  std::cout << "Clearing display wakelock." << std::endl;
235  display_state_lock->request_release(media::power::DisplayState::on);
236  }
237  break;
238  case wakelock_clear_t::WAKELOCK_CLEAR_INVALID:
239  default:
240  cerr << "Can't clear invalid wakelock type" << endl;
241  }
242  }
243  catch(const std::exception& e)
244  {
245  std::cerr << "Warning: failed to clear power state: ";
246  std::cerr << e.what() << std::endl;
247  }
248  }
249 
251  {
252  return (parent->is_video_source()) ?
253  wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY : wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM;
254  }
255 
257  {
258  // Clear both types of wakelocks (display and system)
259  if (system_wakelock_count.load() > 0)
260  {
261  system_wakelock_count = 1;
262  clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM);
263  }
264  if (display_wakelock_count.load() > 0)
265  {
266  display_wakelock_count = 1;
267  clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY);
268  }
269  }
270 
271  std::function<void()> make_clear_wakelock_functor()
272  {
273  // Since this functor will be executed on a separate detached thread
274  // the execution of the functor may surpass the lifetime of this Private
275  // object instance. By keeping a weak_ptr to the private object instance
276  // we can check if the object is dead before calling methods on it
277  std::weak_ptr<Private> weak_self{this->shared_from_this()};
278  auto wakelock_type = current_wakelock_type();
279  return [weak_self, wakelock_type] {
280  if (auto self = weak_self.lock())
281  self->clear_wakelock(wakelock_type);
282  };
283  }
284 
286  {
287  engine->reset();
288  }
289 
291  {
292  const Track::UriType uri = track_list->query_uri_for_track(id);
293  if (!uri.empty())
294  {
295  // Using a TrackList for playback, added tracks via add_track(), but open_uri hasn't
296  // been called yet to load a media resource
297  std::cout << "Calling d->engine->open_resource_for_uri() for first track added only: "
298  << uri << std::endl;
299  std::cout << "\twith a Track::Id: " << id << std::endl;
300  static const bool do_pipeline_reset = false;
301  engine->open_resource_for_uri(uri, do_pipeline_reset);
302  }
303  }
304 
306  {
307  bool has_previous = track_list->has_previous()
308  or parent->Parent::loop_status() != Player::LoopStatus::none;
309  bool has_next = track_list->has_next()
310  or parent->Parent::loop_status() != Player::LoopStatus::none;
311  auto n_tracks = track_list->tracks()->size();
312  bool has_tracks = (n_tracks > 0) ? true : false;
313 
314  std::cout << "Updating MPRIS TrackList properties"
315  << "; Tracks: " << n_tracks
316  << ", has_previous: " << has_previous
317  << ", has_next: " << has_next << std::endl;
318 
319  // Update properties
320  parent->can_play().set(has_tracks);
321  parent->can_pause().set(has_tracks);
322  parent->can_go_previous().set(has_previous);
323  parent->can_go_next().set(has_next);
324  }
325 
326  // Our link back to our parent.
328  // We just store the parameters passed on construction.
330  media::power::StateController::Lock<media::power::DisplayState>::Ptr display_state_lock;
331  media::power::StateController::Lock<media::power::SystemState>::Ptr system_state_lock;
332 
333  std::shared_ptr<Engine> engine;
334  std::shared_ptr<media::TrackListImplementation> track_list;
335  std::atomic<int> system_wakelock_count;
336  std::atomic<int> display_wakelock_count;
337  Engine::State previous_state;
338  core::Signal<> on_client_disconnected;
341  // Prevent the TrackList from auto advancing to the next track
342  std::mutex doing_go_to_track;
343  std::atomic<bool> doing_abandon;
344 };
345 
346 template<typename Parent>
348  : Parent{config.parent},
349  d{std::make_shared<Private>(this, config)}
350 {
351  // Initialize default values for Player interface properties
352  Parent::can_play().set(false);
353  Parent::can_pause().set(false);
354  Parent::can_seek().set(true);
355  Parent::can_go_previous().set(false);
356  Parent::can_go_next().set(false);
357  Parent::is_video_source().set(false);
358  Parent::is_audio_source().set(false);
359  Parent::shuffle().set(false);
360  Parent::playback_rate().set(1.f);
361  Parent::playback_status().set(Player::PlaybackStatus::null);
362  Parent::loop_status().set(Player::LoopStatus::none);
363  Parent::position().set(0);
364  Parent::duration().set(0);
365  Parent::audio_stream_role().set(Player::AudioStreamRole::multimedia);
366  d->engine->audio_stream_role().set(Player::AudioStreamRole::multimedia);
367  Parent::orientation().set(Player::Orientation::rotate0);
368  Parent::lifetime().set(Player::Lifetime::normal);
369  d->engine->lifetime().set(Player::Lifetime::normal);
370 
371  // Make sure that the Position property gets updated from the Engine
372  // every time the client requests position
373  std::function<uint64_t()> position_getter = [this]()
374  {
375  return d->engine->position().get();
376  };
377  Parent::position().install(position_getter);
378 
379  d->engine->position().changed().connect([this](uint64_t position)
380  {
381  d->track_list->on_position_changed(position);
382  });
383 
384  // Make sure that the Duration property gets updated from the Engine
385  // every time the client requests duration
386  std::function<uint64_t()> duration_getter = [this]()
387  {
388  return d->engine->duration().get();
389  };
390  Parent::duration().install(duration_getter);
391 
392  std::function<bool()> video_type_getter = [this]()
393  {
394  return d->engine->is_video_source().get();
395  };
396  Parent::is_video_source().install(video_type_getter);
397 
398  std::function<bool()> audio_type_getter = [this]()
399  {
400  return d->engine->is_audio_source().get();
401  };
402  Parent::is_audio_source().install(audio_type_getter);
403 
404  std::function<bool()> can_go_next_getter = [this]()
405  {
406  // If LoopStatus == playlist, then there is always a next track
407  return d->track_list->has_next() or Parent::loop_status() != Player::LoopStatus::none;
408  };
409  Parent::can_go_next().install(can_go_next_getter);
410 
411  std::function<bool()> can_go_previous_getter = [this]()
412  {
413  // If LoopStatus == playlist, then there is always a next previous
414  return d->track_list->has_previous() or Parent::loop_status() != Player::LoopStatus::none;
415  };
416  Parent::can_go_previous().install(can_go_previous_getter);
417 
418  // When the client changes the loop status, make sure to update the TrackList
419  Parent::loop_status().changed().connect([this](media::Player::LoopStatus loop_status)
420  {
421  std::cout << "LoopStatus: " << loop_status << std::endl;
422  d->track_list->on_loop_status_changed(loop_status);
423  });
424 
425  // When the client changes the shuffle setting, make sure to update the TrackList
426  Parent::shuffle().changed().connect([this](bool shuffle)
427  {
428  d->track_list->on_shuffle_changed(shuffle);
429  });
430 
431  // Make sure that the audio_stream_role property gets updated on the Engine side
432  // whenever the client side sets the role
433  Parent::audio_stream_role().changed().connect([this](media::Player::AudioStreamRole new_role)
434  {
435  d->engine->audio_stream_role().set(new_role);
436  });
437 
438  // When the value of the orientation Property is changed in the Engine by playbin,
439  // update the Player's cached value
440  d->engine->orientation().changed().connect([this](const Player::Orientation& o)
441  {
442  Parent::orientation().set(o);
443  });
444 
445  Parent::lifetime().changed().connect([this](media::Player::Lifetime lifetime)
446  {
447  d->engine->lifetime().set(lifetime);
448  });
449 
450  d->engine->about_to_finish_signal().connect([this]()
451  {
452  if (d->doing_abandon)
453  return;
454 
455  // Prevent on_go_to_track from executing as it's not needed in this case. on_go_to_track
456  // (see the lambda below) is only needed when the client explicitly calls next() not during
457  // the about_to_finish condition
458  d->doing_go_to_track.lock();
459 
460  Parent::about_to_finish()();
461 
462  const media::Track::Id prev_track_id = d->track_list->current();
463  // Make sure that the TrackList keeps advancing. The logic for what gets played next,
464  // if anything at all, occurs in TrackListSkeleton::next()
465  const Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
466  if (prev_track_id != d->track_list->current() && !uri.empty())
467  {
468  std::cout << "Advancing to next track on playbin: " << uri << std::endl;
469  static const bool do_pipeline_reset = false;
470  d->engine->open_resource_for_uri(uri, do_pipeline_reset);
471  }
472 
473  d->doing_go_to_track.unlock();
474  });
475 
476  d->engine->client_disconnected_signal().connect([this]()
477  {
478  // If the client disconnects, make sure both wakelock types
479  // are cleared
480  d->clear_wakelocks();
481  d->track_list->reset();
482  // And tell the outside world that the client has gone away
483  d->on_client_disconnected();
484  });
485 
486  d->engine->seeked_to_signal().connect([this](uint64_t value)
487  {
488  Parent::seeked_to()(value);
489  });
490 
491  d->engine->end_of_stream_signal().connect([this]()
492  {
493  Parent::end_of_stream()();
494  });
495 
496  d->engine->video_dimension_changed_signal().connect([this](const media::video::Dimensions& dimensions)
497  {
498  Parent::video_dimension_changed()(dimensions);
499  });
500 
501  d->engine->error_signal().connect([this](const Player::Error& e)
502  {
503  Parent::error()(e);
504  });
505 
506  d->track_list->on_end_of_tracklist().connect([this]()
507  {
508  if (d->engine->state() != gstreamer::Engine::State::ready
509  && d->engine->state() != gstreamer::Engine::State::stopped)
510  {
511  std::cout << "End of tracklist reached, stopping playback" << std::endl;
512  d->engine->stop();
513  }
514  });
515 
516  d->track_list->on_go_to_track().connect([this](const media::Track::Id& id)
517  {
518  // This lambda needs to be mutually exclusive with the about_to_finish lambda above
519  const bool locked = d->doing_go_to_track.try_lock();
520  // If the try_lock fails, it means that about_to_finish lambda above has it locked and it will
521  // call d->engine->open_resource_for_uri()
522  if (!locked)
523  return;
524 
525  // Store whether we should restore the current playing state after loading the new uri
526  const bool auto_play = Parent::playback_status().get() == media::Player::playing;
527 
528  const Track::UriType uri = d->track_list->query_uri_for_track(id);
529  if (!uri.empty())
530  {
531  std::cout << "Setting next track on playbin (on_go_to_track signal): " << uri << std::endl;
532  std::cout << "\twith a Track::Id: " << id << std::endl;
533  static const bool do_pipeline_reset = true;
534  d->engine->open_resource_for_uri(uri, do_pipeline_reset);
535  }
536 
537  if (auto_play)
538  {
539  std::cout << "Restoring playing state in on_go_to_track()" << std::endl;
540  d->engine->play();
541  }
542 
543  d->doing_go_to_track.unlock();
544  });
545 
546  d->track_list->on_track_added().connect([this](const media::Track::Id& id)
547  {
548  std::cout << "** Track was added, handling in PlayerImplementation" << std::endl;
549  if (d->track_list->tracks()->size() == 1)
550  d->open_first_track_from_tracklist(id);
551 
552  d->update_mpris_properties();
553  });
554 
555  d->track_list->on_tracks_added().connect([this](const media::TrackList::ContainerURI& tracks)
556  {
557  std::cout << "** Track was added, handling in PlayerImplementation" << std::endl;
558  // If the two sizes are the same, that means the TrackList was previously empty and we need
559  // to open the first track in the TrackList so that is_audio_source() and is_video_source()
560  // will function correctly.
561  if (tracks.size() >= 1 and d->track_list->tracks()->size() == tracks.size())
562  d->open_first_track_from_tracklist(tracks.front());
563 
564  d->update_mpris_properties();
565  });
566 
567  d->track_list->on_track_removed().connect([this](const media::Track::Id&)
568  {
569  d->update_mpris_properties();
570  });
571 
572  d->track_list->on_track_list_reset().connect([this](void)
573  {
574  d->update_mpris_properties();
575  });
576 
577  d->track_list->on_track_changed().connect([this](const media::Track::Id&)
578  {
579  d->update_mpris_properties();
580  });
581 
582  d->track_list->on_track_list_replaced().connect(
584  {
585  d->update_mpris_properties();
586  }
587  );
588 
589  // Everything is setup, we now subscribe to death notifications.
590  std::weak_ptr<Private> wp{d};
591 
592  d->config.client_death_observer->register_for_death_notifications_with_key(config.key);
593  d->config.client_death_observer->on_client_with_key_died().connect([wp](const media::Player::PlayerKey& died)
594  {
595  if (auto sp = wp.lock())
596  {
597  if (sp->doing_abandon)
598  return;
599 
600  if (died != sp->config.key)
601  return;
602 
603  static const std::chrono::milliseconds timeout{1000};
604  media::timeout(timeout.count(), true, [wp]()
605  {
606  if (auto sp = wp.lock())
607  sp->on_client_died();
608  });
609  }
610  });
611 }
612 
613 template<typename Parent>
615 {
616  // Install null getters as these properties may be destroyed
617  // after the engine has been destroyed since they are owned by the
618  // base class.
619  std::function<uint64_t()> position_getter = [this]()
620  {
621  return static_cast<uint64_t>(0);
622  };
623  Parent::position().install(position_getter);
624 
625  std::function<uint64_t()> duration_getter = [this]()
626  {
627  return static_cast<uint64_t>(0);
628  };
629  Parent::duration().install(duration_getter);
630 
631  std::function<bool()> video_type_getter = [this]()
632  {
633  return false;
634  };
635  Parent::is_video_source().install(video_type_getter);
636 
637  std::function<bool()> audio_type_getter = [this]()
638  {
639  return false;
640  };
641  Parent::is_audio_source().install(audio_type_getter);
642 }
643 
644 template<typename Parent>
646 {
647  // No impl for now, as not needed internally.
648  return std::string{};
649 }
650 
651 template<typename Parent>
653 {
654  d->config.client_death_observer->register_for_death_notifications_with_key(d->config.key);
655 }
656 
657 template<typename Parent>
659 {
660  // Signal client disconnection due to abandonment of player
661  d->doing_abandon = true;
662  d->on_client_died();
663 }
664 
665 template<typename Parent>
666 std::shared_ptr<media::TrackList> media::PlayerImplementation<Parent>::track_list()
667 {
668  return d->track_list;
669 }
670 
671 // TODO: Convert this to be a property instead of sync call
672 template<typename Parent>
674 {
675  return d->config.key;
676 }
677 
678 template<typename Parent>
679 media::video::Sink::Ptr media::PlayerImplementation<Parent>::create_gl_texture_video_sink(std::uint32_t texture_id)
680 {
681  d->engine->create_video_sink(texture_id);
682  return media::video::Sink::Ptr{};
683 }
684 
685 template<typename Parent>
687 {
688  d->track_list->reset();
689 
690  // If empty uri, give the same meaning as QMediaPlayer::setMedia("")
691  if (uri.empty()) {
692  cout << __PRETTY_FUNCTION__ << ": resetting current media" << endl;
693  return true;
694  }
695 
696  static const bool do_pipeline_reset = false;
697  const bool ret = d->engine->open_resource_for_uri(uri, do_pipeline_reset);
698  // Don't set new track as the current track to play since we're calling open_resource_for_uri above
699  static const bool make_current = false;
700  d->track_list->add_track_with_uri_at(uri, media::TrackList::after_empty_track(), make_current);
701  return ret;
702 }
703 
704 template<typename Parent>
706 {
707  return d->engine->open_resource_for_uri(uri, headers);
708 }
709 
710 template<typename Parent>
712 {
713  d->track_list->next();
714 }
715 
716 template<typename Parent>
718 {
719  d->track_list->previous();
720 }
721 
722 template<typename Parent>
724 {
725  d->engine->play();
726 }
727 
728 template<typename Parent>
730 {
731  d->engine->pause();
732 }
733 
734 template<typename Parent>
736 {
737  std::cout << __PRETTY_FUNCTION__ << std::endl;
738  d->engine->stop();
739 }
740 
741 template<typename Parent>
742 void media::PlayerImplementation<Parent>::seek_to(const std::chrono::microseconds& ms)
743 {
744  d->engine->seek_to(ms);
745 }
746 
747 template<typename Parent>
749 {
750  return d->on_client_disconnected;
751 }
752 
753 template<typename Parent>
755 {
756  Parent::playback_status_changed()(status);
757 }
758 
760 
761 // For linking purposes, we have to make sure that we have all symbols included within the dso.
Private(PlayerImplementation *parent, const media::PlayerImplementation< Parent >::Configuration &config)
virtual bool open_uri(const Track::UriType &uri)
PlayerImplementation(const Configuration &configuration)
std::tuple< Height, Width > Dimensions
Height and Width of a video.
Definition: dimensions.h:139
void emit_playback_status_changed(const Player::PlaybackStatus &status)
virtual Player::PlayerKey key() const
Definition: bus.h:33
std::shared_ptr< media::TrackListImplementation > track_list
media::PlayerImplementation< Parent >::Configuration config
STL namespace.
std::map< std::string, std::string > HeadersType
Definition: player.h:49
wakelock_clear_t current_wakelock_type() const
std::tuple< std::vector< Track::Id >, Track::Id > ContainerTrackIdTuple
Definition: track_list.h:45
media::PlayerImplementation< Parent > * parent
virtual std::string uuid() const
virtual video::Sink::Ptr create_gl_texture_video_sink(std::uint32_t texture_id)
void clear_wakelock(const wakelock_clear_t &wakelock)
media::power::StateController::Lock< media::power::DisplayState >::Ptr display_state_lock
void open_first_track_from_tracklist(const media::Track::Id &id)
virtual std::shared_ptr< TrackList > track_list()
const core::Signal & on_client_disconnected() const
std::string UriType
Definition: track.h:40
std::function< void(const Engine::State &state)> make_state_change_handler()
std::function< void(const media::Player::PlaybackStatus &status)> make_playback_status_change_handler()
static const Track::Id & after_empty_track()
Definition: track_list.cpp:50
std::vector< Track::UriType > ContainerURI
Definition: track_list.h:44
std::function< void()> make_clear_wakelock_functor()
media::power::StateController::Lock< media::power::SystemState >::Ptr system_state_lock
virtual void seek_to(const std::chrono::microseconds &offset)