Music Hub  ..
A session-wide music playback service
playbin.cpp
Go to the documentation of this file.
1 /*
2  * Copyright © 2013-2015 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  */
18 
21 
22 #include <gst/pbutils/missing-plugins.h>
23 
24 #if defined(MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER)
25 #include <hybris/media/surface_texture_client_hybris.h>
26 #include <hybris/media/media_codec_layer.h>
27 
28 #include "../util/uri_check.h"
29 
30 #include <utility>
31 
32 //#define VERBOSE_DEBUG
33 
34 namespace
35 {
36 void setup_video_sink_for_buffer_streaming(GstElement* pipeline)
37 {
38  // Get the service-side BufferQueue (IGraphicBufferProducer) and associate it with
39  // the SurfaceTextureClientHybris instance
40  IGBPWrapperHybris igbp = decoding_service_get_igraphicbufferproducer();
41  SurfaceTextureClientHybris stc = surface_texture_client_create_by_igbp(igbp);
42 
43  // Because mirsink is being loaded, we are definitely doing * hardware rendering.
44  surface_texture_client_set_hardware_rendering(stc, TRUE);
45 
46  GstContext *context = gst_context_new("gst.mir.MirContext", TRUE);
47  GstStructure *structure = gst_context_writable_structure(context);
48  gst_structure_set(structure, "gst_mir_context", G_TYPE_POINTER, stc, NULL);
49 
50  /* Propagate context in pipeline (needed by amchybris and mirsink) */
51  gst_element_set_context(pipeline, context);
52 }
53 }
54 #else // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
55 namespace
56 {
57 void setup_video_sink_for_buffer_streaming(GstElement*)
58 {
60 }
61 }
62 #endif // MEDIA_HUB_HAVE_HYBRIS_MEDIA_COMPAT_LAYER
63 
64 namespace
65 {
66 bool is_mir_video_sink()
67 {
68  return g_strcmp0(::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"), "mirsink") == 0;
69 }
70 }
71 // Uncomment to generate a dot file at the time that the pipeline
72 // goes to the PLAYING state. Make sure to export GST_DEBUG_DUMP_DOT_DIR
73 // before starting media-hub-server. To convert the dot file to something
74 // other image format, use: dot pipeline.dot -Tpng -o pipeline.png
75 //#define DEBUG_GST_PIPELINE
76 
77 namespace media = core::ubuntu::media;
79 
81 {
82  static const std::string s{"playbin"};
83  return s;
84 }
85 
86 void gstreamer::Playbin::about_to_finish(GstElement*, gpointer user_data)
87 {
88  auto thiz = static_cast<Playbin*>(user_data);
89  thiz->signals.about_to_finish();
90 }
91 
93  GstElement *source,
94  gpointer user_data)
95 {
96  if (user_data == nullptr)
97  return;
98 
99  static_cast<Playbin*>(user_data)->setup_source(source);
100 }
101 
103  : pipeline(gst_element_factory_make("playbin", pipeline_name().c_str())),
104  bus{gst_element_get_bus(pipeline)},
106  video_sink(nullptr),
107  audio_sink(nullptr),
109  bus.on_new_message_async.connect(
110  std::bind(
112  this,
113  std::placeholders::_1))),
114  is_seeking(false),
119  player_lifetime(media::Player::Lifetime::normal),
122  is_missing_audio_codec(false),
123  is_missing_video_codec(false),
124  audio_stream_id(-1),
125  video_stream_id(-1)
126 {
127  if (!pipeline)
128  throw std::runtime_error("Could not create pipeline for playbin.");
129 
130  // Add audio and/or video sink elements depending on environment variables
131  // being set or not set
133 
134  about_to_finish_handler_id = g_signal_connect(
135  pipeline,
136  "about-to-finish",
137  G_CALLBACK(about_to_finish),
138  this
139  );
140 
141  source_setup_handler_id = g_signal_connect(
142  pipeline,
143  "source-setup",
144  G_CALLBACK(source_setup),
145  this
146  );
147 }
148 
149 // Note that we might be accessing freed memory here, so activate DEBUG_REFS
150 // only for debugging
151 //#define DEBUG_REFS
152 #ifdef DEBUG_REFS
153 static void print_refs(const gstreamer::Playbin &pb, const char *func)
154 {
155  using namespace std;
156 
157  cout << func << endl;
158  if (pb.pipeline)
159  cout << "pipeline: " << GST_OBJECT_REFCOUNT(pb.pipeline) << endl;
160  if (pb.video_sink)
161  cout << "video_sink: " << GST_OBJECT_REFCOUNT(pb.video_sink) << endl;
162  if (pb.audio_sink)
163  cout << "audio_sink: " << GST_OBJECT_REFCOUNT(pb.audio_sink) << endl;
164 }
165 #endif
166 
168 {
169 #ifdef DEBUG_REFS
170  print_refs(*this, "gstreamer::Playbin::~Playbin pipeline");
171 #endif
172 
173  g_signal_handler_disconnect(pipeline, about_to_finish_handler_id);
174  g_signal_handler_disconnect(pipeline, source_setup_handler_id);
175 
176  if (pipeline)
177  gst_object_unref(pipeline);
178 
179 #ifdef DEBUG_REFS
180  print_refs(*this, "gstreamer::Playbin::~Playbin pipeline");
181 #endif
182 }
183 
185 {
186  std::cout << "Client died, resetting pipeline" << std::endl;
187  // When the client dies, tear down the current pipeline and get it
188  // in a state that is ready for the next client that connects to the
189  // service
190 
191  // Don't reset the pipeline if we want to resume
192  if (player_lifetime != media::Player::Lifetime::resumable) {
193  reset_pipeline();
194  }
195  // Signal to the Player class that the client side has disconnected
196  signals.client_disconnected();
197 }
198 
200 {
201  std::cout << __PRETTY_FUNCTION__ << std::endl;
202  auto ret = gst_element_set_state(pipeline, GST_STATE_NULL);
203  switch(ret)
204  {
205  case GST_STATE_CHANGE_FAILURE:
206  std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl;
207  break;
208  case GST_STATE_CHANGE_NO_PREROLL:
209  case GST_STATE_CHANGE_SUCCESS:
210  case GST_STATE_CHANGE_ASYNC:
211  break;
212  default:
213  std::cout << "Failed to reset the pipeline state. Client reconnect may not function properly." << std::endl;
214  }
216  is_missing_audio_codec = false;
217  is_missing_video_codec = false;
218  audio_stream_id = -1;
219  video_stream_id = -1;
220 }
221 
223 {
224  if (!gst_is_missing_plugin_message(message))
225  return;
226 
227  gchar *desc = gst_missing_plugin_message_get_description(message);
228  std::cerr << "Missing plugin: " << desc << std::endl;
229  g_free(desc);
230 
231  const GstStructure *msg_data = gst_message_get_structure(message);
232  if (g_strcmp0("decoder", gst_structure_get_string(msg_data, "type")) != 0)
233  return;
234 
235  GstCaps *caps;
236  if (!gst_structure_get(msg_data, "detail", GST_TYPE_CAPS, &caps, NULL)) {
237  std::cerr << __PRETTY_FUNCTION__ << ": No detail" << std::endl;
238  return;
239  }
240 
241  GstStructure *caps_data = gst_caps_get_structure(caps, 0);
242  if (!caps_data) {
243  std::cerr << __PRETTY_FUNCTION__ << ": No caps data" << std::endl;
244  return;
245  }
246 
247  const gchar *mime = gst_structure_get_name(caps_data);
248  if (strstr(mime, "audio"))
249  is_missing_audio_codec = true;
250  else if (strstr(mime, "video"))
251  is_missing_video_codec = true;
252 
253  std::cerr << "Missing decoder for " << mime << std::endl;
254 }
255 
257 {
258  switch(message.type)
259  {
260  case GST_MESSAGE_ERROR:
261  signals.on_error(message.detail.error_warning_info);
262  break;
263  case GST_MESSAGE_WARNING:
264  signals.on_warning(message.detail.error_warning_info);
265  break;
266  case GST_MESSAGE_INFO:
267  signals.on_info(message.detail.error_warning_info);
268  break;
269  case GST_MESSAGE_STATE_CHANGED:
270  if (message.source == "playbin") {
271  g_object_get(G_OBJECT(pipeline), "current-audio", &audio_stream_id, NULL);
272  g_object_get(G_OBJECT(pipeline), "current-video", &video_stream_id, NULL);
273  }
274  signals.on_state_changed(std::make_pair(message.detail.state_changed, message.source));
275  break;
276  case GST_MESSAGE_ELEMENT:
278  break;
279  case GST_MESSAGE_TAG:
280  {
281  gchar *orientation;
282  if (gst_tag_list_get_string(message.detail.tag.tag_list, "image-orientation", &orientation))
283  {
284  // If the image-orientation tag is in the GstTagList, signal the Engine
285  signals.on_orientation_changed(orientation_lut(orientation));
286  g_free (orientation);
287  }
288 
289  signals.on_tag_available(message.detail.tag);
290  }
291  break;
292  case GST_MESSAGE_ASYNC_DONE:
293  if (is_seeking)
294  {
295  // FIXME: Pass the actual playback time position to the signal call
296  signals.on_seeked_to(0);
297  is_seeking = false;
298  }
299  break;
300  case GST_MESSAGE_EOS:
301  signals.on_end_of_stream();
302  default:
303  break;
304  }
305 }
306 
308 {
309  return bus;
310 }
311 
313 {
314  gint flags;
315  g_object_get (pipeline, "flags", &flags, nullptr);
316  flags |= GST_PLAY_FLAG_AUDIO;
317  flags |= GST_PLAY_FLAG_VIDEO;
318  flags &= ~GST_PLAY_FLAG_TEXT;
319  g_object_set (pipeline, "flags", flags, nullptr);
320 
321  if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") != nullptr)
322  {
323  audio_sink = gst_element_factory_make (
324  ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME"),
325  "audio-sink");
326 
327  std::cout << "audio_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_AUDIO_SINK_NAME") << std::endl;
328 
329  g_object_set (
330  pipeline,
331  "audio-sink",
332  audio_sink,
333  NULL);
334  }
335 
336  if (::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") != nullptr)
337  {
338  video_sink = gst_element_factory_make (
339  ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME"),
340  "video-sink");
341 
342  std::cout << "video_sink: " << ::getenv("CORE_UBUNTU_MEDIA_SERVICE_VIDEO_SINK_NAME") << std::endl;
343 
344  g_object_set (
345  pipeline,
346  "video-sink",
347  video_sink,
348  NULL);
349  }
350 }
351 
353 {
354  if (not video_sink) throw std::logic_error
355  {
356  "No video sink configured for the current pipeline"
357  };
358 
359  setup_video_sink_for_buffer_streaming(pipeline);
360 }
361 
362 void gstreamer::Playbin::set_volume(double new_volume)
363 {
364  g_object_set (pipeline, "volume", new_volume, NULL);
365 }
366 
369 {
370  switch (audio_role)
371  {
372  case media::Player::AudioStreamRole::alarm:
373  return "alarm";
374  break;
375  case media::Player::AudioStreamRole::alert:
376  return "alert";
377  break;
378  case media::Player::AudioStreamRole::multimedia:
379  return "multimedia";
380  break;
381  case media::Player::AudioStreamRole::phone:
382  return "phone";
383  break;
384  default:
385  return "multimedia";
386  break;
387  }
388 }
389 
391 {
392  if (g_strcmp0(orientation, "rotate-0") == 0)
393  return media::Player::Orientation::rotate0;
394  else if (g_strcmp0(orientation, "rotate-90") == 0)
395  return media::Player::Orientation::rotate90;
396  else if (g_strcmp0(orientation, "rotate-180") == 0)
397  return media::Player::Orientation::rotate180;
398  else if (g_strcmp0(orientation, "rotate-270") == 0)
399  return media::Player::Orientation::rotate270;
400  else
401  return media::Player::Orientation::rotate0;
402 }
403 
406 {
407  std::string role_str("props,media.role=" + get_audio_role_str(new_audio_role));
408  std::cout << "Audio stream role: " << role_str << std::endl;
409 
410  GstStructure *props = gst_structure_from_string (role_str.c_str(), NULL);
411  if (audio_sink != nullptr && props != nullptr) {
412  g_object_set (audio_sink, "stream-properties", props, NULL);
413  } else {
414  std::cerr <<
415  "Warning: couldn't set audio stream role - couldn't get audio_sink from pipeline" <<
416  std::endl;
417  }
418 
419  gst_structure_free (props);
420 }
421 
423 {
424  player_lifetime = lifetime;
425 }
426 
428 {
429  int64_t pos = 0;
430  gst_element_query_position (pipeline, GST_FORMAT_TIME, &pos);
431 
432  // This prevents a 0 position from being reported to the app which happens while seeking.
433  // This is covering over a GStreamer issue
434  if ((static_cast<uint64_t>(pos) < duration()) && is_seeking && pos == 0)
435  {
436  return previous_position;
437  }
438 
439  // Save the current position to use just in case it's needed the next time position is
440  // requested
441  previous_position = static_cast<uint64_t>(pos);
442 
443  // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly
444  return static_cast<uint64_t>(pos);
445 }
446 
448 {
449  int64_t dur = 0;
450  gst_element_query_duration (pipeline, GST_FORMAT_TIME, &dur);
451 
452  // FIXME: this should be int64_t, but dbus-cpp doesn't seem to handle it correctly
453  return static_cast<uint64_t>(dur);
454 }
455 
457  const std::string& uri,
459  bool do_pipeline_reset)
460 {
461  gchar *current_uri = nullptr;
462  g_object_get(pipeline, "current-uri", &current_uri, NULL);
463 
464  // Checking for a current_uri being set and not resetting the pipeline
465  // if there isn't a current_uri causes the first play to start playback
466  // sooner since reset_pipeline won't be called
467  if (current_uri and do_pipeline_reset)
468  reset_pipeline();
469 
470  std::string tmp_uri{uri};
471  media::UriCheck::Ptr uri_check{std::make_shared<media::UriCheck>(uri)};
472  if (uri_check->is_local_file())
473  {
474  if (uri_check->is_encoded())
475  {
476  // First decode the URI just in case it's partially encoded already
477  tmp_uri = decode_uri(uri);
478 #ifdef VERBOSE_DEBUG
479  std::cout << "File URI was encoded, now decoded: " << tmp_uri << std::endl;
480 #endif
481  }
482  tmp_uri = encode_uri(tmp_uri);
483  }
484 
485  g_object_set(pipeline, "uri", tmp_uri.c_str(), NULL);
486  if (is_video_file(tmp_uri))
488  else if (is_audio_file(tmp_uri))
490 
491  request_headers = headers;
492 
493  g_free(current_uri);
494 }
495 
496 void gstreamer::Playbin::setup_source(GstElement *source)
497 {
498  if (source == NULL || request_headers.empty())
499  return;
500 
501  if (request_headers.find("Cookie") != request_headers.end()) {
502  if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),
503  "cookies") != NULL) {
504  gchar ** cookies = g_strsplit(request_headers["Cookie"].c_str(), ";", 0);
505  g_object_set(source, "cookies", cookies, NULL);
506  g_strfreev(cookies);
507  }
508  }
509 
510  if (request_headers.find("User-Agent") != request_headers.end()) {
511  if (g_object_class_find_property(G_OBJECT_GET_CLASS(source),
512  "user-agent") != NULL) {
513  g_object_set(source, "user-agent", request_headers["User-Agent"].c_str(), NULL);
514  }
515  }
516 }
517 
518 std::string gstreamer::Playbin::uri() const
519 {
520  gchar* data = nullptr;
521  g_object_get(pipeline, "current-uri", &data, nullptr);
522 
523  std::string result((data == nullptr ? "" : data));
524  g_free(data);
525 
526  return result;
527 }
528 
530 {
531  static const std::chrono::nanoseconds state_change_timeout
532  {
533  // We choose a quite high value here as tests are run under valgrind
534  // and gstreamer pipeline setup/state changes take longer in that scenario.
535  // The value does not negatively impact runtime performance.
536  std::chrono::milliseconds{5000}
537  };
538 
539  auto ret = gst_element_set_state(pipeline, new_state);
540 
541  std::cout << __PRETTY_FUNCTION__ << ": requested state change." << std::endl;
542 
543  bool result = false; GstState current, pending;
544  switch(ret)
545  {
546  case GST_STATE_CHANGE_FAILURE:
547  result = false; break;
548  case GST_STATE_CHANGE_NO_PREROLL:
549  case GST_STATE_CHANGE_SUCCESS:
550  result = true; break;
551  case GST_STATE_CHANGE_ASYNC:
552  result = GST_STATE_CHANGE_SUCCESS == gst_element_get_state(
553  pipeline,
554  &current,
555  &pending,
556  state_change_timeout.count());
557  break;
558  }
559 
560  // We only should query the pipeline if we actually succeeded in
561  // setting the requested state.
562  if (result && new_state == GST_STATE_PLAYING)
563  {
564  // Get the video height/width from the video sink
565  try
566  {
569  cached_video_dimensions = new_dimensions;
570  }
571  catch (const std::exception& e)
572  {
573  std::cerr << "Problem querying video dimensions: " << e.what() << std::endl;
574  }
575  catch (...)
576  {
577  std::cerr << "Problem querying video dimensions." << std::endl;
578  }
579 
580 #ifdef DEBUG_GST_PIPELINE
581  std::cout << "Dumping pipeline dot file" << std::endl;
582  GST_DEBUG_BIN_TO_DOT_FILE((GstBin*)pipeline, GST_DEBUG_GRAPH_SHOW_ALL, "pipeline");
583 #endif
584  }
585 
586  return result;
587 }
588 
589 bool gstreamer::Playbin::seek(const std::chrono::microseconds& ms)
590 {
591  is_seeking = true;
592  return gst_element_seek_simple(
593  pipeline,
594  GST_FORMAT_TIME,
595  (GstSeekFlags)(GST_SEEK_FLAG_FLUSH | GST_SEEK_FLAG_KEY_UNIT),
596  ms.count() * 1000);
597 }
598 
600 {
601  if (not video_sink || not is_mir_video_sink())
602  throw std::runtime_error
603  {
604  "Missing video sink or video sink does not support query of width and height."
605  };
606 
607  // Initialize to default value prior to querying actual values from the sink.
608  uint32_t video_width = 0, video_height = 0;
609  g_object_get (video_sink, "height", &video_height, nullptr);
610  g_object_get (video_sink, "width", &video_width, nullptr);
611 
612  // TODO(tvoss): We should probably check here if width and height are valid.
614  {
617  };
618 }
619 
621 {
622  // Only signal the application layer if the dimensions have in fact changed. This might happen
623  // if reusing the same media-hub session to play two different video sources.
624  if (new_dimensions != cached_video_dimensions)
625  signals.on_video_dimensions_changed(new_dimensions);
626 }
627 
628 std::string gstreamer::Playbin::file_info_from_uri(const std::string& uri) const
629 {
630  GError *error = nullptr;
631  // Open the URI and get the mime type from it. This will currently only work for
632  // a local file
633  std::unique_ptr<GFile, void(*)(void *)> file(
634  g_file_new_for_uri(uri.c_str()), g_object_unref);
635  std::unique_ptr<GFileInfo, void(*)(void *)> info(
636  g_file_query_info(
637  file.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE ","
638  G_FILE_ATTRIBUTE_ETAG_VALUE, G_FILE_QUERY_INFO_NONE,
639  /* cancellable */ NULL, &error),
640  g_object_unref);
641  if (!info)
642  return std::string();
643 
644  return std::string(g_file_info_get_attribute_string(
645  info.get(), G_FILE_ATTRIBUTE_STANDARD_FAST_CONTENT_TYPE));
646 }
647 
648 std::string gstreamer::Playbin::encode_uri(const std::string& uri) const
649 {
650  if (uri.empty())
651  return std::string();
652 
653  std::string encoded_uri;
654  media::UriCheck::Ptr uri_check{std::make_shared<media::UriCheck>(uri)};
655  gchar *uri_scheme = g_uri_parse_scheme(uri.c_str());
656  // We have a URI and it is already percent encoded
657  if (uri_scheme and strlen(uri_scheme) > 0 and uri_check->is_encoded())
658  {
659 #ifdef VERBOSE_DEBUG
660  std::cout << "Is a URI and is already percent encoded" << std::endl;
661 #endif
662  encoded_uri = uri;
663  }
664  // We have a URI but it's not already percent encoded
665  else if (uri_scheme and strlen(uri_scheme) > 0 and !uri_check->is_encoded())
666  {
667 #ifdef VERBOSE_DEBUG
668  std::cout << "Is a URI and is not already percent encoded" << std::endl;
669 #endif
670  gchar *encoded = g_uri_escape_string(uri.c_str(),
671  "!$&'()*+,;=:/?[]@", // reserved chars
672  TRUE); // Allow UTF-8 chars
673  if (!encoded)
674  {
675  g_free(uri_scheme);
676  return std::string();
677  }
678  encoded_uri.assign(encoded);
679  g_free(encoded);
680  }
681  else // We have a path and not a URI. Turn it into a full URI and encode it
682  {
683  GError *error = nullptr;
684 #ifdef VERBOSE_DEBUG
685  std::cout << "Is a path and is not already percent encoded" << std::endl;
686 #endif
687  gchar *str = g_filename_to_uri(uri.c_str(), nullptr, &error);
688  if (!str)
689  {
690  g_free(uri_scheme);
691  return std::string();
692  }
693  encoded_uri.assign(str);
694  g_free(str);
695  if (error != nullptr)
696  {
697  std::cerr << "Warning: failed to get actual track content type: " << error->message
698  << std::endl;
699  g_error_free(error);
700  g_free(str);
701  g_free(uri_scheme);
702  return std::string("audio/video/");
703  }
704  gchar *escaped = g_uri_escape_string(encoded_uri.c_str(),
705  "!$&'()*+,;=:/?[]@", // reserved chars
706  TRUE); // Allow UTF-8 chars
707  if (!escaped)
708  {
709  g_free(uri_scheme);
710  return std::string();
711  }
712  encoded_uri.assign(escaped);
713  g_free(escaped);
714  }
715 
716  g_free(uri_scheme);
717 
718  return encoded_uri;
719 }
720 
721 std::string gstreamer::Playbin::decode_uri(const std::string& uri) const
722 {
723  if (uri.empty())
724  return std::string();
725 
726  gchar *decoded_gchar = g_uri_unescape_string(uri.c_str(), nullptr);
727  if (!decoded_gchar)
728  return std::string();
729 
730  const std::string decoded{decoded_gchar};
731  g_free(decoded_gchar);
732  return decoded;
733 }
734 
735 std::string gstreamer::Playbin::get_file_content_type(const std::string& uri) const
736 {
737  if (uri.empty())
738  return std::string();
739 
740  const std::string encoded_uri{encode_uri(uri)};
741 
742  const std::string content_type {file_info_from_uri(encoded_uri)};
743  if (content_type.empty())
744  {
745  std::cerr << "Warning: failed to get actual track content type" << std::endl;
746  return std::string("audio/video/");
747  }
748 
749  std::cout << "Found content type: " << content_type << std::endl;
750 
751  return content_type;
752 }
753 
754 bool gstreamer::Playbin::is_audio_file(const std::string& uri) const
755 {
756  if (uri.empty())
757  return false;
758 
759  if (get_file_content_type(uri).find("audio/") == 0)
760  {
761  std::cout << "Found audio content" << std::endl;
762  return true;
763  }
764 
765  return false;
766 }
767 
768 bool gstreamer::Playbin::is_video_file(const std::string& uri) const
769 {
770  if (uri.empty())
771  return false;
772 
773  if (get_file_content_type(uri).find("video/") == 0)
774  {
775  std::cout << "Found video content" << std::endl;
776  return true;
777  }
778 
779  return false;
780 }
781 
783 {
784  return file_type;
785 }
786 
788 {
789  /*
790  * We do not consider that we can play the video when
791  * 1. No audio stream selected due to missing decoder
792  * 2. No video stream selected due to missing decoder
793  * 3. No stream selected at all
794  * Note that if there are several, say, audio streams, we will play the file
795  * provided that we can decode just one of them, even if there are missing
796  * audio codecs. We will also play files with only one type of stream.
797  */
798  if ((is_missing_audio_codec && audio_stream_id == -1) ||
800  (audio_stream_id == -1 && video_stream_id == -1))
801  return false;
802  else
803  return true;
804 }
static std::string get_audio_role_str(core::ubuntu::media::Player::AudioStreamRole audio_role)
Definition: playbin.cpp:368
bool set_state_and_wait(GstState new_state)
Definition: playbin.cpp:529
static void source_setup(GstElement *, GstElement *source, gpointer user_data)
Definition: playbin.cpp:92
void setup_source(GstElement *source)
Definition: playbin.cpp:496
std::tuple< Height, Width > Dimensions
Height and Width of a video.
Definition: dimensions.h:139
gint audio_stream_id
Definition: playbin.h:149
void set_uri(const std::string &uri, const core::ubuntu::media::Player::HeadersType &headers, bool do_pipeline_reset=true)
Definition: playbin.cpp:456
core::ubuntu::media::video::Dimensions get_video_dimensions() const
Definition: playbin.cpp:599
GstMessageType type
Definition: bus.h:181
void process_message_element(GstMessage *message)
Definition: playbin.cpp:222
bool seek(const std::chrono::microseconds &ms)
Definition: playbin.cpp:589
uint64_t duration() const
Definition: playbin.cpp:447
void reset_pipeline()
Definition: playbin.cpp:199
core::Connection on_new_message_connection_async
Definition: playbin.h:124
static const std::string & pipeline_name()
Definition: playbin.cpp:80
void emit_video_dimensions_changed_if_changed(const core::ubuntu::media::video::Dimensions &new_dimensions)
Definition: playbin.cpp:620
STL namespace.
bool can_play_streams() const
Definition: playbin.cpp:787
void set_lifetime(core::ubuntu::media::Player::Lifetime)
Definition: playbin.cpp:422
struct gstreamer::Playbin::@12 signals
GstElement * video_sink
Definition: playbin.h:122
void set_audio_stream_role(core::ubuntu::media::Player::AudioStreamRole new_audio_role)
Definition: playbin.cpp:405
bool is_missing_audio_codec
Definition: playbin.h:147
gint video_stream_id
Definition: playbin.h:150
std::map< std::string, std::string > HeadersType
Definition: player.h:49
core::ubuntu::media::Player::Orientation orientation_lut(const gchar *orientation)
Definition: playbin.cpp:390
gstreamer::Bus & message_bus()
Definition: playbin.cpp:307
core::Signal< void > about_to_finish
Definition: playbin.h:134
std::shared_ptr< UriCheck > Ptr
Definition: uri_check.h:37
std::string uri() const
Definition: playbin.cpp:518
void setup_pipeline_for_audio_video()
Definition: playbin.cpp:312
GstMessage * message
Definition: bus.h:180
union gstreamer::Bus::Message::Detail detail
GstElement * pipeline
Definition: playbin.h:119
GstElement * audio_sink
Definition: playbin.h:123
std::string get_file_content_type(const std::string &uri) const
Definition: playbin.cpp:735
std::string encode_uri(const std::string &uri) const
Definition: playbin.cpp:648
gulong source_setup_handler_id
Definition: playbin.h:131
core::ubuntu::media::video::Dimensions cached_video_dimensions
Definition: playbin.h:127
struct gstreamer::Bus::Message::Detail::Tag tag
boost::flyweight< std::string > source
Definition: bus.h:182
bool is_audio_file(const std::string &uri) const
Definition: playbin.cpp:754
bool is_video_file(const std::string &uri) const
Definition: playbin.cpp:768
struct gstreamer::Bus::Message::Detail::ErrorWarningInfo error_warning_info
void set_volume(double new_volume)
Definition: playbin.cpp:362
void on_new_message_async(const Bus::Message &message)
Definition: playbin.cpp:256
void create_video_sink(uint32_t texture_id)
Definition: playbin.cpp:352
gulong about_to_finish_handler_id
Definition: playbin.h:130
gstreamer::Bus bus
Definition: playbin.h:120
bool is_missing_video_codec
Definition: playbin.h:148
core::Signal< Message > on_new_message_async
Definition: bus.h:333
MediaFileType file_type
Definition: playbin.h:121
core::ubuntu::media::Player::HeadersType request_headers
Definition: playbin.h:128
uint64_t position() const
Definition: playbin.cpp:427
IntWrapper is a type-safe integer that allows for encoding/enforcing semantics by means of tags...
Definition: dimensions.h:68
std::string decode_uri(const std::string &uri) const
Definition: playbin.cpp:721
MediaFileType media_file_type() const
Definition: playbin.cpp:782
struct gstreamer::Bus::Message::Detail::StateChanged state_changed
uint64_t previous_position
Definition: playbin.h:126
core::ubuntu::media::Player::Lifetime player_lifetime
Definition: playbin.h:129
std::string file_info_from_uri(const std::string &uri) const
Definition: playbin.cpp:628