Music Hub  ..
A session-wide music playback service
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
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 
178  void on_video_dimension_changed(uint32_t height, uint32_t width)
179  {
180  video_dimension_changed(height, width);
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_add_frame_dimension.connect(
266  std::bind(
267  &Private::on_video_dimension_changed,
268  this,
269  std::placeholders::_1,
270  std::placeholders::_2)))
271  {
272  }
273 
274  // Ensure the playbin is the last item destroyed
275  // otherwise properties could try to access a dead playbin object
277 
278  std::shared_ptr<Engine::MetaDataExtractor> meta_data_extractor;
279  core::Property<Engine::State> state;
280  core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>> track_meta_data;
281  core::Property<uint64_t> position;
282  core::Property<uint64_t> duration;
283  core::Property<media::Engine::Volume> volume;
284  core::Property<media::Player::AudioStreamRole> audio_role;
285  core::Property<media::Player::Orientation> orientation;
286  core::Property<media::Player::Lifetime> lifetime;
287  core::Property<bool> is_video_source;
288  core::Property<bool> is_audio_source;
289 
290  core::ScopedConnection about_to_finish_connection;
291  core::ScopedConnection on_state_changed_connection;
292  core::ScopedConnection on_error_connection;
293  core::ScopedConnection on_warning_connection;
294  core::ScopedConnection on_info_connection;
295  core::ScopedConnection on_tag_available_connection;
296  core::ScopedConnection on_volume_changed_connection;
298  core::ScopedConnection on_orientation_changed_connection;
299  core::ScopedConnection on_lifetime_changed_connection;
300  core::ScopedConnection on_seeked_to_connection;
301  core::ScopedConnection client_disconnected_connection;
302  core::ScopedConnection on_end_of_stream_connection;
304 
305  core::Signal<void> about_to_finish;
306  core::Signal<uint64_t> seeked_to;
307  core::Signal<void> client_disconnected;
308  core::Signal<void> end_of_stream;
309  core::Signal<media::Player::PlaybackStatus> playback_status_changed;
310  core::Signal<uint32_t, uint32_t> video_dimension_changed;
311  core::Signal<media::Player::Error> error;
312 };
313 
315 {
316  cout << "Creating a new Engine instance in " << __PRETTY_FUNCTION__ << endl;
317  d->state = media::Engine::State::ready;
318 }
319 
321 {
322  stop();
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 
336 {
337  d->playbin.set_uri(uri);
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 << "play" << endl;
360  d->playback_status_changed(media::Player::PlaybackStatus::playing);
361  }
362 
363  return result;
364 }
365 
367 {
368  // No need to wait, and we can immediately return.
369  if (d->state == media::Engine::State::stopped)
370  return true;
371 
372  auto result = d->playbin.set_state_and_wait(GST_STATE_NULL);
373 
374  if (result)
375  {
376  d->state = media::Engine::State::stopped;
377  cout << "stop" << endl;
378  d->playback_status_changed(media::Player::PlaybackStatus::stopped);
379  }
380 
381  return result;
382 }
383 
385 {
386  auto result = d->playbin.set_state_and_wait(GST_STATE_PAUSED);
387 
388  if (result)
389  {
390  d->state = media::Engine::State::paused;
391  cout << "pause" << endl;
392  d->playback_status_changed(media::Player::PlaybackStatus::paused);
393  }
394 
395  return result;
396 }
397 
398 bool gstreamer::Engine::seek_to(const std::chrono::microseconds& ts)
399 {
400  return d->playbin.seek(ts);
401 }
402 
403 const core::Property<bool>& gstreamer::Engine::is_video_source() const
404 {
405  gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
406  if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_VIDEO)
407  d->is_video_source.set(true);
408  else
409  d->is_video_source.set(false);
410 
411  return d->is_video_source;
412 }
413 
414 const core::Property<bool>& gstreamer::Engine::is_audio_source() const
415 {
416  gstreamer::Playbin::MediaFileType type = d->playbin.media_file_type();
417  if (type == gstreamer::Playbin::MediaFileType::MEDIA_FILE_TYPE_AUDIO)
418  d->is_audio_source.set(true);
419  else
420  d->is_audio_source.set(false);
421 
422  return d->is_audio_source;
423 }
424 
425 const core::Property<uint64_t>& gstreamer::Engine::position() const
426 {
427  d->position.set(d->playbin.position());
428  return d->position;
429 }
430 
431 const core::Property<uint64_t>& gstreamer::Engine::duration() const
432 {
433  d->duration.set(d->playbin.duration());
434  return d->duration;
435 }
436 
437 const core::Property<core::ubuntu::media::Engine::Volume>& gstreamer::Engine::volume() const
438 {
439  return d->volume;
440 }
441 
442 core::Property<core::ubuntu::media::Engine::Volume>& gstreamer::Engine::volume()
443 {
444  return d->volume;
445 }
446 
447 const core::Property<core::ubuntu::media::Player::AudioStreamRole>& gstreamer::Engine::audio_stream_role() const
448 {
449  return d->audio_role;
450 }
451 
452 const core::Property<core::ubuntu::media::Player::Lifetime>& gstreamer::Engine::lifetime() const
453 {
454  return d->lifetime;
455 }
456 
457 core::Property<core::ubuntu::media::Player::AudioStreamRole>& gstreamer::Engine::audio_stream_role()
458 {
459  return d->audio_role;
460 }
461 
462 const core::Property<core::ubuntu::media::Player::Orientation>& gstreamer::Engine::orientation() const
463 {
464  return d->orientation;
465 }
466 
467 core::Property<core::ubuntu::media::Player::Lifetime>& gstreamer::Engine::lifetime()
468 {
469  return d->lifetime;
470 }
471 
472 const core::Property<std::tuple<media::Track::UriType, media::Track::MetaData>>&
474 {
475  return d->track_meta_data;
476 }
477 
478 const core::Signal<void>& gstreamer::Engine::about_to_finish_signal() const
479 {
480  return d->about_to_finish;
481 }
482 
483 const core::Signal<uint64_t>& gstreamer::Engine::seeked_to_signal() const
484 {
485  return d->seeked_to;
486 }
487 
488 const core::Signal<void>& gstreamer::Engine::client_disconnected_signal() const
489 {
490  return d->client_disconnected;
491 }
492 
493 const core::Signal<void>& gstreamer::Engine::end_of_stream_signal() const
494 {
495  return d->end_of_stream;
496 }
497 
498 const core::Signal<media::Player::PlaybackStatus>& gstreamer::Engine::playback_status_changed_signal() const
499 {
500  return d->playback_status_changed;
501 }
502 
503 const core::Signal<uint32_t, uint32_t>& gstreamer::Engine::video_dimension_changed_signal() const
504 {
505  return d->video_dimension_changed;
506 }
507 
508 const core::Signal<core::ubuntu::media::Player::Error>& gstreamer::Engine::error_signal() const
509 {
510  return d->error;
511 }
512 
514 {
515  d->playbin.reset();
516 }
void on_tag_available(const gstreamer::Bus::Message::Detail::Tag &tag)
Definition: engine.cpp:128
core::ScopedConnection on_state_changed_connection
Definition: engine.cpp:291
bool open_resource_for_uri(const core::ubuntu::media::Track::UriType &uri)
core::ScopedConnection on_video_dimension_changed_connection
Definition: engine.cpp:303
const core::Property< core::ubuntu::media::Engine::Volume > & volume() const
Definition: engine.cpp:437
core::Property< bool > is_video_source
Definition: engine.cpp:287
const core::Signal< core::ubuntu::media::Player::PlaybackStatus > & playback_status_changed_signal() const
Definition: engine.cpp:498
const core::Property< core::ubuntu::media::Player::AudioStreamRole > & audio_stream_role() const
Definition: engine.cpp:447
const core::Property< bool > & is_video_source() const
Definition: engine.cpp:403
void on_video_dimension_changed(uint32_t height, uint32_t width)
Definition: engine.cpp:178
const core::Signal< void > & client_disconnected_signal() const
Definition: engine.cpp:488
core::ScopedConnection on_info_connection
Definition: engine.cpp:294
core::Signal< void > end_of_stream
Definition: engine.cpp:308
Definition: bus.h:33
core::ScopedConnection on_lifetime_changed_connection
Definition: engine.cpp:299
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:452
core::ScopedConnection on_seeked_to_connection
Definition: engine.cpp:300
core::ScopedConnection on_end_of_stream_connection
Definition: engine.cpp:302
void on_orientation_changed(const media::Player::Orientation &o)
Definition: engine.cpp:145
core::Signal< media::Player::Error > error
Definition: engine.cpp:311
std::map< std::string, std::string > HeadersType
Definition: player.h:45
gstreamer::Playbin playbin
Definition: engine.cpp:276
bool seek_to(const std::chrono::microseconds &ts)
Definition: engine.cpp:398
core::ScopedConnection on_warning_connection
Definition: engine.cpp:293
const core::Signal< uint64_t > & seeked_to_signal() const
Definition: engine.cpp:483
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:473
core::ScopedConnection client_disconnected_connection
Definition: engine.cpp:301
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::Signal< uint32_t, uint32_t > & video_dimension_changed_signal() const
Definition: engine.cpp:503
const core::Property< bool > & is_audio_source() const
Definition: engine.cpp:414
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:508
const core::Property< uint64_t > & duration() const
Definition: engine.cpp:431
core::ScopedConnection on_volume_changed_connection
Definition: engine.cpp:296
const core::Signal< void > & about_to_finish_signal() const
Definition: engine.cpp:478
core::Property< media::Player::Orientation > orientation
Definition: engine.cpp:285
core::Property< media::Player::AudioStreamRole > audio_role
Definition: engine.cpp:284
core::Signal< uint32_t, uint32_t > video_dimension_changed
Definition: engine.cpp:310
const core::Signal< void > & end_of_stream_signal() const
Definition: engine.cpp:493
core::Signal< void > client_disconnected
Definition: engine.cpp:307
core::Signal< uint64_t > seeked_to
Definition: engine.cpp:306
void on_playbin_info(const gstreamer::Bus::Message::Detail::ErrorWarningInfo &ewi)
Definition: engine.cpp:123
core::ScopedConnection on_error_connection
Definition: engine.cpp:292
core::Property< media::Player::Lifetime > lifetime
Definition: engine.cpp:286
std::shared_ptr< Engine::MetaDataExtractor > meta_data_extractor
Definition: engine.cpp:278
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:295
const std::shared_ptr< MetaDataExtractor > & meta_data_extractor() const
Definition: engine.cpp:325
core::Property< media::Engine::Volume > volume
Definition: engine.cpp:283
std::string UriType
Definition: track.h:40
core::ScopedConnection about_to_finish_connection
Definition: engine.cpp:290
core::Property< Engine::State > state
Definition: engine.cpp:279
const core::Property< core::ubuntu::media::Player::Orientation > & orientation() const
Definition: engine.cpp:462
core::Property< uint64_t > position
Definition: engine.cpp:281
core::Property< std::tuple< media::Track::UriType, media::Track::MetaData > > track_meta_data
Definition: engine.cpp:280
core::Signal< void > about_to_finish
Definition: engine.cpp:305
core::Signal< media::Player::PlaybackStatus > playback_status_changed
Definition: engine.cpp:309
const core::Property< uint64_t > & position() const
Definition: engine.cpp:425
core::ScopedConnection on_orientation_changed_connection
Definition: engine.cpp:298
void on_audio_stream_role_changed(const media::Player::AudioStreamRole &new_audio_role)
Definition: engine.cpp:140
void on_lifetime_changed(const media::Player::Lifetime &lifetime)
Definition: engine.cpp:152
core::Property< bool > is_audio_source
Definition: engine.cpp:288
core::Property< uint64_t > duration
Definition: engine.cpp:282
core::ScopedConnection on_audio_stream_role_changed_connection
Definition: engine.cpp:297