Music Hub  ..
A session-wide music playback service
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
player_stub.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2013-2014 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 <core/media/service.h>
21 #include <core/media/track_list.h>
22 
23 #include "codec.h"
24 #include "player_stub.h"
25 #include "player_traits.h"
26 #include "property_stub.h"
27 #include "the_session_bus.h"
28 #include "track_list_stub.h"
29 
30 #include "mpris/player.h"
31 
32 #include <core/dbus/property.h>
33 #include <core/dbus/types/object_path.h>
34 
35 // Hybris
36 #include <hybris/media/media_codec_layer.h>
37 #include <hybris/media/surface_texture_client_hybris.h>
38 
39 #include <limits>
40 
41 #define UNUSED __attribute__((unused))
42 
43 namespace dbus = core::dbus;
44 namespace media = core::ubuntu::media;
45 
47 {
48  Private(const std::shared_ptr<Service>& parent,
49  const std::shared_ptr<dbus::Service>& remote,
50  const dbus::types::ObjectPath& path
51  ) : parent(parent),
52  texture_id(0),
53  igbc_wrapper(nullptr),
54  glc_wrapper(nullptr),
55  decoding_session(decoding_service_create_session()),
56  frame_available_cb(nullptr),
57  frame_available_context(nullptr),
58  path(path),
59  object(remote->object_for_path(path)),
61  {
62  object->get_property<mpris::Player::Properties::CanPlay>(),
63  object->get_property<mpris::Player::Properties::CanPause>(),
64  object->get_property<mpris::Player::Properties::CanSeek>(),
65  object->get_property<mpris::Player::Properties::CanControl>(),
66  object->get_property<mpris::Player::Properties::CanGoNext>(),
67  object->get_property<mpris::Player::Properties::CanGoPrevious>(),
68  object->get_property<mpris::Player::Properties::IsVideoSource>(),
69  object->get_property<mpris::Player::Properties::IsAudioSource>(),
70  object->get_property<mpris::Player::Properties::PlaybackStatus>(),
71  object->get_property<mpris::Player::Properties::LoopStatus>(),
72  object->get_property<mpris::Player::Properties::PlaybackRate>(),
73  object->get_property<mpris::Player::Properties::Shuffle>(),
74  object->get_property<mpris::Player::Properties::MetaData>(),
75  object->get_property<mpris::Player::Properties::Volume>(),
76  object->get_property<mpris::Player::Properties::Position>(),
77  object->get_property<mpris::Player::Properties::Duration>(),
78  object->get_property<mpris::Player::Properties::AudioStreamRole>(),
79  object->get_property<mpris::Player::Properties::MinimumRate>(),
80  object->get_property<mpris::Player::Properties::MaximumRate>()
81  },
82  signals
83  {
84  object->get_signal<mpris::Player::Signals::Seeked>(),
85  object->get_signal<mpris::Player::Signals::EndOfStream>(),
86  object->get_signal<mpris::Player::Signals::PlaybackStatusChanged>()
87  }
88  {
89  }
90 
92  {
93  }
94 
95  static void on_frame_available_cb(UNUSED GLConsumerWrapperHybris wrapper, void *context)
96  {
97  if (context != nullptr) {
98  Private *p = static_cast<Private*>(context);
99  p->on_frame_available();
100  }
101  else
102  std::cerr << "context is nullptr, can't call on_frame_available()" << std::endl;
103  }
104 
106  {
107  if (frame_available_cb != nullptr) {
109  }
110  else
111  std::cerr << "frame_available_cb is nullptr, can't call frame_available_cb()" << std::endl;
112  }
113 
114  void set_frame_available_cb(FrameAvailableCb cb, void *context)
115  {
116  frame_available_cb = cb;
117  frame_available_context = context;
118 
119  gl_consumer_set_frame_available_cb(glc_wrapper, &Private::on_frame_available_cb, static_cast<void*>(this));
120  }
121 
125  {
126  igbc_wrapper = decoding_service_get_igraphicbufferconsumer();
127  glc_wrapper = gl_consumer_create_by_id_with_igbc(texture_id, igbc_wrapper);
128 
129  }
130 
131  std::shared_ptr<Service> parent;
132  std::shared_ptr<TrackList> track_list;
133 
134  uint32_t texture_id;
135  IGBCWrapperHybris igbc_wrapper;
136  GLConsumerWrapperHybris glc_wrapper;
137 
138  DSSessionWrapperHybris decoding_session;
139 
140  FrameAvailableCb frame_available_cb;
142 
143  dbus::Bus::Ptr bus;
144  dbus::types::ObjectPath path;
145  dbus::Object::Ptr object;
146 
147  struct
148  {
149  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPlay>> can_play;
150  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanPause>> can_pause;
151  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanSeek>> can_seek;
152  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanControl>> can_control;
153  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoNext>> can_go_next;
154  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::CanGoPrevious>> can_go_previous;
155  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsVideoSource>> is_video_source;
156  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::IsAudioSource>> is_audio_source;
157 
158  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackStatus>> playback_status;
159  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::LoopStatus>> loop_status;
160  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::PlaybackRate>> playback_rate;
161  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Shuffle>> is_shuffle;
162  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MetaData>> meta_data_for_current_track;
163  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Volume>> volume;
164  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Position>> position;
165  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::Duration>> duration;
166  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::AudioStreamRole>> audio_role;
167  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MinimumRate>> minimum_playback_rate;
168  std::shared_ptr<core::dbus::Property<mpris::Player::Properties::MaximumRate>> maximum_playback_rate;
169  } properties;
170 
171  struct Signals
172  {
173  typedef core::dbus::Signal<mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType> DBusSeekedToSignal;
174  typedef core::dbus::Signal<mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType> DBusEndOfStreamSignal;
175  typedef core::dbus::Signal<mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType> DBusPlaybackStatusChangedSignal;
176 
177  Signals(const std::shared_ptr<DBusSeekedToSignal>& seeked,
178  const std::shared_ptr<DBusEndOfStreamSignal>& eos,
179  const std::shared_ptr<DBusPlaybackStatusChangedSignal>& status)
180  : dbus
181  {
182  seeked,
183  eos,
184  status
185  },
186  playback_complete_cb(nullptr),
187  playback_complete_context(nullptr),
188  seeked_to(),
189  end_of_stream(),
191  {
192  dbus.seeked_to->connect([this](std::uint64_t value)
193  {
194  std::cout << "seeked_to signal arrived via the bus." << std::endl;
195  seeked_to(value);
196  });
197 
198  dbus.end_of_stream->connect([this]()
199  {
200  std::cout << "EndOfStream signal arrived via the bus." << std::endl;
203  end_of_stream();
204  });
205 
206  dbus.playback_status_changed->connect([this](const media::Player::PlaybackStatus& status)
207  {
208  std::cout << "PlaybackStatusChanged signal arrived via the bus." << std::endl;
209  playback_status_changed(status);
210  });
211  }
212 
213  struct DBus
214  {
215  std::shared_ptr<DBusSeekedToSignal> seeked_to;
216  std::shared_ptr<DBusEndOfStreamSignal> end_of_stream;
217  std::shared_ptr<DBusPlaybackStatusChangedSignal> playback_status_changed;
218  } dbus;
219 
220  void set_playback_complete_cb(PlaybackCompleteCb cb, void *context)
221  {
223  playback_complete_context = context;
224  }
225 
226  PlaybackCompleteCb playback_complete_cb;
228  core::Signal<uint64_t> seeked_to;
229  core::Signal<void> end_of_stream;
230  core::Signal<media::Player::PlaybackStatus> playback_status_changed;
231  } signals;
232 };
233 
235  const std::shared_ptr<Service>& parent,
236  const dbus::types::ObjectPath& object_path)
237  : dbus::Stub<Player>(the_session_bus()),
238  d(new Private{parent, access_service(), object_path})
239 {
240  auto bus = the_session_bus();
241  worker = std::move(std::thread([bus]()
242  {
243  bus->run();
244  }));
245 }
246 
248 {
249  auto bus = the_session_bus();
250  bus->stop();
251 
252  if (worker.joinable())
253  worker.join();
254 }
255 
256 std::shared_ptr<media::TrackList> media::PlayerStub::track_list()
257 {
258  if (!d->track_list)
259  {
260  d->track_list = std::make_shared<media::TrackListStub>(
261  shared_from_this(),
262  dbus::types::ObjectPath(d->path.as_string() + "/TrackList"));
263  }
264  return d->track_list;
265 }
266 
268 {
269  auto op = d->object->invoke_method_synchronously<mpris::Player::Key, media::Player::PlayerKey>();
270 
271  return op.value();
272 }
273 
275 {
276  auto op = d->object->invoke_method_synchronously<mpris::Player::OpenUri, bool>(uri);
277 
278  return op.value();
279 }
280 
281 void media::PlayerStub::create_video_sink(uint32_t texture_id)
282 {
283  auto op = d->object->invoke_method_synchronously<mpris::Player::CreateVideoSink, void>(texture_id);
284  d->texture_id = texture_id;
285  d->get_gl_consumer();
286 
287  if (op.is_error())
288  throw std::runtime_error("Problem creating new video sink instance on remote object");
289 }
290 
291 GLConsumerWrapperHybris media::PlayerStub::gl_consumer() const
292 {
293  return d->glc_wrapper;
294 }
295 
297 {
298  auto op = d->object->invoke_method_synchronously<mpris::Player::Next, void>();
299 
300  if (op.is_error())
301  throw std::runtime_error("Problem switching to next track on remote object");
302 }
303 
305 {
306  auto op = d->object->invoke_method_synchronously<mpris::Player::Previous, void>();
307 
308  if (op.is_error())
309  throw std::runtime_error("Problem switching to previous track on remote object");
310 }
311 
313 {
314  auto op = d->object->invoke_method_synchronously<mpris::Player::Play, void>();
315 
316  if (op.is_error())
317  throw std::runtime_error("Problem starting playback on remote object");
318 }
319 
321 {
322  auto op = d->object->invoke_method_synchronously<mpris::Player::Pause, void>();
323 
324  if (op.is_error())
325  throw std::runtime_error("Problem pausing playback on remote object");
326 }
327 
328 void media::PlayerStub::seek_to(const std::chrono::microseconds& offset)
329 {
330  auto op = d->object->invoke_method_synchronously<mpris::Player::Seek, void, uint64_t>(offset.count());
331 
332  if (op.is_error())
333  throw std::runtime_error("Problem seeking on remote object");
334 }
335 
337 {
338  auto op = d->object->invoke_method_synchronously<mpris::Player::Stop, void>();
339 
340  if (op.is_error())
341  throw std::runtime_error("Problem stopping playback on remote object");
342 }
343 
344 void media::PlayerStub::set_frame_available_callback(FrameAvailableCb cb, void *context)
345 {
346  d->set_frame_available_cb(cb, context);
347 }
348 
349 void media::PlayerStub::set_playback_complete_callback(PlaybackCompleteCb cb, void *context)
350 {
351  d->signals.set_playback_complete_cb(cb, context);
352 }
353 
354 const core::Property<bool>& media::PlayerStub::can_play() const
355 {
356  return *d->properties.can_play;
357 }
358 
359 const core::Property<bool>& media::PlayerStub::can_pause() const
360 {
361  return *d->properties.can_pause;
362 }
363 
364 const core::Property<bool>& media::PlayerStub::can_seek() const
365 {
366  return *d->properties.can_seek;
367 }
368 
369 const core::Property<bool>& media::PlayerStub::can_go_previous() const
370 {
371  return *d->properties.can_go_previous;
372 }
373 
374 const core::Property<bool>& media::PlayerStub::can_go_next() const
375 {
376  return *d->properties.can_go_next;
377 }
378 
379 const core::Property<bool>& media::PlayerStub::is_video_source() const
380 {
381  return *d->properties.is_video_source;
382 }
383 
384 const core::Property<bool>& media::PlayerStub::is_audio_source() const
385 {
386  return *d->properties.is_audio_source;
387 }
388 
389 const core::Property<media::Player::PlaybackStatus>& media::PlayerStub::playback_status() const
390 {
391  return *d->properties.playback_status;
392 }
393 
394 const core::Property<media::Player::LoopStatus>& media::PlayerStub::loop_status() const
395 {
396  return *d->properties.loop_status;
397 }
398 
399 const core::Property<media::Player::PlaybackRate>& media::PlayerStub::playback_rate() const
400 {
401  return *d->properties.playback_rate;
402 }
403 
404 const core::Property<bool>& media::PlayerStub::is_shuffle() const
405 {
406  return *d->properties.is_shuffle;
407 }
408 
409 const core::Property<media::Track::MetaData>& media::PlayerStub::meta_data_for_current_track() const
410 {
411  return *d->properties.meta_data_for_current_track;
412 }
413 
414 const core::Property<media::Player::Volume>& media::PlayerStub::volume() const
415 {
416  return *d->properties.volume;
417 }
418 
419 const core::Property<uint64_t>& media::PlayerStub::position() const
420 {
421  return *d->properties.position;
422 }
423 
424 const core::Property<uint64_t>& media::PlayerStub::duration() const
425 {
426  return *d->properties.duration;
427 }
428 
429 const core::Property<media::Player::AudioStreamRole>& media::PlayerStub::audio_stream_role() const
430 {
431  return *d->properties.audio_role;
432 }
433 
434 const core::Property<media::Player::PlaybackRate>& media::PlayerStub::minimum_playback_rate() const
435 {
436  return *d->properties.minimum_playback_rate;
437 }
438 
439 const core::Property<media::Player::PlaybackRate>& media::PlayerStub::maximum_playback_rate() const
440 {
441  return *d->properties.maximum_playback_rate;
442 }
443 
444 core::Property<media::Player::LoopStatus>& media::PlayerStub::loop_status()
445 {
446  return *d->properties.loop_status;
447 }
448 
449 core::Property<media::Player::PlaybackRate>& media::PlayerStub::playback_rate()
450 {
451  return *d->properties.playback_rate;
452 }
453 
454 core::Property<bool>& media::PlayerStub::is_shuffle()
455 {
456  return *d->properties.is_shuffle;
457 }
458 
459 core::Property<media::Player::Volume>& media::PlayerStub::volume()
460 {
461  return *d->properties.volume;
462 }
463 
464 core::Property<media::Player::AudioStreamRole>& media::PlayerStub::audio_stream_role()
465 {
466  return *d->properties.audio_role;
467 }
468 
469 const core::Signal<uint64_t>& media::PlayerStub::seeked_to() const
470 {
471  return d->signals.seeked_to;
472 }
473 
474 const core::Signal<void>& media::PlayerStub::end_of_stream() const
475 {
476  return d->signals.end_of_stream;
477 }
478 
479 core::Signal<media::Player::PlaybackStatus>& media::PlayerStub::playback_status_changed()
480 {
481  return d->signals.playback_status_changed;
482 }
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::CanPlay > > can_play
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::CanControl > > can_control
IGBCWrapperHybris igbc_wrapper
std::shared_ptr< DBusEndOfStreamSignal > end_of_stream
void set_frame_available_cb(FrameAvailableCb cb, void *context)
virtual void create_video_sink(uint32_t texture_id)
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::LoopStatus > > loop_status
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::IsAudioSource > > is_audio_source
core::dbus::Bus::Ptr the_session_bus()
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::MinimumRate > > minimum_playback_rate
virtual const core::Property< bool > & is_video_source() const
struct media::PlayerStub::Private::Signals signals
virtual const core::Signal< void > & end_of_stream() const
core::Signal< void > end_of_stream
static void on_frame_available_cb(UNUSED GLConsumerWrapperHybris wrapper, void *context)
Definition: player_stub.cpp:95
Private(const std::shared_ptr< Service > &parent, const std::shared_ptr< dbus::Service > &remote, const dbus::types::ObjectPath &path)
Definition: player_stub.cpp:48
dbus::Object::Ptr object
std::shared_ptr< DBusSeekedToSignal > seeked_to
virtual const core::Property< bool > & is_audio_source() const
core::dbus::Signal< mpris::Player::Signals::EndOfStream, mpris::Player::Signals::EndOfStream::ArgumentType > DBusEndOfStreamSignal
std::shared_ptr< Service > parent
virtual const core::Property< bool > & can_pause() const
GLConsumerWrapperHybris glc_wrapper
virtual const core::Property< Volume > & volume() const
virtual void set_playback_complete_callback(PlaybackCompleteCb cb, void *context)
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::PlaybackStatus > > playback_status
struct media::PlayerStub::Private::Signals::DBus dbus
virtual const core::Property< bool > & can_play() const
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::Volume > > volume
virtual GLConsumerWrapperHybris gl_consumer() const
PlayerStub(const std::shared_ptr< Service > &parent, const core::dbus::types::ObjectPath &object)
virtual const core::Signal< uint64_t > & seeked_to() const
virtual const core::Property< bool > & can_go_next() const
core::dbus::Signal< mpris::Player::Signals::Seeked, mpris::Player::Signals::Seeked::ArgumentType > DBusSeekedToSignal
virtual const core::Property< Track::MetaData > & meta_data_for_current_track() const
core::dbus::Signal< mpris::Player::Signals::PlaybackStatusChanged, mpris::Player::Signals::PlaybackStatusChanged::ArgumentType > DBusPlaybackStatusChangedSignal
virtual const core::Property< uint64_t > & duration() const
virtual const core::Property< bool > & is_shuffle() const
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::CanPause > > can_pause
dbus::types::ObjectPath path
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::CanGoPrevious > > can_go_previous
virtual core::Signal< PlaybackStatus > & playback_status_changed()
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::MetaData > > meta_data_for_current_track
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::Shuffle > > is_shuffle
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::Duration > > duration
std::shared_ptr< TrackList > track_list
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::CanSeek > > can_seek
DSSessionWrapperHybris decoding_session
virtual const core::Property< PlaybackStatus > & playback_status() const
virtual const core::Property< AudioStreamRole > & audio_stream_role() const
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::AudioStreamRole > > audio_role
virtual const core::Property< uint64_t > & position() const
virtual PlayerKey key() const
#define UNUSED
Definition: player_stub.cpp:41
virtual const core::Property< PlaybackRate > & minimum_playback_rate() const
virtual const core::Property< bool > & can_seek() const
virtual void set_frame_available_callback(FrameAvailableCb cb, void *context)
FrameAvailableCb frame_available_cb
virtual bool open_uri(const Track::UriType &uri)
std::string UriType
Definition: track.h:40
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::Position > > position
void set_playback_complete_cb(PlaybackCompleteCb cb, void *context)
PlaybackCompleteCb playback_complete_cb
virtual void seek_to(const std::chrono::microseconds &offset)
virtual std::shared_ptr< TrackList > track_list()
Signals(const std::shared_ptr< DBusSeekedToSignal > &seeked, const std::shared_ptr< DBusEndOfStreamSignal > &eos, const std::shared_ptr< DBusPlaybackStatusChangedSignal > &status)
core::Signal< uint64_t > seeked_to
struct media::PlayerStub::Private::@13 properties
std::shared_ptr< DBusPlaybackStatusChangedSignal > playback_status_changed
virtual const core::Property< PlaybackRate > & playback_rate() const
virtual const core::Property< bool > & can_go_previous() const
virtual const core::Property< PlaybackRate > & maximum_playback_rate() const
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::PlaybackRate > > playback_rate
core::Signal< media::Player::PlaybackStatus > playback_status_changed
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::MaximumRate > > maximum_playback_rate
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::IsVideoSource > > is_video_source
std::shared_ptr< core::dbus::Property< mpris::Player::Properties::CanGoNext > > can_go_next
virtual const core::Property< LoopStatus > & loop_status() const