Music Hub  ..
A session-wide music playback service
engine.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 <stdio.h>
21 #include <stdlib.h>
22 
23 #include "bus.h"
24 #include "engine.h"
25 #include "meta_data_extractor.h"
26 #include "playbin.h"
27 
28 #include <cassert>
29 
30 namespace media = core::ubuntu::media;
31 
32 using namespace std;
33 
34 namespace gstreamer
35 {
36 struct Init
37 {
38  Init()
39  {
40  gst_init(nullptr, nullptr);
41  }
42 
44  {
45  gst_deinit();
46  }
47 } init;
48 }
49 
51 {
53  {
54  if (state.new_state == GST_STATE_PLAYING)
55  return media::Player::PlaybackStatus::playing;
56  else if (state.new_state == GST_STATE_PAUSED)
57  return media::Player::PlaybackStatus::paused;
58  else if (state.new_state == GST_STATE_READY)
59  return media::Player::PlaybackStatus::ready;
60  else if (state.new_state == GST_STATE_NULL)
61  return media::Player::PlaybackStatus::null;
62  else
63  return media::Player::PlaybackStatus::stopped;
64  }
65 
66  void on_playbin_state_changed(const std::pair<gstreamer::Bus::Message::Detail::StateChanged,std::string>& p)
67  {
68  if (p.second == "playbin")
69  {
70  std::cout << "State changed on playbin: " << p.first.new_state << std::endl;
71  playback_status_changed(gst_state_to_player_status(p.first));
72  }
73  }
74 
75  // Converts from a GStreamer GError to a media::Player:Error enum
77  {
78  if (g_strcmp0(g_quark_to_string(ewi.error->domain), "gst-core-error-quark") == 0)
79  {
80  switch (ewi.error->code)
81  {
82  case GST_CORE_ERROR_NEGOTIATION:
83  return media::Player::Error::resource_error;
84  case GST_CORE_ERROR_MISSING_PLUGIN:
85  return media::Player::Error::format_error;
86  default:
87  std::cerr << "Got an unhandled core error: '"
88  << ewi.debug << "' (code: " << ewi.error->code << ")" << std::endl;
89  return media::Player::Error::no_error;
90  }
91  }
92  else if (g_strcmp0(g_quark_to_string(ewi.error->domain), "gst-resource-error-quark") == 0)
93  {
94  switch (ewi.error->code)
95  {
96  case GST_RESOURCE_ERROR_NOT_FOUND:
97  case GST_RESOURCE_ERROR_OPEN_READ:
98  case GST_RESOURCE_ERROR_OPEN_WRITE:
99  case GST_RESOURCE_ERROR_READ:
100  case GST_RESOURCE_ERROR_WRITE:
101  return media::Player::Error::resource_error;
102  case GST_RESOURCE_ERROR_NOT_AUTHORIZED:
103  return media::Player::Error::access_denied_error;
104  default:
105  std::cerr << "Got an unhandled resource error: '"
106  << ewi.debug << "' (code: " << ewi.error->code << ")" << std::endl;
107  return media::Player::Error::no_error;
108  }
109  }
110  else if (g_strcmp0(g_quark_to_string(ewi.error->domain), "gst-stream-error-quark") == 0)
111  {
112  switch (ewi.error->code)
113  {
114  case GST_STREAM_ERROR_CODEC_NOT_FOUND:
115  case GST_STREAM_ERROR_DECODE:
116  return media::Player::Error::format_error;
117  default:
118  std::cerr << "Got an unhandled stream error: '"
119  << ewi.debug << "' (code: " << ewi.error->code << ")" << std::endl;
120  return media::Player::Error::no_error;
121  }
122  }
123 
124  return media::Player::Error::no_error;
125  }
126 
128  {
129  const media::Player::Error e = from_gst_errorwarning(ewi);
130  if (e != media::Player::Error::no_error)
131  error(e);
132  }
133 
135  {
136  const media::Player::Error e = from_gst_errorwarning(ewi);
137  if (e != media::Player::Error::no_error)
138  error(e);
139  }
140 
142  {
143  std::cerr << "Got a playbin info message (no action taken): " << ewi.debug << std::endl;
144  }
145 
147  {
148  media::Track::MetaData md;
150  track_meta_data.set(std::make_tuple(playbin.uri(), md));
151  }
152 
153  void on_volume_changed(const media::Engine::Volume& new_volume)
154  {
155  playbin.set_volume(new_volume.value);
156  }
157 
159  {
160  playbin.set_audio_stream_role(new_audio_role);
161  }
162 
164  {
165  // Update the local orientation Property, which should then update the Player
166  // orientation Property
167  orientation.set(o);
168  }
169 
171  {
172  playbin.set_lifetime(lifetime);
173  }
174 
176  {
177  state = Engine::State::ready;
178  about_to_finish();
179  }
180 
181  void on_seeked_to(uint64_t value)
182  {
183  seeked_to(value);
184  }
185 
187  {
188  client_disconnected();
189  }
190 
192  {
193  end_of_stream();
194  }
195 
197  {
198  video_dimension_changed(dimensions);
199  }
200 
202  : meta_data_extractor(new gstreamer::MetaDataExtractor()),
203  volume(media::Engine::Volume(1.)),
204  orientation(media::Player::Orientation::rotate0),
205  is_video_source(false),
206  is_audio_source(false),
207  about_to_finish_connection(
208  playbin.signals.about_to_finish.connect(
209  std::bind(
210  &Private::on_about_to_finish,
211  this))),
212  on_state_changed_connection(
213  playbin.signals.on_state_changed.connect(
214  std::bind(
215  &Private::on_playbin_state_changed,
216  this,
217  std::placeholders::_1))),
218  on_error_connection(
219  playbin.signals.on_error.connect(
220  std::bind(
221  &Private::on_playbin_error,
222  this,
223  std::placeholders::_1))),
224  on_warning_connection(
225  playbin.signals.on_warning.connect(
226  std::bind(
227  &Private::on_playbin_warning,
228  this,
229  std::placeholders::_1))),
230  on_info_connection(
231  playbin.signals.on_info.connect(
232  std::bind(
233  &Private::on_playbin_info,
234  this,
235  std::placeholders::_1))),
236  on_tag_available_connection(
237  playbin.signals.on_tag_available.connect(
238  std::bind(
239  &Private::on_tag_available,
240  this,
241  std::placeholders::_1))),
242  on_volume_changed_connection(
243  volume.changed().connect(
244  std::bind(
245  &Private::on_volume_changed,
246  this,
247  std::placeholders::_1))),
248  on_audio_stream_role_changed_connection(
249  audio_role.changed().connect(
250  std::bind(
251  &Private::on_audio_stream_role_changed,
252  this,
253  std::placeholders::_1))),
254  on_orientation_changed_connection(
255  playbin.signals.on_orientation_changed.connect(
256  std::bind(
257  &Private::on_orientation_changed,
258  this,
259  std::placeholders::_1))),
260  on_lifetime_changed_connection(
261  lifetime.changed().connect(
262  std::bind(
263  &Private::on_lifetime_changed,
264  this,
265  std::placeholders::_1))),
266  on_seeked_to_connection(
267  playbin.signals.on_seeked_to.connect(
268  std::bind(
269  &Private::on_seeked_to,
270  this,
271  std::placeholders::_1))),
272  client_disconnected_connection(
273  playbin.signals.client_disconnected.connect(
274  std::bind(
275  &Private::on_client_disconnected,
276  this))),
277  on_end_of_stream_connection(
278  playbin.signals.on_end_of_stream.connect(
279  std::bind(
280  &Private::on_end_of_stream,
281  this))),
282  on_video_dimension_changed_connection(
283  playbin.signals.on_video_dimensions_changed.connect(
284  std::bind(
285  &Private::on_video_dimension_changed,
286  this,
287  std::placeholders::_1)))
288  {
289  }
290 
291  // Ensure the playbin is the last item destroyed
292  // otherwise properties could try to access a dead playbin object
294 
295  std::shared_ptr<Engine::MetaDataExtractor> meta_data_extractor;
296  core::Property<Engine::State> state;
297  core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>> track_meta_data;
298  core::Property<uint64_t> position;
299  core::Property<uint64_t> duration;
300  core::Property<media::Engine::Volume> volume;
301  core::Property<media::Player::AudioStreamRole> audio_role;
302  core::Property<media::Player::Orientation> orientation;
303  core::Property<media::Player::Lifetime> lifetime;
304  core::Property<bool> is_video_source;
305  core::Property<bool> is_audio_source;
306 
307  core::ScopedConnection about_to_finish_connection;
308  core::ScopedConnection on_state_changed_connection;
309  core::ScopedConnection on_error_connection;
310  core::ScopedConnection on_warning_connection;
311  core::ScopedConnection on_info_connection;
312  core::ScopedConnection on_tag_available_connection;
313  core::ScopedConnection on_volume_changed_connection;
315  core::ScopedConnection on_orientation_changed_connection;
316  core::ScopedConnection on_lifetime_changed_connection;
317  core::ScopedConnection on_seeked_to_connection;
318  core::ScopedConnection client_disconnected_connection;
319  core::ScopedConnection on_end_of_stream_connection;
321 
322  core::Signal<void> about_to_finish;
323  core::Signal<uint64_t> seeked_to;
324  core::Signal<void> client_disconnected;
325  core::Signal<void> end_of_stream;
326  core::Signal<media::Player::PlaybackStatus> playback_status_changed;
327  core::Signal<core::ubuntu::media::video::Dimensions> video_dimension_changed;
328  core::Signal<media::Player::Error> error;
329 };
330 
332 {
333  cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl;
334  d->state = media::Engine::State::no_media;
335 }
336 
338 {
339  stop();
340  d->state = media::Engine::State::no_media;
341 }
342 
343 const std::shared_ptr<media::Engine::MetaDataExtractor>& gstreamer::Engine::meta_data_extractor() const
344 {
345  return d->meta_data_extractor;
346 }
347 
348 const core::Property<media::Engine::State>& gstreamer::Engine::state() const
349 {
350  return d->state;
351 }
352 
353 bool gstreamer::Engine::open_resource_for_uri(const media::Track::UriType& uri, bool do_pipeline_reset)
354 {
355  d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{}, do_pipeline_reset);
356  return true;
357 }
358 
360 {
361  d->playbin.set_uri(uri, headers);
362  return true;
363 }
364 
365 void gstreamer::Engine::create_video_sink(uint32_t texture_id)
366 {
367  d->playbin.create_video_sink(texture_id);
368 }
369 
371 {
372  auto result = d->playbin.set_state_and_wait(GST_STATE_PLAYING);
373 
374  if (result)
375  {
376  d->state = media::Engine::State::playing;
377  cout << __PRETTY_FUNCTION__ << endl;
378  cout << "Engine: playing uri: " << d->playbin.uri() << endl;
379  d->playback_status_changed(media::Player::PlaybackStatus::playing);
380  }
381 
382  return result;
383 }
384 
386 {
387  // No need to wait, and we can immediately return.
388  if (d->state == media::Engine::State::stopped)
389  return true;
390 
391  auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);
392 
393  if (result)
394  {
395  d->state = media::Engine::State::stopped;
396  cout << __PRETTY_FUNCTION__ << endl;
397  d->playback_status_changed(media::Player::PlaybackStatus::stopped);
398  }
399 
400  return result;
401 }
402 
404 {
405  auto result = d->playbin.set_state_and_wait(GST_STATE_PAUSED);
406 
407  if (result)
408  {
409  d->state = media::Engine::State::paused;
410  cout << __PRETTY_FUNCTION__ << endl;
411  d->playback_status_changed(media::Player::PlaybackStatus::paused);
412  }
413 
414  return result;
415 }
416 
417 bool gstreamer::Engine::seek_to(const std::chrono::microseconds& ts)
418 {
419  return d->playbin.seek(ts);
420 }
421 
422 const core::Property<bool>& gstreamer::Engine::is_video_source() const
423 {
424  gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
425  if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_VIDEO)
426  d->is_video_source.set(true);
427  else
428  d->is_video_source.set(false);
429 
430  return d->is_video_source;
431 }
432 
433 const core::Property<bool>& gstreamer::Engine::is_audio_source() const
434 {
435  gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
436  if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_AUDIO)
437  d->is_audio_source.set(true);
438  else
439  d->is_audio_source.set(false);
440 
441  return d->is_audio_source;
442 }
443 
444 const core::Property<uint64_t>& gstreamer::Engine::position() const
445 {
446  d->position.set(d->playbin.position());
447  return d->position;
448 }
449 
450 const core::Property<uint64_t>& gstreamer::Engine::duration() const
451 {
452  d->duration.set(d->playbin.duration());
453  return d->duration;
454 }
455 
456 const core::Property<core::ubuntu::media::Engine::Volume>& gstreamer::Engine::volume() const
457 {
458  return d->volume;
459 }
460 
461 core::Property<core::ubuntu::media::Engine::Volume>& gstreamer::Engine::volume()
462 {
463  return d->volume;
464 }
465 
466 const core::Property<core::ubuntu::media::Player::AudioStreamRole>& gstreamer::Engine::audio_stream_role() const
467 {
468  return d->audio_role;
469 }
470 
471 const core::Property<core::ubuntu::media::Player::Lifetime>& gstreamer::Engine::lifetime() const
472 {
473  return d->lifetime;
474 }
475 
476 core::Property<core::ubuntu::media::Player::AudioStreamRole>& gstreamer::Engine::audio_stream_role()
477 {
478  return d->audio_role;
479 }
480 
481 const core::Property<core::ubuntu::media::Player::Orientation>& gstreamer::Engine::orientation() const
482 {
483  return d->orientation;
484 }
485 
486 core::Property<core::ubuntu::media::Player::Lifetime>& gstreamer::Engine::lifetime()
487 {
488  return d->lifetime;
489 }
490 
491 const core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>&
493 {
494  return d->track_meta_data;
495 }
496 
497 const core::Signal<void>& gstreamer::Engine::about_to_finish_signal() const
498 {
499  return d->about_to_finish;
500 }
501 
502 const core::Signal<uint64_t>& gstreamer::Engine::seeked_to_signal() const
503 {
504  return d->seeked_to;
505 }
506 
507 const core::Signal<void>& gstreamer::Engine::client_disconnected_signal() const
508 {
509  return d->client_disconnected;
510 }
511 
512 const core::Signal<void>& gstreamer::Engine::end_of_stream_signal() const
513 {
514  return d->end_of_stream;
515 }
516 
517 const core::Signal<media::Player::PlaybackStatus>& gstreamer::Engine::playback_status_changed_signal() const
518 {
519  return d->playback_status_changed;
520 }
521 
522 const core::Signal<core::ubuntu::media::video::Dimensions>& gstreamer::Engine::video_dimension_changed_signal() const
523 {
524  return d->video_dimension_changed;
525 }
526 
527 const core::Signal<core::ubuntu::media::Player::Error>& gstreamer::Engine::error_signal() const
528 {
529  return d->error;
530 }
531 
533 {
534  d->playbin.reset();
535 }
void on_tag_available(const gstreamer::Bus::Message::Detail::Tag &tag)
Definition: engine.cpp:146
core::ScopedConnection on_state_changed_connection
Definition: engine.cpp:308
core::ScopedConnection on_video_dimension_changed_connection
Definition: engine.cpp:320
core::Signal< core::ubuntu::media::video::Dimensions > video_dimension_changed
Definition: engine.cpp:327
const core::Property< core::ubuntu::media::Engine::Volume > & volume() const
Definition: engine.cpp:456
core::Property< bool > is_video_source
Definition: engine.cpp:304
const core::Signal< core::ubuntu::media::Player::PlaybackStatus > & playback_status_changed_signal() const
Definition: engine.cpp:517
const core::Property< core::ubuntu::media::Player::AudioStreamRole > & audio_stream_role() const
Definition: engine.cpp:466
std::tuple< Height, Width > Dimensions
Height and Width of a video.
Definition: dimensions.h:139
const core::Property< bool > & is_video_source() const
Definition: engine.cpp:422
const core::Signal< void > & client_disconnected_signal() const
Definition: engine.cpp:507
core::ScopedConnection on_info_connection
Definition: engine.cpp:311
core::Signal< void > end_of_stream
Definition: engine.cpp:325
Definition: bus.h:33
core::ScopedConnection on_lifetime_changed_connection
Definition: engine.cpp:316
void on_seeked_to(uint64_t value)
Definition: engine.cpp:181
void on_playbin_warning(const gstreamer::Bus::Message::Detail::ErrorWarningInfo &ewi)
Definition: engine.cpp:134
STL namespace.
const core::Property< State > & state() const
Definition: engine.cpp:348
const core::Property< core::ubuntu::media::Player::Lifetime > & lifetime() const
Definition: engine.cpp:471
core::ScopedConnection on_seeked_to_connection
Definition: engine.cpp:317
core::ScopedConnection on_end_of_stream_connection
Definition: engine.cpp:319
void on_orientation_changed(const media::Player::Orientation &o)
Definition: engine.cpp:163
core::Signal< media::Player::Error > error
Definition: engine.cpp:328
std::map< std::string, std::string > HeadersType
Definition: player.h:49
gstreamer::Playbin playbin
Definition: engine.cpp:293
bool seek_to(const std::chrono::microseconds &ts)
Definition: engine.cpp:417
core::ScopedConnection on_warning_connection
Definition: engine.cpp:310
bool open_resource_for_uri(const core::ubuntu::media::Track::UriType &uri, bool do_pipeline_reset)
const core::Signal< uint64_t > & seeked_to_signal() const
Definition: engine.cpp:502
struct gstreamer::Init init
void create_video_sink(uint32_t texture_id)
Definition: engine.cpp:365
const core::Property< std::tuple< core::ubuntu::media::Track::UriType, core::ubuntu::media::Track::MetaData > > & track_meta_data() const
Definition: engine.cpp:492
core::ScopedConnection client_disconnected_connection
Definition: engine.cpp:318
void on_playbin_error(const gstreamer::Bus::Message::Detail::ErrorWarningInfo &ewi)
Definition: engine.cpp:127
media::Player::Error from_gst_errorwarning(const gstreamer::Bus::Message::Detail::ErrorWarningInfo &ewi)
Definition: engine.cpp:76
const core::Property< bool > & is_audio_source() const
Definition: engine.cpp:433
void on_volume_changed(const media::Engine::Volume &new_volume)
Definition: engine.cpp:153
const core::Signal< core::ubuntu::media::Player::Error > & error_signal() const
Definition: engine.cpp:527
const core::Property< uint64_t > & duration() const
Definition: engine.cpp:450
core::ScopedConnection on_volume_changed_connection
Definition: engine.cpp:313
const core::Signal< void > & about_to_finish_signal() const
Definition: engine.cpp:497
core::Property< media::Player::Orientation > orientation
Definition: engine.cpp:302
core::Property< media::Player::AudioStreamRole > audio_role
Definition: engine.cpp:301
const core::Signal< void > & end_of_stream_signal() const
Definition: engine.cpp:512
const core::Signal< core::ubuntu::media::video::Dimensions > & video_dimension_changed_signal() const
Definition: engine.cpp:522
core::Signal< void > client_disconnected
Definition: engine.cpp:324
core::Signal< uint64_t > seeked_to
Definition: engine.cpp:323
void on_playbin_info(const gstreamer::Bus::Message::Detail::ErrorWarningInfo &ewi)
Definition: engine.cpp:141
core::ScopedConnection on_error_connection
Definition: engine.cpp:309
core::Property< media::Player::Lifetime > lifetime
Definition: engine.cpp:303
std::shared_ptr< Engine::MetaDataExtractor > meta_data_extractor
Definition: engine.cpp:295
static void on_tag_available(const gstreamer::Bus::Message::Detail::Tag &tag, core::ubuntu::media::Track::MetaData &md)
core::ScopedConnection on_tag_available_connection
Definition: engine.cpp:312
const std::shared_ptr< MetaDataExtractor > & meta_data_extractor() const
Definition: engine.cpp:343
core::Property< media::Engine::Volume > volume
Definition: engine.cpp:300
void on_playbin_state_changed(const std::pair< gstreamer::Bus::Message::Detail::StateChanged, std::string > &p)
Definition: engine.cpp:66
media::Player::PlaybackStatus gst_state_to_player_status(const gstreamer::Bus::Message::Detail::StateChanged &state)
Definition: engine.cpp:52
std::string UriType
Definition: track.h:40
core::ScopedConnection about_to_finish_connection
Definition: engine.cpp:307
core::Property< Engine::State > state
Definition: engine.cpp:296
const core::Property< core::ubuntu::media::Player::Orientation > & orientation() const
Definition: engine.cpp:481
core::Property< uint64_t > position
Definition: engine.cpp:298
core::Property< std::tuple< media::Track::UriType, media::Track::MetaData > > track_meta_data
Definition: engine.cpp:297
core::Signal< void > about_to_finish
Definition: engine.cpp:322
core::Signal< media::Player::PlaybackStatus > playback_status_changed
Definition: engine.cpp:326
const core::Property< uint64_t > & position() const
Definition: engine.cpp:444
core::ScopedConnection on_orientation_changed_connection
Definition: engine.cpp:315
void on_audio_stream_role_changed(const media::Player::AudioStreamRole &new_audio_role)
Definition: engine.cpp:158
void on_video_dimension_changed(const media::video::Dimensions &dimensions)
Definition: engine.cpp:196
void on_lifetime_changed(const media::Player::Lifetime &lifetime)
Definition: engine.cpp:170
core::Property< bool > is_audio_source
Definition: engine.cpp:305
core::Property< uint64_t > duration
Definition: engine.cpp:299
core::ScopedConnection on_audio_stream_role_changed_connection
Definition: engine.cpp:314