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