Music Hub  ..
A session-wide music playback service
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
player_implementation.cpp
Go to the documentation of this file.
1 /*
2  *
3  * This program is free software: you can redistribute it and/or modify it
4  * under the terms of the GNU Lesser General Public License version 3,
5  * as published by the Free Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU Lesser General Public License for more details.
11  *
12  * You should have received a copy of the GNU Lesser General Public License
13  * along with this program. If not, see <http://www.gnu.org/licenses/>.
14  *
15  * Authored by: Thomas Voß <thomas.voss@canonical.com>
16  * Jim Hodapp <jim.hodapp@canonical.com>
17  */
18 
19 #include "player_implementation.h"
20 #include "util/timeout.h"
21 
22 #include <unistd.h>
23 
24 #include "engine.h"
26 
27 #include "powerd_service.h"
28 #include "unity_screen_service.h"
29 #include "gstreamer/engine.h"
30 
31 #include <exception>
32 #include <iostream>
33 #include <mutex>
34 
35 #define UNUSED __attribute__((unused))
36 
37 namespace media = core::ubuntu::media;
38 namespace dbus = core::dbus;
39 
40 using namespace std;
41 
43 {
44  enum class wakelock_clear_t
45  {
46  WAKELOCK_CLEAR_INACTIVE,
47  WAKELOCK_CLEAR_DISPLAY,
48  WAKELOCK_CLEAR_SYSTEM,
49  WAKELOCK_CLEAR_INVALID
50  };
51 
52  Private(PlayerImplementation* parent,
53  const dbus::types::ObjectPath& session_path,
54  const std::shared_ptr<media::Service>& service,
55  PlayerImplementation::PlayerKey key)
56  : parent(parent),
57  service(service),
58  engine(std::make_shared<gstreamer::Engine>()),
59  session_path(session_path),
60  track_list(
61  new media::TrackListImplementation(
62  session_path.as_string() + "/TrackList",
63  engine->meta_data_extractor())),
64  sys_lock_name("media-hub-music-playback"),
65  disp_cookie(-1),
66  system_wakelock_count(0),
67  display_wakelock_count(0),
68  previous_state(Engine::State::stopped),
69  key(key)
70  {
71  auto bus = std::shared_ptr<dbus::Bus>(new dbus::Bus(core::dbus::WellKnownBus::system));
72  bus->install_executor(dbus::asio::make_executor(bus));
73 
74  auto stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::Powerd>::interface_name());
75  powerd_session = stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/powerd"));
76 
77  auto uscreen_stub_service = dbus::Service::use_service(bus, dbus::traits::Service<core::UScreen>::interface_name());
78  uscreen_session = uscreen_stub_service->object_for_path(dbus::types::ObjectPath("/com/canonical/Unity/Screen"));
79 
80  /*
81  * Wakelock state logic:
82  *
83  * PLAYING->READY: delay 4 seconds and try to clear current wakelock type
84  * PLAYING->PAUSED or PLAYING->STOPPED: delay 4 seconds and try to clear current wakelock type
85  * READY->PAUSED: request a new wakelock (system or display)
86  * PLAYING->PAUSED: delay 4 seconds and try to clear current wakelock type
87  */
88  engine->state().changed().connect(
89  [parent, this](const Engine::State& state)
90  {
91  switch(state)
92  {
93  case Engine::State::ready:
94  {
95  parent->playback_status().set(media::Player::ready);
96  if (previous_state == Engine::State::playing)
97  {
98  wakelock_timeout.reset(new timeout(4000, true, std::bind(&Private::clear_wakelock,
99  this, std::placeholders::_1), current_wakelock_type()));
100  }
101  break;
102  }
103  case Engine::State::playing:
104  {
105  parent->playback_status().set(media::Player::playing);
106  if (previous_state == Engine::State::stopped || previous_state == Engine::State::paused)
107  {
108  request_power_state();
109  }
110  break;
111  }
112  case Engine::State::stopped:
113  {
114  parent->playback_status().set(media::Player::stopped);
115  break;
116  }
117  case Engine::State::paused:
118  {
119  parent->playback_status().set(media::Player::paused);
120  if (previous_state == Engine::State::ready)
121  {
122  request_power_state();
123  }
124  else if (previous_state == Engine::State::playing)
125  {
126  wakelock_timeout.reset(new timeout(4000, true, std::bind(&Private::clear_wakelock,
127  this, std::placeholders::_1), current_wakelock_type()));
128  }
129  break;
130  }
131  default:
132  break;
133  };
134 
135  // Keep track of the previous Engine playback state:
136  previous_state = state;
137  });
138 
139  }
140 
142  {
143  // Make sure that we don't hold on to the wakelocks if media-hub-server
144  // ever gets restarted manually or automatically
145  clear_wakelocks();
146  }
147 
149  {
150  try
151  {
152  if (parent->is_video_source())
153  {
154  if (display_wakelock_count == 0)
155  {
156  auto result = uscreen_session->invoke_method_synchronously<core::UScreen::keepDisplayOn, int>();
157  if (result.is_error())
158  throw std::runtime_error(result.error().print());
159  disp_cookie = result.value();
160  cout << "Requested new display wakelock" << endl;
161  }
162 
163  {
164  // Keep track of how many display wakelocks have been requested
165  std::lock_guard<std::mutex> lock(wakelock_mutex);
166  ++display_wakelock_count;
167  }
168  }
169  else
170  {
171  if (system_wakelock_count == 0)
172  {
173  auto result = powerd_session->invoke_method_synchronously<core::Powerd::requestSysState, std::string>(sys_lock_name, static_cast<int>(1));
174  if (result.is_error())
175  throw std::runtime_error(result.error().print());
176  sys_cookie = result.value();
177  cout << "Requested new system wakelock" << endl;
178  }
179 
180  {
181  // Keep track of how many system wakelocks have been requested
182  std::lock_guard<std::mutex> lock(wakelock_mutex);
183  ++system_wakelock_count;
184  }
185  }
186  }
187  catch(const std::exception& e)
188  {
189  std::cerr << "Warning: failed to request power state: ";
190  std::cerr << e.what() << std::endl;
191  }
192  }
193 
194  void clear_wakelock(const wakelock_clear_t &wakelock)
195  {
196  cout << __PRETTY_FUNCTION__ << endl;
197  try
198  {
199  switch (wakelock)
200  {
201  case wakelock_clear_t::WAKELOCK_CLEAR_INACTIVE:
202  break;
203  case wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM:
204  {
205  std::lock_guard<std::mutex> lock(wakelock_mutex);
206  --system_wakelock_count;
207  }
208  // Only actually clear the system wakelock once the count reaches zero
209  if (system_wakelock_count == 0)
210  {
211  cout << "Clearing system wakelock" << endl;
212  powerd_session->invoke_method_synchronously<core::Powerd::clearSysState, void>(sys_cookie);
213  sys_cookie.clear();
214  }
215  break;
216  case wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY:
217  {
218  std::lock_guard<std::mutex> lock(wakelock_mutex);
219  --display_wakelock_count;
220  }
221  // Only actually clear the display wakelock once the count reaches zero
222  if (display_wakelock_count == 0)
223  {
224  cout << "Clearing display wakelock" << endl;
225  uscreen_session->invoke_method_synchronously<core::UScreen::removeDisplayOnRequest, void>(disp_cookie);
226  disp_cookie = -1;
227  }
228  break;
229  case wakelock_clear_t::WAKELOCK_CLEAR_INVALID:
230  default:
231  cerr << "Can't clear invalid wakelock type" << endl;
232  }
233  }
234  catch(const std::exception& e)
235  {
236  std::cerr << "Warning: failed to clear power state: ";
237  std::cerr << e.what() << std::endl;
238  }
239  }
240 
242  {
243  return (parent->is_video_source()) ?
244  wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY : wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM;
245  }
246 
248  {
249  // Clear both types of wakelocks (display and system)
250  if (system_wakelock_count > 0)
251  {
252  {
253  std::lock_guard<std::mutex> lock(wakelock_mutex);
254  system_wakelock_count = 1;
255  }
256  clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_SYSTEM);
257  }
258  if (display_wakelock_count > 0)
259  {
260  {
261  std::lock_guard<std::mutex> lock(wakelock_mutex);
262  display_wakelock_count = 1;
263  }
264  clear_wakelock(wakelock_clear_t::WAKELOCK_CLEAR_DISPLAY);
265  }
266  }
267 
268  PlayerImplementation* parent;
269  std::shared_ptr<Service> service;
270  std::shared_ptr<Engine> engine;
271  dbus::types::ObjectPath session_path;
272  std::shared_ptr<TrackListImplementation> track_list;
273  std::shared_ptr<dbus::Object> powerd_session;
274  std::shared_ptr<dbus::Object> uscreen_session;
275  std::string sys_lock_name;
277  std::string sys_cookie;
278  std::mutex wakelock_mutex;
281  std::unique_ptr<timeout> wakelock_timeout;
282  Engine::State previous_state;
283  PlayerImplementation::PlayerKey key;
284 };
285 
287  const dbus::types::ObjectPath& session_path,
288  const std::shared_ptr<Service>& service,
289  PlayerKey key)
290  : media::PlayerSkeleton(session_path),
291  d(new Private(
292  this,
293  session_path,
294  service,
295  key))
296 {
297  // Initializing default values for properties
298  can_play().set(true);
299  can_pause().set(true);
300  can_seek().set(true);
301  can_go_previous().set(true);
302  can_go_next().set(true);
303  is_video_source().set(false);
304  is_audio_source().set(false);
305  is_shuffle().set(true);
306  playback_rate().set(1.f);
307  playback_status().set(Player::PlaybackStatus::null);
308  loop_status().set(Player::LoopStatus::none);
309  position().set(0);
310  duration().set(0);
311 
312  // Make sure that the Position property gets updated from the Engine
313  // every time the client requests position
314  std::function<uint64_t()> position_getter = [this]()
315  {
316  return d->engine->position().get();
317  };
318  position().install(position_getter);
319 
320  // Make sure that the Duration property gets updated from the Engine
321  // every time the client requests duration
322  std::function<uint64_t()> duration_getter = [this]()
323  {
324  return d->engine->duration().get();
325  };
326  duration().install(duration_getter);
327 
328  std::function<bool()> video_type_getter = [this]()
329  {
330  return d->engine->is_video_source().get();
331  };
332  is_video_source().install(video_type_getter);
333 
334  std::function<bool()> audio_type_getter = [this]()
335  {
336  return d->engine->is_audio_source().get();
337  };
338  is_audio_source().install(audio_type_getter);
339 
340  d->engine->about_to_finish_signal().connect([this]()
341  {
342  if (d->track_list->has_next())
343  {
344  Track::UriType uri = d->track_list->query_uri_for_track(d->track_list->next());
345  if (!uri.empty())
346  d->parent->open_uri(uri);
347  }
348  });
349 
350  d->engine->client_disconnected_signal().connect([this]()
351  {
352  // If the client disconnects, make sure both wakelock types
353  // are cleared
354  d->clear_wakelocks();
355  });
356 
357  d->engine->seeked_to_signal().connect([this](uint64_t value)
358  {
359  seeked_to()(value);
360  });
361 
362  d->engine->end_of_stream_signal().connect([this]()
363  {
364  end_of_stream()();
365  });
366 
367  d->engine->playback_status_changed_signal().connect([this](const Player::PlaybackStatus& status)
368  {
369  playback_status_changed()(status);
370  });
371 }
372 
374 {
375 }
376 
377 std::shared_ptr<media::TrackList> media::PlayerImplementation::track_list()
378 {
379  return d->track_list;
380 }
381 
382 // TODO: Convert this to be a property instead of sync call
384 {
385  return d->key;
386 }
387 
389 {
390  return d->engine->open_resource_for_uri(uri);
391 }
392 
393 void media::PlayerImplementation::create_video_sink(uint32_t texture_id)
394 {
395  d->engine->create_video_sink(texture_id);
396 }
397 
399 {
400  // This method is client-side only and is simply a no-op for the service side
401  return NULL;
402 }
403 
405 {
406 }
407 
409 {
410 }
411 
413 {
414  d->engine->play();
415 }
416 
418 {
419  d->engine->pause();
420 }
421 
423 {
424  d->engine->stop();
425 }
426 
428  UNUSED FrameAvailableCb cb, UNUSED void *context)
429 {
430  // This method is client-side only and is simply a no-op for the service side
431 }
432 
434  UNUSED PlaybackCompleteCb cb, UNUSED void *context)
435 {
436  // This method is client-side only and is simply a no-op for the service side
437 }
438 
439 void media::PlayerImplementation::seek_to(const std::chrono::microseconds& ms)
440 {
441  d->engine->seek_to(ms);
442 }
void * GLConsumerWrapperHybris
Definition: player.h:43
virtual void set_playback_complete_callback(PlaybackCompleteCb cb, void *context)
virtual GLConsumerWrapperHybris gl_consumer() const
virtual const core::Property< PlaybackStatus > & playback_status() const
virtual const core::Property< bool > & is_video_source() const
std::shared_ptr< TrackListImplementation > track_list
virtual const core::Signal< void > & end_of_stream() const
virtual const core::Property< bool > & can_seek() const
virtual const core::Property< bool > & is_shuffle() const
virtual const core::Property< bool > & can_go_next() const
virtual const core::Property< bool > & can_go_previous() const
wakelock_clear_t current_wakelock_type() const
virtual const core::Property< uint64_t > & duration() const
virtual const core::Property< bool > & can_play() const
#define UNUSED
virtual const core::Signal< uint64_t > & seeked_to() const
PlayerImplementation(const core::dbus::types::ObjectPath &session_path, const std::shared_ptr< Service > &service, PlayerKey key)
virtual const core::Property< bool > & can_pause() const
virtual const core::Property< LoopStatus > & loop_status() const
void clear_wakelock(const wakelock_clear_t &wakelock)
virtual const core::Property< PlaybackRate > & playback_rate() const
virtual void create_video_sink(uint32_t texture_id)
Private(PlayerImplementation *parent, const dbus::types::ObjectPath &session_path, const std::shared_ptr< media::Service > &service, PlayerImplementation::PlayerKey key)
std::shared_ptr< dbus::Object > powerd_session
virtual bool open_uri(const Track::UriType &uri)
virtual void seek_to(const std::chrono::microseconds &offset)
virtual void set_frame_available_callback(FrameAvailableCb cb, void *context)
std::string UriType
Definition: track.h:40
virtual core::Signal< PlaybackStatus > & playback_status_changed()
PlayerImplementation::PlayerKey key
virtual const core::Property< bool > & is_audio_source() const
virtual std::shared_ptr< TrackList > track_list()
std::shared_ptr< dbus::Object > uscreen_session
virtual const core::Property< uint64_t > & position() const