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::ready;
317 }
318 
320 {
321  stop();
322 }
323 
324 const std::shared_ptr<media::Engine::MetaDataExtractor>& gstreamer::Engine::meta_data_extractor() const
325 {
326  return d->meta_data_extractor;
327 }
328 
329 const core::Property<media::Engine::State>& gstreamer::Engine::state() const
330 {
331  return d->state;
332 }
333 
335 {
336  d->playbin.set_uri(uri, core::ubuntu::media::Player::HeadersType{});
337  return true;
338 }
339 
341 {
342  d->playbin.set_uri(uri, headers);
343  return true;
344 }
345 
346 void gstreamer::Engine::create_video_sink(uint32_t texture_id)
347 {
348  d->playbin.create_video_sink(texture_id);
349 }
350 
352 {
353  auto result = d->playbin.set_state_and_wait(GST_STATE_PLAYING);
354 
355  if (result)
356  {
357  d->state = media::Engine::State::playing;
358  cout << __PRETTY_FUNCTION__ << endl;
359  d->playback_status_changed(media::Player::PlaybackStatus::playing);
360  }
361 
362  return result;
363 }
364 
366 {
367  // No need to wait, and we can immediately return.
368  if (d->state == media::Engine::State::stopped)
369  return true;
370 
371  auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);
372 
373  if (result)
374  {
375  d->state = media::Engine::State::stopped;
376  cout << __PRETTY_FUNCTION__ << endl;
377  d->playback_status_changed(media::Player::PlaybackStatus::stopped);
378  }
379 
380  return result;
381 }
382 
384 {
385  auto result = d->playbin.set_state_and_wait(GST_STATE_PAUSED);
386 
387  if (result)
388  {
389  d->state = media::Engine::State::paused;
390  cout << __PRETTY_FUNCTION__ << endl;
391  d->playback_status_changed(media::Player::PlaybackStatus::paused);
392  }
393 
394  return result;
395 }
396 
397 bool gstreamer::Engine::seek_to(const std::chrono::microseconds& ts)
398 {
399  return d->playbin.seek(ts);
400 }
401 
402 const core::Property<bool>& gstreamer::Engine::is_video_source() const
403 {
404  gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
405  if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_VIDEO)
406  d->is_video_source.set(true);
407  else
408  d->is_video_source.set(false);
409 
410  return d->is_video_source;
411 }
412 
413 const core::Property<bool>& gstreamer::Engine::is_audio_source() const
414 {
415  gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
416  if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_AUDIO)
417  d->is_audio_source.set(true);
418  else
419  d->is_audio_source.set(false);
420 
421  return d->is_audio_source;
422 }
423 
424 const core::Property<uint64_t>& gstreamer::Engine::position() const
425 {
426  d->position.set(d->playbin.position());
427  return d->position;
428 }
429 
430 const core::Property<uint64_t>& gstreamer::Engine::duration() const
431 {
432  d->duration.set(d->playbin.duration());
433  return d->duration;
434 }
435 
436 const core::Property<core::ubuntu::media::Engine::Volume>& gstreamer::Engine::volume() const
437 {
438  return d->volume;
439 }
440 
441 core::Property<core::ubuntu::media::Engine::Volume>& gstreamer::Engine::volume()
442 {
443  return d->volume;
444 }
445 
446 const core::Property<core::ubuntu::media::Player::AudioStreamRole>& gstreamer::Engine::audio_stream_role() const
447 {
448  return d->audio_role;
449 }
450 
451 const core::Property<core::ubuntu::media::Player::Lifetime>& gstreamer::Engine::lifetime() const
452 {
453  return d->lifetime;
454 }
455 
456 core::Property<core::ubuntu::media::Player::AudioStreamRole>& gstreamer::Engine::audio_stream_role()
457 {
458  return d->audio_role;
459 }
460 
461 const core::Property<core::ubuntu::media::Player::Orientation>& gstreamer::Engine::orientation() const
462 {
463  return d->orientation;
464 }
465 
466 core::Property<core::ubuntu::media::Player::Lifetime>& gstreamer::Engine::lifetime()
467 {
468  return d->lifetime;
469 }
470 
471 const core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>&
473 {
474  return d->track_meta_data;
475 }
476 
477 const core::Signal<void>& gstreamer::Engine::about_to_finish_signal() const
478 {
479  return d->about_to_finish;
480 }
481 
482 const core::Signal<uint64_t>& gstreamer::Engine::seeked_to_signal() const
483 {
484  return d->seeked_to;
485 }
486 
487 const core::Signal<void>& gstreamer::Engine::client_disconnected_signal() const
488 {
489  return d->client_disconnected;
490 }
491 
492 const core::Signal<void>& gstreamer::Engine::end_of_stream_signal() const
493 {
494  return d->end_of_stream;
495 }
496 
497 const core::Signal<media::Player::PlaybackStatus>& gstreamer::Engine::playback_status_changed_signal() const
498 {
499  return d->playback_status_changed;
500 }
501 
502 const core::Signal<core::ubuntu::media::video::Dimensions>& gstreamer::Engine::video_dimension_changed_signal() const
503 {
504  return d->video_dimension_changed;
505 }
506 
507 const core::Signal<core::ubuntu::media::Player::Error>& gstreamer::Engine::error_signal() const
508 {
509  return d->error;
510 }
511 
513 {
514  d->playbin.reset();
515 }
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
bool open_resource_for_uri(const core::ubuntu::media::Track::UriType &uri)
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:436
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:497
const core::Property< core::ubuntu::media::Player::AudioStreamRole > & audio_stream_role() const
Definition: engine.cpp:446
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:402
const core::Signal< void > & client_disconnected_signal() const
Definition: engine.cpp:487
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:329
const core::Property< core::ubuntu::media::Player::Lifetime > & lifetime() const
Definition: engine.cpp:451
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:48
gstreamer::Playbin playbin
Definition: engine.cpp:275
bool seek_to(const std::chrono::microseconds &ts)
Definition: engine.cpp:397
core::ScopedConnection on_warning_connection
Definition: engine.cpp:292
const core::Signal< uint64_t > & seeked_to_signal() const
Definition: engine.cpp:482
struct gstreamer::Init init
void create_video_sink(uint32_t texture_id)
Definition: engine.cpp:346
const core::Property< std::tuple< core::ubuntu::media::Track::UriType, core::ubuntu::media::Track::MetaData > > & track_meta_data() const
Definition: engine.cpp:472
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:413
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:507
const core::Property< uint64_t > & duration() const
Definition: engine.cpp:430
core::ScopedConnection on_volume_changed_connection
Definition: engine.cpp:295
const core::Signal< void > & about_to_finish_signal() const
Definition: engine.cpp:477
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:492
const core::Signal< core::ubuntu::media::video::Dimensions > & video_dimension_changed_signal() const
Definition: engine.cpp:502
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:324
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:461
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:424
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