3 * Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
4 * Copyright (C) 2015 Brijesh Singh <brijesh.ksingh@gmail.com>
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the
18 * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
25 * @short_description: Player
36 * - Subtitle font, connection speed
38 * - Buffering control (-> progressive downloading)
39 * - Playlist/queue object
40 * - Custom video sink (e.g. embed in GL scene)
48 #include "gstplayer.h"
49 #include "gstplayer-signal-dispatcher-private.h"
50 #include "gstplayer-video-renderer-private.h"
51 #include "gstplayer-media-info-private.h"
54 #include <gst/video/video.h>
55 #include <gst/video/colorbalance.h>
56 #include <gst/tag/tag.h>
57 #include <gst/pbutils/descriptions.h>
61 GST_DEBUG_CATEGORY_STATIC (gst_player_debug);
62 #define GST_CAT_DEFAULT gst_player_debug
64 #define DEFAULT_URI NULL
65 #define DEFAULT_POSITION GST_CLOCK_TIME_NONE
66 #define DEFAULT_DURATION GST_CLOCK_TIME_NONE
67 #define DEFAULT_VOLUME 1.0
68 #define DEFAULT_MUTE FALSE
69 #define DEFAULT_RATE 1.0
70 #define DEFAULT_POSITION_UPDATE_INTERVAL_MS 100
71 #define DEFAULT_AUDIO_VIDEO_OFFSET 0
72 #define DEFAULT_SUBTITLE_VIDEO_OFFSET 0
75 * gst_player_error_quark:
78 gst_player_error_quark (void)
80 return g_quark_from_static_string ("gst-player-error-quark");
83 static GQuark QUARK_CONFIG;
85 /* Keep ConfigQuarkId and _config_quark_strings ordered and synced */
88 CONFIG_QUARK_USER_AGENT = 0,
89 CONFIG_QUARK_POSITION_INTERVAL_UPDATE,
90 CONFIG_QUARK_ACCURATE_SEEK,
95 static const gchar *_config_quark_strings[] = {
97 "position-interval-update",
101 GQuark _config_quark_table[CONFIG_QUARK_MAX];
103 #define CONFIG_QUARK(q) _config_quark_table[CONFIG_QUARK_##q]
109 PROP_SIGNAL_DISPATCHER,
115 PROP_CURRENT_AUDIO_TRACK,
116 PROP_CURRENT_VIDEO_TRACK,
117 PROP_CURRENT_SUBTITLE_TRACK,
122 PROP_VIDEO_MULTIVIEW_MODE,
123 PROP_VIDEO_MULTIVIEW_FLAGS,
124 PROP_AUDIO_VIDEO_OFFSET,
125 PROP_SUBTITLE_VIDEO_OFFSET,
132 SIGNAL_POSITION_UPDATED,
133 SIGNAL_DURATION_CHANGED,
134 SIGNAL_STATE_CHANGED,
136 SIGNAL_END_OF_STREAM,
139 SIGNAL_VIDEO_DIMENSIONS_CHANGED,
140 SIGNAL_MEDIA_INFO_UPDATED,
141 SIGNAL_VOLUME_CHANGED,
149 GST_PLAY_FLAG_VIDEO = (1 << 0),
150 GST_PLAY_FLAG_AUDIO = (1 << 1),
151 GST_PLAY_FLAG_SUBTITLE = (1 << 2),
152 GST_PLAY_FLAG_VIS = (1 << 3)
159 GstPlayerVideoRenderer *video_renderer;
160 GstPlayerSignalDispatcher *signal_dispatcher;
169 GMainContext *context;
174 GstState target_state, current_state;
175 gboolean is_live, is_eos;
176 GSource *tick_source, *ready_timeout_source;
177 GstClockTime cached_duration;
181 GstPlayerState app_state;
184 GstTagList *global_tags;
185 GstPlayerMediaInfo *media_info;
187 GstElement *current_vis_element;
189 GstStructure *config;
191 /* Protected by lock */
192 gboolean seek_pending; /* Only set from main context */
193 GstClockTime last_seek_time; /* Only set from main context */
194 GSource *seek_source;
195 GstClockTime seek_position;
196 /* If TRUE, all signals are inhibited except the
197 * state-changed:GST_PLAYER_STATE_STOPPED/PAUSED. This ensures that no signal
198 * is emitted after gst_player_stop/pause() has been called by the user. */
199 gboolean inhibit_sigs;
202 gboolean use_playbin3;
203 GstStreamCollection *collection;
207 gulong stream_notify_id;
210 struct _GstPlayerClass
212 GstObjectClass parent_class;
215 #define parent_class gst_player_parent_class
216 G_DEFINE_TYPE (GstPlayer, gst_player, GST_TYPE_OBJECT);
218 static guint signals[SIGNAL_LAST] = { 0, };
219 static GParamSpec *param_specs[PROP_LAST] = { NULL, };
221 static void gst_player_dispose (GObject * object);
222 static void gst_player_finalize (GObject * object);
223 static void gst_player_set_property (GObject * object, guint prop_id,
224 const GValue * value, GParamSpec * pspec);
225 static void gst_player_get_property (GObject * object, guint prop_id,
226 GValue * value, GParamSpec * pspec);
227 static void gst_player_constructed (GObject * object);
229 static gpointer gst_player_main (gpointer data);
231 static void gst_player_seek_internal_locked (GstPlayer * self);
232 static void gst_player_stop_internal (GstPlayer * self, gboolean transient);
233 static gboolean gst_player_pause_internal (gpointer user_data);
234 static gboolean gst_player_play_internal (gpointer user_data);
235 static gboolean gst_player_seek_internal (gpointer user_data);
236 static void gst_player_set_rate_internal (GstPlayer * self);
237 static void change_state (GstPlayer * self, GstPlayerState state);
239 static GstPlayerMediaInfo *gst_player_media_info_create (GstPlayer * self);
241 static void gst_player_streams_info_create (GstPlayer * self,
242 GstPlayerMediaInfo * media_info, const gchar * prop, GType type);
243 static void gst_player_stream_info_update (GstPlayer * self,
244 GstPlayerStreamInfo * s);
245 static void gst_player_stream_info_update_tags_and_caps (GstPlayer * self,
246 GstPlayerStreamInfo * s);
247 static GstPlayerStreamInfo *gst_player_stream_info_find (GstPlayerMediaInfo *
248 media_info, GType type, gint stream_index);
249 static GstPlayerStreamInfo *gst_player_stream_info_get_current (GstPlayer *
250 self, const gchar * prop, GType type);
252 static void gst_player_video_info_update (GstPlayer * self,
253 GstPlayerStreamInfo * stream_info);
254 static void gst_player_audio_info_update (GstPlayer * self,
255 GstPlayerStreamInfo * stream_info);
256 static void gst_player_subtitle_info_update (GstPlayer * self,
257 GstPlayerStreamInfo * stream_info);
260 static void gst_player_streams_info_create_from_collection (GstPlayer * self,
261 GstPlayerMediaInfo * media_info, GstStreamCollection * collection);
262 static void gst_player_stream_info_update_from_stream (GstPlayer * self,
263 GstPlayerStreamInfo * s, GstStream * stream);
264 static GstPlayerStreamInfo *gst_player_stream_info_find_from_stream_id
265 (GstPlayerMediaInfo * media_info, const gchar * stream_id);
266 static GstPlayerStreamInfo *gst_player_stream_info_get_current_from_stream_id
267 (GstPlayer * self, const gchar * stream_id, GType type);
268 static void stream_notify_cb (GstStreamCollection * collection,
269 GstStream * stream, GParamSpec * pspec, GstPlayer * self);
271 static void emit_media_info_updated_signal (GstPlayer * self);
273 static void *get_title (GstTagList * tags);
274 static void *get_container_format (GstTagList * tags);
275 static void *get_from_tags (GstPlayer * self, GstPlayerMediaInfo * media_info,
276 void *(*func) (GstTagList *));
277 static void *get_cover_sample (GstTagList * tags);
279 static void remove_seek_source (GstPlayer * self);
282 gst_player_init (GstPlayer * self)
284 GST_TRACE_OBJECT (self, "Initializing");
286 self = gst_player_get_instance_private (self);
288 g_mutex_init (&self->lock);
289 g_cond_init (&self->cond);
291 self->context = g_main_context_new ();
292 self->loop = g_main_loop_new (self->context, FALSE);
295 self->config = gst_structure_new_id (QUARK_CONFIG,
296 CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, DEFAULT_POSITION_UPDATE_INTERVAL_MS,
297 CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, FALSE,
301 self->seek_pending = FALSE;
302 self->seek_position = GST_CLOCK_TIME_NONE;
303 self->last_seek_time = GST_CLOCK_TIME_NONE;
304 self->inhibit_sigs = FALSE;
306 GST_TRACE_OBJECT (self, "Initialized");
310 config_quark_initialize (void)
314 QUARK_CONFIG = g_quark_from_static_string ("player-config");
316 if (G_N_ELEMENTS (_config_quark_strings) != CONFIG_QUARK_MAX)
317 g_warning ("the quark table is not consistent! %d != %d",
318 (int) G_N_ELEMENTS (_config_quark_strings), CONFIG_QUARK_MAX);
320 for (i = 0; i < CONFIG_QUARK_MAX; i++) {
321 _config_quark_table[i] =
322 g_quark_from_static_string (_config_quark_strings[i]);
327 gst_player_class_init (GstPlayerClass * klass)
329 GObjectClass *gobject_class = (GObjectClass *) klass;
331 gobject_class->set_property = gst_player_set_property;
332 gobject_class->get_property = gst_player_get_property;
333 gobject_class->dispose = gst_player_dispose;
334 gobject_class->finalize = gst_player_finalize;
335 gobject_class->constructed = gst_player_constructed;
337 param_specs[PROP_VIDEO_RENDERER] =
338 g_param_spec_object ("video-renderer",
339 "Video Renderer", "Video renderer to use for rendering videos",
340 GST_TYPE_PLAYER_VIDEO_RENDERER,
341 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
343 param_specs[PROP_SIGNAL_DISPATCHER] =
344 g_param_spec_object ("signal-dispatcher",
345 "Signal Dispatcher", "Dispatcher for the signals to e.g. event loops",
346 GST_TYPE_PLAYER_SIGNAL_DISPATCHER,
347 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
349 param_specs[PROP_URI] = g_param_spec_string ("uri", "URI", "Current URI",
350 DEFAULT_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
352 param_specs[PROP_SUBURI] = g_param_spec_string ("suburi", "Subtitle URI",
353 "Current Subtitle URI", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
355 param_specs[PROP_POSITION] =
356 g_param_spec_uint64 ("position", "Position", "Current Position",
357 0, G_MAXUINT64, DEFAULT_POSITION,
358 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
360 param_specs[PROP_MEDIA_INFO] =
361 g_param_spec_object ("media-info", "Media Info",
362 "Current media information", GST_TYPE_PLAYER_MEDIA_INFO,
363 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
365 param_specs[PROP_CURRENT_AUDIO_TRACK] =
366 g_param_spec_object ("current-audio-track", "Current Audio Track",
367 "Current audio track information", GST_TYPE_PLAYER_AUDIO_INFO,
368 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
370 param_specs[PROP_CURRENT_VIDEO_TRACK] =
371 g_param_spec_object ("current-video-track", "Current Video Track",
372 "Current video track information", GST_TYPE_PLAYER_VIDEO_INFO,
373 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
375 param_specs[PROP_CURRENT_SUBTITLE_TRACK] =
376 g_param_spec_object ("current-subtitle-track", "Current Subtitle Track",
377 "Current audio subtitle information", GST_TYPE_PLAYER_SUBTITLE_INFO,
378 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
380 param_specs[PROP_DURATION] =
381 g_param_spec_uint64 ("duration", "Duration", "Duration",
382 0, G_MAXUINT64, DEFAULT_DURATION,
383 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
385 param_specs[PROP_VOLUME] =
386 g_param_spec_double ("volume", "Volume", "Volume",
387 0, 10.0, DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
389 param_specs[PROP_MUTE] =
390 g_param_spec_boolean ("mute", "Mute", "Mute",
391 DEFAULT_MUTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
393 param_specs[PROP_PIPELINE] =
394 g_param_spec_object ("pipeline", "Pipeline",
395 "GStreamer pipeline that is used",
396 GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
398 param_specs[PROP_RATE] =
399 g_param_spec_double ("rate", "rate", "Playback rate",
400 -64.0, 64.0, DEFAULT_RATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
402 param_specs[PROP_VIDEO_MULTIVIEW_MODE] =
403 g_param_spec_enum ("video-multiview-mode",
404 "Multiview Mode Override",
405 "Re-interpret a video stream as one of several frame-packed stereoscopic modes.",
406 GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING,
407 GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE,
408 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
410 param_specs[PROP_VIDEO_MULTIVIEW_FLAGS] =
411 g_param_spec_flags ("video-multiview-flags",
412 "Multiview Flags Override",
413 "Override details of the multiview frame layout",
414 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
415 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
417 param_specs[PROP_AUDIO_VIDEO_OFFSET] =
418 g_param_spec_int64 ("audio-video-offset", "Audio Video Offset",
419 "The synchronisation offset between audio and video in nanoseconds",
420 G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
422 param_specs[PROP_SUBTITLE_VIDEO_OFFSET] =
423 g_param_spec_int64 ("subtitle-video-offset", "Text Video Offset",
424 "The synchronisation offset between text and video in nanoseconds",
425 G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
427 g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
429 signals[SIGNAL_URI_LOADED] =
430 g_signal_new ("uri-loaded", G_TYPE_FROM_CLASS (klass),
431 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
432 NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING);
434 signals[SIGNAL_POSITION_UPDATED] =
435 g_signal_new ("position-updated", G_TYPE_FROM_CLASS (klass),
436 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
437 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
439 signals[SIGNAL_DURATION_CHANGED] =
440 g_signal_new ("duration-changed", G_TYPE_FROM_CLASS (klass),
441 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
442 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
444 signals[SIGNAL_STATE_CHANGED] =
445 g_signal_new ("state-changed", G_TYPE_FROM_CLASS (klass),
446 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
447 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_PLAYER_STATE);
449 signals[SIGNAL_BUFFERING] =
450 g_signal_new ("buffering", G_TYPE_FROM_CLASS (klass),
451 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
452 NULL, NULL, G_TYPE_NONE, 1, G_TYPE_INT);
454 signals[SIGNAL_END_OF_STREAM] =
455 g_signal_new ("end-of-stream", G_TYPE_FROM_CLASS (klass),
456 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
457 NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID);
459 signals[SIGNAL_ERROR] =
460 g_signal_new ("error", G_TYPE_FROM_CLASS (klass),
461 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
462 NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ERROR);
464 signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED] =
465 g_signal_new ("video-dimensions-changed", G_TYPE_FROM_CLASS (klass),
466 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
467 NULL, NULL, G_TYPE_NONE, 2, G_TYPE_INT, G_TYPE_INT);
469 signals[SIGNAL_MEDIA_INFO_UPDATED] =
470 g_signal_new ("media-info-updated", G_TYPE_FROM_CLASS (klass),
471 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
472 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_PLAYER_MEDIA_INFO);
474 signals[SIGNAL_VOLUME_CHANGED] =
475 g_signal_new ("volume-changed", G_TYPE_FROM_CLASS (klass),
476 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
477 NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID);
479 signals[SIGNAL_MUTE_CHANGED] =
480 g_signal_new ("mute-changed", G_TYPE_FROM_CLASS (klass),
481 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
482 NULL, NULL, G_TYPE_NONE, 0, G_TYPE_INVALID);
484 signals[SIGNAL_WARNING] =
485 g_signal_new ("warning", G_TYPE_FROM_CLASS (klass),
486 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
487 NULL, NULL, G_TYPE_NONE, 1, G_TYPE_ERROR);
489 signals[SIGNAL_SEEK_DONE] =
490 g_signal_new ("seek-done", G_TYPE_FROM_CLASS (klass),
491 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
492 NULL, NULL, G_TYPE_NONE, 1, GST_TYPE_CLOCK_TIME);
494 config_quark_initialize ();
498 gst_player_dispose (GObject * object)
500 GstPlayer *self = GST_PLAYER (object);
502 GST_TRACE_OBJECT (self, "Stopping main thread");
505 g_main_loop_quit (self->loop);
507 if (self->thread != g_thread_self ())
508 g_thread_join (self->thread);
510 g_thread_unref (self->thread);
513 g_main_loop_unref (self->loop);
516 g_main_context_unref (self->context);
517 self->context = NULL;
520 G_OBJECT_CLASS (parent_class)->dispose (object);
524 gst_player_finalize (GObject * object)
526 GstPlayer *self = GST_PLAYER (object);
528 GST_TRACE_OBJECT (self, "Finalizing");
531 g_free (self->redirect_uri);
532 g_free (self->suburi);
533 g_free (self->video_sid);
534 g_free (self->audio_sid);
535 g_free (self->subtitle_sid);
536 if (self->global_tags)
537 gst_tag_list_unref (self->global_tags);
538 if (self->video_renderer)
539 g_object_unref (self->video_renderer);
540 if (self->signal_dispatcher)
541 g_object_unref (self->signal_dispatcher);
542 if (self->current_vis_element)
543 gst_object_unref (self->current_vis_element);
545 gst_structure_free (self->config);
546 if (self->collection)
547 gst_object_unref (self->collection);
548 g_mutex_clear (&self->lock);
549 g_cond_clear (&self->cond);
551 G_OBJECT_CLASS (parent_class)->finalize (object);
555 gst_player_constructed (GObject * object)
557 GstPlayer *self = GST_PLAYER (object);
559 GST_TRACE_OBJECT (self, "Constructed");
561 g_mutex_lock (&self->lock);
562 self->thread = g_thread_new ("GstPlayer", gst_player_main, self);
563 while (!self->loop || !g_main_loop_is_running (self->loop))
564 g_cond_wait (&self->cond, &self->lock);
565 g_mutex_unlock (&self->lock);
567 G_OBJECT_CLASS (parent_class)->constructed (object);
574 } UriLoadedSignalData;
577 uri_loaded_dispatch (gpointer user_data)
579 UriLoadedSignalData *data = user_data;
581 g_signal_emit (data->player, signals[SIGNAL_URI_LOADED], 0, data->uri);
585 uri_loaded_signal_data_free (UriLoadedSignalData * data)
587 g_object_unref (data->player);
593 gst_player_set_uri_internal (gpointer user_data)
595 GstPlayer *self = user_data;
597 gst_player_stop_internal (self, FALSE);
599 g_mutex_lock (&self->lock);
601 GST_DEBUG_OBJECT (self, "Changing URI to '%s'", GST_STR_NULL (self->uri));
603 g_object_set (self->playbin, "uri", self->uri, NULL);
605 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
606 signals[SIGNAL_URI_LOADED], 0, NULL, NULL, NULL) != 0) {
607 UriLoadedSignalData *data = g_new (UriLoadedSignalData, 1);
609 data->player = g_object_ref (self);
610 data->uri = g_strdup (self->uri);
611 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
612 uri_loaded_dispatch, data,
613 (GDestroyNotify) uri_loaded_signal_data_free);
616 g_object_set (self->playbin, "suburi", NULL, NULL);
618 g_mutex_unlock (&self->lock);
620 return G_SOURCE_REMOVE;
624 gst_player_set_suburi_internal (gpointer user_data)
626 GstPlayer *self = user_data;
627 GstClockTime position;
628 GstState target_state;
630 /* save the state and position */
631 target_state = self->target_state;
632 position = gst_player_get_position (self);
634 gst_player_stop_internal (self, TRUE);
635 g_mutex_lock (&self->lock);
637 GST_DEBUG_OBJECT (self, "Changing SUBURI to '%s'",
638 GST_STR_NULL (self->suburi));
640 g_object_set (self->playbin, "suburi", self->suburi, NULL);
642 g_mutex_unlock (&self->lock);
644 /* restore state and position */
645 if (position != GST_CLOCK_TIME_NONE)
646 gst_player_seek (self, position);
647 if (target_state == GST_STATE_PAUSED)
648 gst_player_pause_internal (self);
649 else if (target_state == GST_STATE_PLAYING)
650 gst_player_play_internal (self);
652 return G_SOURCE_REMOVE;
656 gst_player_set_rate_internal (GstPlayer * self)
658 self->seek_position = gst_player_get_position (self);
660 /* If there is no seek being dispatch to the main context currently do that,
661 * otherwise we just updated the rate so that it will be taken by
662 * the seek handler from the main context instead of the old one.
664 if (!self->seek_source) {
665 /* If no seek is pending then create new seek source */
666 if (!self->seek_pending) {
667 self->seek_source = g_idle_source_new ();
668 g_source_set_callback (self->seek_source,
669 (GSourceFunc) gst_player_seek_internal, self, NULL);
670 g_source_attach (self->seek_source, self->context);
676 gst_player_set_property (GObject * object, guint prop_id,
677 const GValue * value, GParamSpec * pspec)
679 GstPlayer *self = GST_PLAYER (object);
682 case PROP_VIDEO_RENDERER:
683 self->video_renderer = g_value_dup_object (value);
685 case PROP_SIGNAL_DISPATCHER:
686 self->signal_dispatcher = g_value_dup_object (value);
689 g_mutex_lock (&self->lock);
691 g_free (self->redirect_uri);
692 self->redirect_uri = NULL;
694 g_free (self->suburi);
697 self->uri = g_value_dup_string (value);
698 GST_DEBUG_OBJECT (self, "Set uri=%s", self->uri);
699 g_mutex_unlock (&self->lock);
701 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
702 gst_player_set_uri_internal, self, NULL);
706 g_mutex_lock (&self->lock);
707 g_free (self->suburi);
709 self->suburi = g_value_dup_string (value);
710 GST_DEBUG_OBJECT (self, "Set suburi=%s", self->suburi);
711 g_mutex_unlock (&self->lock);
713 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
714 gst_player_set_suburi_internal, self, NULL);
718 GST_DEBUG_OBJECT (self, "Set volume=%lf", g_value_get_double (value));
719 g_object_set_property (G_OBJECT (self->playbin), "volume", value);
722 g_mutex_lock (&self->lock);
723 self->rate = g_value_get_double (value);
724 GST_DEBUG_OBJECT (self, "Set rate=%lf", g_value_get_double (value));
725 gst_player_set_rate_internal (self);
726 g_mutex_unlock (&self->lock);
729 GST_DEBUG_OBJECT (self, "Set mute=%d", g_value_get_boolean (value));
730 g_object_set_property (G_OBJECT (self->playbin), "mute", value);
732 case PROP_VIDEO_MULTIVIEW_MODE:
733 GST_DEBUG_OBJECT (self, "Set multiview mode=%u",
734 g_value_get_enum (value));
735 g_object_set_property (G_OBJECT (self->playbin), "video-multiview-mode",
738 case PROP_VIDEO_MULTIVIEW_FLAGS:
739 GST_DEBUG_OBJECT (self, "Set multiview flags=%x",
740 g_value_get_flags (value));
741 g_object_set_property (G_OBJECT (self->playbin), "video-multiview-flags",
744 case PROP_AUDIO_VIDEO_OFFSET:
745 g_object_set_property (G_OBJECT (self->playbin), "av-offset", value);
747 case PROP_SUBTITLE_VIDEO_OFFSET:
748 g_object_set_property (G_OBJECT (self->playbin), "text-offset", value);
751 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
757 gst_player_get_property (GObject * object, guint prop_id,
758 GValue * value, GParamSpec * pspec)
760 GstPlayer *self = GST_PLAYER (object);
764 g_mutex_lock (&self->lock);
765 g_value_set_string (value, self->uri);
766 g_mutex_unlock (&self->lock);
769 g_mutex_lock (&self->lock);
770 g_value_set_string (value, self->suburi);
771 g_mutex_unlock (&self->lock);
772 GST_DEBUG_OBJECT (self, "Returning suburi=%s",
773 g_value_get_string (value));
776 gint64 position = GST_CLOCK_TIME_NONE;
778 gst_element_query_position (self->playbin, GST_FORMAT_TIME, &position);
779 g_value_set_uint64 (value, position);
780 GST_TRACE_OBJECT (self, "Returning position=%" GST_TIME_FORMAT,
781 GST_TIME_ARGS (g_value_get_uint64 (value)));
785 g_value_set_uint64 (value, self->cached_duration);
786 GST_TRACE_OBJECT (self, "Returning duration=%" GST_TIME_FORMAT,
787 GST_TIME_ARGS (g_value_get_uint64 (value)));
790 case PROP_MEDIA_INFO:{
791 GstPlayerMediaInfo *media_info = gst_player_get_media_info (self);
792 g_value_take_object (value, media_info);
795 case PROP_CURRENT_AUDIO_TRACK:{
796 GstPlayerAudioInfo *audio_info =
797 gst_player_get_current_audio_track (self);
798 g_value_take_object (value, audio_info);
801 case PROP_CURRENT_VIDEO_TRACK:{
802 GstPlayerVideoInfo *video_info =
803 gst_player_get_current_video_track (self);
804 g_value_take_object (value, video_info);
807 case PROP_CURRENT_SUBTITLE_TRACK:{
808 GstPlayerSubtitleInfo *subtitle_info =
809 gst_player_get_current_subtitle_track (self);
810 g_value_take_object (value, subtitle_info);
814 g_object_get_property (G_OBJECT (self->playbin), "volume", value);
815 GST_TRACE_OBJECT (self, "Returning volume=%lf",
816 g_value_get_double (value));
819 g_mutex_lock (&self->lock);
820 g_value_set_double (value, self->rate);
821 g_mutex_unlock (&self->lock);
824 g_object_get_property (G_OBJECT (self->playbin), "mute", value);
825 GST_TRACE_OBJECT (self, "Returning mute=%d", g_value_get_boolean (value));
828 g_value_set_object (value, self->playbin);
830 case PROP_VIDEO_MULTIVIEW_MODE:{
831 g_object_get_property (G_OBJECT (self->playbin), "video-multiview-mode",
833 GST_TRACE_OBJECT (self, "Return multiview mode=%d",
834 g_value_get_enum (value));
837 case PROP_VIDEO_MULTIVIEW_FLAGS:{
838 g_object_get_property (G_OBJECT (self->playbin), "video-multiview-flags",
840 GST_TRACE_OBJECT (self, "Return multiview flags=%x",
841 g_value_get_flags (value));
844 case PROP_AUDIO_VIDEO_OFFSET:
845 g_object_get_property (G_OBJECT (self->playbin), "av-offset", value);
847 case PROP_SUBTITLE_VIDEO_OFFSET:
848 g_object_get_property (G_OBJECT (self->playbin), "text-offset", value);
851 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
857 main_loop_running_cb (gpointer user_data)
859 GstPlayer *self = GST_PLAYER (user_data);
861 GST_TRACE_OBJECT (self, "Main loop running now");
863 g_mutex_lock (&self->lock);
864 g_cond_signal (&self->cond);
865 g_mutex_unlock (&self->lock);
867 return G_SOURCE_REMOVE;
873 GstPlayerState state;
874 } StateChangedSignalData;
877 state_changed_dispatch (gpointer user_data)
879 StateChangedSignalData *data = user_data;
881 if (data->player->inhibit_sigs && data->state != GST_PLAYER_STATE_STOPPED
882 && data->state != GST_PLAYER_STATE_PAUSED)
885 g_signal_emit (data->player, signals[SIGNAL_STATE_CHANGED], 0, data->state);
889 state_changed_signal_data_free (StateChangedSignalData * data)
891 g_object_unref (data->player);
896 change_state (GstPlayer * self, GstPlayerState state)
898 if (state == self->app_state)
901 GST_DEBUG_OBJECT (self, "Changing app state from %s to %s",
902 gst_player_state_get_name (self->app_state),
903 gst_player_state_get_name (state));
904 self->app_state = state;
906 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
907 signals[SIGNAL_STATE_CHANGED], 0, NULL, NULL, NULL) != 0) {
908 StateChangedSignalData *data = g_new (StateChangedSignalData, 1);
910 data->player = g_object_ref (self);
912 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
913 state_changed_dispatch, data,
914 (GDestroyNotify) state_changed_signal_data_free);
921 GstClockTime position;
922 } PositionUpdatedSignalData;
925 position_updated_dispatch (gpointer user_data)
927 PositionUpdatedSignalData *data = user_data;
929 if (data->player->inhibit_sigs)
932 if (data->player->target_state >= GST_STATE_PAUSED) {
933 g_signal_emit (data->player, signals[SIGNAL_POSITION_UPDATED], 0,
935 g_object_notify_by_pspec (G_OBJECT (data->player),
936 param_specs[PROP_POSITION]);
941 position_updated_signal_data_free (PositionUpdatedSignalData * data)
943 g_object_unref (data->player);
948 tick_cb (gpointer user_data)
950 GstPlayer *self = GST_PLAYER (user_data);
953 if (self->target_state >= GST_STATE_PAUSED
954 && gst_element_query_position (self->playbin, GST_FORMAT_TIME,
956 GST_LOG_OBJECT (self, "Position %" GST_TIME_FORMAT,
957 GST_TIME_ARGS (position));
959 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
960 signals[SIGNAL_POSITION_UPDATED], 0, NULL, NULL, NULL) != 0) {
961 PositionUpdatedSignalData *data = g_new (PositionUpdatedSignalData, 1);
963 data->player = g_object_ref (self);
964 data->position = position;
965 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
966 position_updated_dispatch, data,
967 (GDestroyNotify) position_updated_signal_data_free);
971 return G_SOURCE_CONTINUE;
975 add_tick_source (GstPlayer * self)
977 guint position_update_interval_ms;
979 if (self->tick_source)
982 position_update_interval_ms =
983 gst_player_config_get_position_update_interval (self->config);
984 if (!position_update_interval_ms)
987 self->tick_source = g_timeout_source_new (position_update_interval_ms);
988 g_source_set_callback (self->tick_source, (GSourceFunc) tick_cb, self, NULL);
989 g_source_attach (self->tick_source, self->context);
993 remove_tick_source (GstPlayer * self)
995 if (!self->tick_source)
998 g_source_destroy (self->tick_source);
999 g_source_unref (self->tick_source);
1000 self->tick_source = NULL;
1004 ready_timeout_cb (gpointer user_data)
1006 GstPlayer *self = user_data;
1008 if (self->target_state <= GST_STATE_READY) {
1009 GST_DEBUG_OBJECT (self, "Setting pipeline to NULL state");
1010 self->target_state = GST_STATE_NULL;
1011 self->current_state = GST_STATE_NULL;
1012 gst_element_set_state (self->playbin, GST_STATE_NULL);
1015 return G_SOURCE_REMOVE;
1019 add_ready_timeout_source (GstPlayer * self)
1021 if (self->ready_timeout_source)
1024 self->ready_timeout_source = g_timeout_source_new_seconds (60);
1025 g_source_set_callback (self->ready_timeout_source,
1026 (GSourceFunc) ready_timeout_cb, self, NULL);
1027 g_source_attach (self->ready_timeout_source, self->context);
1031 remove_ready_timeout_source (GstPlayer * self)
1033 if (!self->ready_timeout_source)
1036 g_source_destroy (self->ready_timeout_source);
1037 g_source_unref (self->ready_timeout_source);
1038 self->ready_timeout_source = NULL;
1048 error_dispatch (gpointer user_data)
1050 ErrorSignalData *data = user_data;
1052 if (data->player->inhibit_sigs)
1055 g_signal_emit (data->player, signals[SIGNAL_ERROR], 0, data->err);
1059 free_error_signal_data (ErrorSignalData * data)
1061 g_object_unref (data->player);
1062 g_clear_error (&data->err);
1067 emit_error (GstPlayer * self, GError * err)
1069 GST_ERROR_OBJECT (self, "Error: %s (%s, %d)", err->message,
1070 g_quark_to_string (err->domain), err->code);
1072 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1073 signals[SIGNAL_ERROR], 0, NULL, NULL, NULL) != 0) {
1074 ErrorSignalData *data = g_new (ErrorSignalData, 1);
1076 data->player = g_object_ref (self);
1077 data->err = g_error_copy (err);
1078 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1079 error_dispatch, data, (GDestroyNotify) free_error_signal_data);
1084 remove_tick_source (self);
1085 remove_ready_timeout_source (self);
1087 self->target_state = GST_STATE_NULL;
1088 self->current_state = GST_STATE_NULL;
1089 self->is_live = FALSE;
1090 self->is_eos = FALSE;
1091 gst_element_set_state (self->playbin, GST_STATE_NULL);
1092 change_state (self, GST_PLAYER_STATE_STOPPED);
1093 self->buffering = 100;
1095 g_mutex_lock (&self->lock);
1096 if (self->media_info) {
1097 g_object_unref (self->media_info);
1098 self->media_info = NULL;
1101 if (self->global_tags) {
1102 gst_tag_list_unref (self->global_tags);
1103 self->global_tags = NULL;
1106 self->seek_pending = FALSE;
1107 remove_seek_source (self);
1108 self->seek_position = GST_CLOCK_TIME_NONE;
1109 self->last_seek_time = GST_CLOCK_TIME_NONE;
1110 g_mutex_unlock (&self->lock);
1114 dump_dot_file (GstPlayer * self, const gchar * name)
1118 full_name = g_strdup_printf ("gst-player.%p.%s", self, name);
1120 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->playbin),
1121 GST_DEBUG_GRAPH_SHOW_ALL, full_name);
1130 } WarningSignalData;
1133 warning_dispatch (gpointer user_data)
1135 WarningSignalData *data = user_data;
1137 if (data->player->inhibit_sigs)
1140 g_signal_emit (data->player, signals[SIGNAL_WARNING], 0, data->err);
1144 free_warning_signal_data (WarningSignalData * data)
1146 g_object_unref (data->player);
1147 g_clear_error (&data->err);
1152 emit_warning (GstPlayer * self, GError * err)
1154 GST_ERROR_OBJECT (self, "Warning: %s (%s, %d)", err->message,
1155 g_quark_to_string (err->domain), err->code);
1157 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1158 signals[SIGNAL_WARNING], 0, NULL, NULL, NULL) != 0) {
1159 WarningSignalData *data = g_new (WarningSignalData, 1);
1161 data->player = g_object_ref (self);
1162 data->err = g_error_copy (err);
1163 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1164 warning_dispatch, data, (GDestroyNotify) free_warning_signal_data);
1171 error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1173 GstPlayer *self = GST_PLAYER (user_data);
1174 GError *err, *player_err;
1175 gchar *name, *debug, *message, *full_message;
1177 dump_dot_file (self, "error");
1179 gst_message_parse_error (msg, &err, &debug);
1181 name = gst_object_get_path_string (msg->src);
1182 message = gst_error_get_message (err->domain, err->code);
1186 g_strdup_printf ("Error from element %s: %s\n%s\n%s", name, message,
1187 err->message, debug);
1190 g_strdup_printf ("Error from element %s: %s\n%s", name, message,
1193 GST_ERROR_OBJECT (self, "ERROR: from element %s: %s\n", name, err->message);
1195 GST_ERROR_OBJECT (self, "Additional debug info:\n%s\n", debug);
1198 g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1200 emit_error (self, player_err);
1202 g_clear_error (&err);
1205 g_free (full_message);
1210 warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1212 GstPlayer *self = GST_PLAYER (user_data);
1213 GError *err, *player_err;
1214 gchar *name, *debug, *message, *full_message;
1216 dump_dot_file (self, "warning");
1218 gst_message_parse_warning (msg, &err, &debug);
1220 name = gst_object_get_path_string (msg->src);
1221 message = gst_error_get_message (err->domain, err->code);
1225 g_strdup_printf ("Warning from element %s: %s\n%s\n%s", name, message,
1226 err->message, debug);
1229 g_strdup_printf ("Warning from element %s: %s\n%s", name, message,
1232 GST_WARNING_OBJECT (self, "WARNING: from element %s: %s\n", name,
1235 GST_WARNING_OBJECT (self, "Additional debug info:\n%s\n", debug);
1238 g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1240 emit_warning (self, player_err);
1242 g_clear_error (&err);
1245 g_free (full_message);
1250 eos_dispatch (gpointer user_data)
1252 GstPlayer *player = user_data;
1254 if (player->inhibit_sigs)
1257 g_signal_emit (player, signals[SIGNAL_END_OF_STREAM], 0);
1261 eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1264 GstPlayer *self = GST_PLAYER (user_data);
1266 GST_DEBUG_OBJECT (self, "End of stream");
1269 remove_tick_source (self);
1271 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1272 signals[SIGNAL_END_OF_STREAM], 0, NULL, NULL, NULL) != 0) {
1273 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1274 eos_dispatch, g_object_ref (self), (GDestroyNotify) g_object_unref);
1276 change_state (self, GST_PLAYER_STATE_STOPPED);
1277 self->buffering = 100;
1278 self->is_eos = TRUE;
1285 } BufferingSignalData;
1288 buffering_dispatch (gpointer user_data)
1290 BufferingSignalData *data = user_data;
1292 if (data->player->inhibit_sigs)
1295 if (data->player->target_state >= GST_STATE_PAUSED) {
1296 g_signal_emit (data->player, signals[SIGNAL_BUFFERING], 0, data->percent);
1301 buffering_signal_data_free (BufferingSignalData * data)
1303 g_object_unref (data->player);
1308 buffering_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1310 GstPlayer *self = GST_PLAYER (user_data);
1313 if (self->target_state < GST_STATE_PAUSED)
1318 gst_message_parse_buffering (msg, &percent);
1319 GST_LOG_OBJECT (self, "Buffering %d%%", percent);
1321 if (percent < 100 && self->target_state >= GST_STATE_PAUSED) {
1322 GstStateChangeReturn state_ret;
1324 GST_DEBUG_OBJECT (self, "Waiting for buffering to finish");
1325 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
1327 if (state_ret == GST_STATE_CHANGE_FAILURE) {
1328 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1329 "Failed to handle buffering"));
1333 change_state (self, GST_PLAYER_STATE_BUFFERING);
1336 if (self->buffering != percent) {
1337 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1338 signals[SIGNAL_BUFFERING], 0, NULL, NULL, NULL) != 0) {
1339 BufferingSignalData *data = g_new (BufferingSignalData, 1);
1341 data->player = g_object_ref (self);
1342 data->percent = percent;
1343 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1344 buffering_dispatch, data,
1345 (GDestroyNotify) buffering_signal_data_free);
1348 self->buffering = percent;
1352 g_mutex_lock (&self->lock);
1353 if (percent == 100 && (self->seek_position != GST_CLOCK_TIME_NONE ||
1354 self->seek_pending)) {
1355 g_mutex_unlock (&self->lock);
1357 GST_DEBUG_OBJECT (self, "Buffering finished - seek pending");
1358 } else if (percent == 100 && self->target_state >= GST_STATE_PLAYING
1359 && self->current_state >= GST_STATE_PAUSED) {
1360 GstStateChangeReturn state_ret;
1362 g_mutex_unlock (&self->lock);
1364 GST_DEBUG_OBJECT (self, "Buffering finished - going to PLAYING");
1365 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
1366 /* Application state change is happening when the state change happened */
1367 if (state_ret == GST_STATE_CHANGE_FAILURE)
1368 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1369 "Failed to handle buffering"));
1370 } else if (percent == 100 && self->target_state >= GST_STATE_PAUSED) {
1371 g_mutex_unlock (&self->lock);
1373 GST_DEBUG_OBJECT (self, "Buffering finished - staying PAUSED");
1374 change_state (self, GST_PLAYER_STATE_PAUSED);
1376 g_mutex_unlock (&self->lock);
1381 clock_lost_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1384 GstPlayer *self = GST_PLAYER (user_data);
1385 GstStateChangeReturn state_ret;
1387 GST_DEBUG_OBJECT (self, "Clock lost");
1388 if (self->target_state >= GST_STATE_PLAYING) {
1389 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
1390 if (state_ret != GST_STATE_CHANGE_FAILURE)
1391 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
1393 if (state_ret == GST_STATE_CHANGE_FAILURE)
1394 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1395 "Failed to handle clock loss"));
1403 } VideoDimensionsChangedSignalData;
1406 video_dimensions_changed_dispatch (gpointer user_data)
1408 VideoDimensionsChangedSignalData *data = user_data;
1410 if (data->player->inhibit_sigs)
1413 if (data->player->target_state >= GST_STATE_PAUSED) {
1414 g_signal_emit (data->player, signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED], 0,
1415 data->width, data->height);
1420 video_dimensions_changed_signal_data_free (VideoDimensionsChangedSignalData *
1423 g_object_unref (data->player);
1428 check_video_dimensions_changed (GstPlayer * self)
1430 GstElement *video_sink;
1431 GstPad *video_sink_pad;
1434 gint width = 0, height = 0;
1436 g_object_get (self->playbin, "video-sink", &video_sink, NULL);
1440 video_sink_pad = gst_element_get_static_pad (video_sink, "sink");
1441 if (!video_sink_pad) {
1442 gst_object_unref (video_sink);
1446 caps = gst_pad_get_current_caps (video_sink_pad);
1449 if (gst_video_info_from_caps (&info, caps)) {
1450 info.width = info.width * info.par_n / info.par_d;
1452 GST_DEBUG_OBJECT (self, "Video dimensions changed: %dx%d", info.width,
1455 height = info.height;
1458 gst_caps_unref (caps);
1460 gst_object_unref (video_sink_pad);
1461 gst_object_unref (video_sink);
1464 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1465 signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED], 0, NULL, NULL, NULL) != 0) {
1466 VideoDimensionsChangedSignalData *data =
1467 g_new (VideoDimensionsChangedSignalData, 1);
1469 data->player = g_object_ref (self);
1470 data->width = width;
1471 data->height = height;
1472 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1473 video_dimensions_changed_dispatch, data,
1474 (GDestroyNotify) video_dimensions_changed_signal_data_free);
1479 notify_caps_cb (G_GNUC_UNUSED GObject * object,
1480 G_GNUC_UNUSED GParamSpec * pspec, gpointer user_data)
1482 GstPlayer *self = GST_PLAYER (user_data);
1484 check_video_dimensions_changed (self);
1490 GstClockTime duration;
1491 } DurationChangedSignalData;
1494 duration_changed_dispatch (gpointer user_data)
1496 DurationChangedSignalData *data = user_data;
1498 if (data->player->inhibit_sigs)
1501 if (data->player->target_state >= GST_STATE_PAUSED) {
1502 g_signal_emit (data->player, signals[SIGNAL_DURATION_CHANGED], 0,
1504 g_object_notify_by_pspec (G_OBJECT (data->player),
1505 param_specs[PROP_DURATION]);
1510 duration_changed_signal_data_free (DurationChangedSignalData * data)
1512 g_object_unref (data->player);
1517 emit_duration_changed (GstPlayer * self, GstClockTime duration)
1519 gboolean updated = FALSE;
1521 if (self->cached_duration == duration)
1524 GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT,
1525 GST_TIME_ARGS (duration));
1527 self->cached_duration = duration;
1528 g_mutex_lock (&self->lock);
1529 if (self->media_info) {
1530 self->media_info->duration = duration;
1533 g_mutex_unlock (&self->lock);
1535 emit_media_info_updated_signal (self);
1538 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1539 signals[SIGNAL_DURATION_CHANGED], 0, NULL, NULL, NULL) != 0) {
1540 DurationChangedSignalData *data = g_new (DurationChangedSignalData, 1);
1542 data->player = g_object_ref (self);
1543 data->duration = duration;
1544 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1545 duration_changed_dispatch, data,
1546 (GDestroyNotify) duration_changed_signal_data_free);
1553 GstClockTime position;
1554 } SeekDoneSignalData;
1557 seek_done_dispatch (gpointer user_data)
1559 SeekDoneSignalData *data = user_data;
1561 if (data->player->inhibit_sigs)
1564 g_signal_emit (data->player, signals[SIGNAL_SEEK_DONE], 0, data->position);
1568 seek_done_signal_data_free (SeekDoneSignalData * data)
1570 g_object_unref (data->player);
1575 emit_seek_done (GstPlayer * self)
1577 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1578 signals[SIGNAL_SEEK_DONE], 0, NULL, NULL, NULL) != 0) {
1579 SeekDoneSignalData *data = g_new (SeekDoneSignalData, 1);
1581 data->player = g_object_ref (self);
1582 data->position = gst_player_get_position (self);
1583 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1584 seek_done_dispatch, data, (GDestroyNotify) seek_done_signal_data_free);
1589 state_changed_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1592 GstPlayer *self = GST_PLAYER (user_data);
1593 GstState old_state, new_state, pending_state;
1595 gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
1597 if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->playbin)) {
1598 gchar *transition_name;
1600 GST_DEBUG_OBJECT (self, "Changed state old: %s new: %s pending: %s",
1601 gst_element_state_get_name (old_state),
1602 gst_element_state_get_name (new_state),
1603 gst_element_state_get_name (pending_state));
1605 transition_name = g_strdup_printf ("%s_%s",
1606 gst_element_state_get_name (old_state),
1607 gst_element_state_get_name (new_state));
1608 dump_dot_file (self, transition_name);
1609 g_free (transition_name);
1611 self->current_state = new_state;
1613 if (old_state == GST_STATE_READY && new_state == GST_STATE_PAUSED
1614 && pending_state == GST_STATE_VOID_PENDING) {
1615 GstElement *video_sink;
1616 GstPad *video_sink_pad;
1617 gint64 duration = -1;
1619 GST_DEBUG_OBJECT (self, "Initial PAUSED - pre-rolled");
1621 g_mutex_lock (&self->lock);
1622 if (self->media_info)
1623 g_object_unref (self->media_info);
1624 self->media_info = gst_player_media_info_create (self);
1625 g_mutex_unlock (&self->lock);
1626 emit_media_info_updated_signal (self);
1628 g_object_get (self->playbin, "video-sink", &video_sink, NULL);
1631 video_sink_pad = gst_element_get_static_pad (video_sink, "sink");
1633 if (video_sink_pad) {
1634 g_signal_connect (video_sink_pad, "notify::caps",
1635 (GCallback) notify_caps_cb, self);
1636 gst_object_unref (video_sink_pad);
1638 gst_object_unref (video_sink);
1641 check_video_dimensions_changed (self);
1642 if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME,
1644 emit_duration_changed (self, duration);
1646 self->cached_duration = GST_CLOCK_TIME_NONE;
1650 if (new_state == GST_STATE_PAUSED
1651 && pending_state == GST_STATE_VOID_PENDING) {
1652 remove_tick_source (self);
1654 g_mutex_lock (&self->lock);
1655 if (self->seek_pending) {
1656 self->seek_pending = FALSE;
1658 if (!self->media_info->seekable) {
1659 GST_DEBUG_OBJECT (self, "Media is not seekable");
1660 remove_seek_source (self);
1661 self->seek_position = GST_CLOCK_TIME_NONE;
1662 self->last_seek_time = GST_CLOCK_TIME_NONE;
1663 } else if (self->seek_source) {
1664 GST_DEBUG_OBJECT (self, "Seek finished but new seek is pending");
1665 gst_player_seek_internal_locked (self);
1667 GST_DEBUG_OBJECT (self, "Seek finished");
1668 emit_seek_done (self);
1672 if (self->seek_position != GST_CLOCK_TIME_NONE) {
1673 GST_DEBUG_OBJECT (self, "Seeking now that we reached PAUSED state");
1674 gst_player_seek_internal_locked (self);
1675 g_mutex_unlock (&self->lock);
1676 } else if (!self->seek_pending) {
1677 g_mutex_unlock (&self->lock);
1681 if (self->target_state >= GST_STATE_PLAYING && self->buffering == 100) {
1682 GstStateChangeReturn state_ret;
1684 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
1685 if (state_ret == GST_STATE_CHANGE_FAILURE)
1686 emit_error (self, g_error_new (GST_PLAYER_ERROR,
1687 GST_PLAYER_ERROR_FAILED, "Failed to play"));
1688 } else if (self->buffering == 100) {
1689 change_state (self, GST_PLAYER_STATE_PAUSED);
1692 g_mutex_unlock (&self->lock);
1694 } else if (new_state == GST_STATE_PLAYING
1695 && pending_state == GST_STATE_VOID_PENDING) {
1697 /* If no seek is currently pending, add the tick source. This can happen
1698 * if we seeked already but the state-change message was still queued up */
1699 if (!self->seek_pending) {
1700 add_tick_source (self);
1701 change_state (self, GST_PLAYER_STATE_PLAYING);
1703 } else if (new_state == GST_STATE_READY && old_state > GST_STATE_READY) {
1704 change_state (self, GST_PLAYER_STATE_STOPPED);
1706 /* Otherwise we neither reached PLAYING nor PAUSED, so must
1707 * wait for something to happen... i.e. are BUFFERING now */
1708 change_state (self, GST_PLAYER_STATE_BUFFERING);
1714 duration_changed_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1717 GstPlayer *self = GST_PLAYER (user_data);
1718 gint64 duration = GST_CLOCK_TIME_NONE;
1720 if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME, &duration)) {
1721 emit_duration_changed (self, duration);
1726 latency_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1729 GstPlayer *self = GST_PLAYER (user_data);
1731 GST_DEBUG_OBJECT (self, "Latency changed");
1733 gst_bin_recalculate_latency (GST_BIN (self->playbin));
1737 request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1740 GstPlayer *self = GST_PLAYER (user_data);
1742 GstStateChangeReturn state_ret;
1744 gst_message_parse_request_state (msg, &state);
1746 GST_DEBUG_OBJECT (self, "State %s requested",
1747 gst_element_state_get_name (state));
1749 self->target_state = state;
1750 state_ret = gst_element_set_state (self->playbin, state);
1751 if (state_ret == GST_STATE_CHANGE_FAILURE)
1752 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1753 "Failed to change to requested state %s",
1754 gst_element_state_get_name (state)));
1758 media_info_update (GstPlayer * self, GstPlayerMediaInfo * info)
1760 g_free (info->title);
1761 info->title = get_from_tags (self, info, get_title);
1763 g_free (info->container);
1764 info->container = get_from_tags (self, info, get_container_format);
1766 if (info->image_sample)
1767 gst_sample_unref (info->image_sample);
1768 info->image_sample = get_from_tags (self, info, get_cover_sample);
1770 GST_DEBUG_OBJECT (self, "title: %s, container: %s "
1771 "image_sample: %p", info->title, info->container, info->image_sample);
1775 tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1777 GstPlayer *self = GST_PLAYER (user_data);
1778 GstTagList *tags = NULL;
1780 gst_message_parse_tag (msg, &tags);
1782 GST_DEBUG_OBJECT (self, "received %s tags",
1783 gst_tag_list_get_scope (tags) ==
1784 GST_TAG_SCOPE_GLOBAL ? "global" : "stream");
1786 if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_GLOBAL) {
1787 g_mutex_lock (&self->lock);
1788 if (self->media_info) {
1789 if (self->media_info->tags)
1790 gst_tag_list_unref (self->media_info->tags);
1791 self->media_info->tags = gst_tag_list_ref (tags);
1792 media_info_update (self, self->media_info);
1793 g_mutex_unlock (&self->lock);
1794 emit_media_info_updated_signal (self);
1796 if (self->global_tags)
1797 gst_tag_list_unref (self->global_tags);
1798 self->global_tags = gst_tag_list_ref (tags);
1799 g_mutex_unlock (&self->lock);
1803 gst_tag_list_unref (tags);
1807 element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1809 GstPlayer *self = GST_PLAYER (user_data);
1810 const GstStructure *s;
1812 s = gst_message_get_structure (msg);
1813 if (gst_structure_has_name (s, "redirect")) {
1814 const gchar *new_location;
1816 new_location = gst_structure_get_string (s, "new-location");
1817 if (!new_location) {
1818 const GValue *locations_list, *location_val;
1821 locations_list = gst_structure_get_value (s, "locations");
1822 size = gst_value_list_get_size (locations_list);
1823 for (i = 0; i < size; ++i) {
1824 const GstStructure *location_s;
1826 location_val = gst_value_list_get_value (locations_list, i);
1827 if (!GST_VALUE_HOLDS_STRUCTURE (location_val))
1830 location_s = (const GstStructure *) g_value_get_boxed (location_val);
1831 if (!gst_structure_has_name (location_s, "redirect"))
1834 new_location = gst_structure_get_string (location_s, "new-location");
1841 GstState target_state;
1843 GST_DEBUG_OBJECT (self, "Redirect to '%s'", new_location);
1845 /* Remember target state and restore after setting the URI */
1846 target_state = self->target_state;
1848 gst_player_stop_internal (self, TRUE);
1850 g_mutex_lock (&self->lock);
1851 g_free (self->redirect_uri);
1852 self->redirect_uri = g_strdup (new_location);
1853 g_object_set (self->playbin, "uri", self->redirect_uri, NULL);
1854 g_mutex_unlock (&self->lock);
1856 if (target_state == GST_STATE_PAUSED)
1857 gst_player_pause_internal (self);
1858 else if (target_state == GST_STATE_PLAYING)
1859 gst_player_play_internal (self);
1864 /* Must be called with lock */
1866 update_stream_collection (GstPlayer * self, GstStreamCollection * collection)
1868 if (self->collection && self->collection == collection)
1871 if (self->collection && self->stream_notify_id)
1872 g_signal_handler_disconnect (self->collection, self->stream_notify_id);
1874 gst_object_replace ((GstObject **) & self->collection,
1875 (GstObject *) collection);
1876 if (self->media_info) {
1877 gst_object_unref (self->media_info);
1878 self->media_info = gst_player_media_info_create (self);
1881 self->stream_notify_id =
1882 g_signal_connect (self->collection, "stream-notify",
1883 G_CALLBACK (stream_notify_cb), self);
1889 stream_collection_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1892 GstPlayer *self = GST_PLAYER (user_data);
1893 GstStreamCollection *collection = NULL;
1894 gboolean updated = FALSE;
1896 gst_message_parse_stream_collection (msg, &collection);
1901 g_mutex_lock (&self->lock);
1902 updated = update_stream_collection (self, collection);
1903 gst_object_unref (collection);
1904 g_mutex_unlock (&self->lock);
1906 if (self->media_info && updated)
1907 emit_media_info_updated_signal (self);
1911 streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1914 GstPlayer *self = GST_PLAYER (user_data);
1915 GstStreamCollection *collection = NULL;
1916 gboolean updated = FALSE;
1919 gst_message_parse_streams_selected (msg, &collection);
1924 g_mutex_lock (&self->lock);
1925 updated = update_stream_collection (self, collection);
1926 gst_object_unref (collection);
1928 g_free (self->video_sid);
1929 g_free (self->audio_sid);
1930 g_free (self->subtitle_sid);
1931 self->video_sid = NULL;
1932 self->audio_sid = NULL;
1933 self->subtitle_sid = NULL;
1935 len = gst_message_streams_selected_get_size (msg);
1936 for (i = 0; i < len; i++) {
1938 GstStreamType stream_type;
1939 const gchar *stream_id;
1940 gchar **current_sid;
1941 stream = gst_message_streams_selected_get_stream (msg, i);
1942 stream_type = gst_stream_get_stream_type (stream);
1943 stream_id = gst_stream_get_stream_id (stream);
1944 if (stream_type & GST_STREAM_TYPE_AUDIO)
1945 current_sid = &self->audio_sid;
1946 else if (stream_type & GST_STREAM_TYPE_VIDEO)
1947 current_sid = &self->video_sid;
1948 else if (stream_type & GST_STREAM_TYPE_TEXT)
1949 current_sid = &self->subtitle_sid;
1951 GST_WARNING_OBJECT (self,
1952 "Unknown stream-id %s with type 0x%x", stream_id, stream_type);
1956 if (G_UNLIKELY (*current_sid)) {
1957 GST_FIXME_OBJECT (self,
1958 "Multiple streams are selected for type %s, choose the first one",
1959 gst_stream_type_get_name (stream_type));
1963 *current_sid = g_strdup (stream_id);
1965 g_mutex_unlock (&self->lock);
1967 if (self->media_info && updated)
1968 emit_media_info_updated_signal (self);
1972 player_set_flag (GstPlayer * self, gint pos)
1976 g_object_get (self->playbin, "flags", &flags, NULL);
1978 g_object_set (self->playbin, "flags", flags, NULL);
1980 GST_DEBUG_OBJECT (self, "setting flags=%#x", flags);
1984 player_clear_flag (GstPlayer * self, gint pos)
1988 g_object_get (self->playbin, "flags", &flags, NULL);
1990 g_object_set (self->playbin, "flags", flags, NULL);
1992 GST_DEBUG_OBJECT (self, "setting flags=%#x", flags);
1998 GstPlayerMediaInfo *info;
1999 } MediaInfoUpdatedSignalData;
2002 media_info_updated_dispatch (gpointer user_data)
2004 MediaInfoUpdatedSignalData *data = user_data;
2006 if (data->player->inhibit_sigs)
2009 if (data->player->target_state >= GST_STATE_PAUSED) {
2010 g_signal_emit (data->player, signals[SIGNAL_MEDIA_INFO_UPDATED], 0,
2016 free_media_info_updated_signal_data (MediaInfoUpdatedSignalData * data)
2018 g_object_unref (data->player);
2019 g_object_unref (data->info);
2024 * emit_media_info_updated_signal:
2026 * create a new copy of self->media_info object and emits the newly created
2027 * copy to user application. The newly created media_info will be unref'ed
2028 * as part of signal finalize method.
2031 emit_media_info_updated_signal (GstPlayer * self)
2033 MediaInfoUpdatedSignalData *data = g_new (MediaInfoUpdatedSignalData, 1);
2034 data->player = g_object_ref (self);
2035 g_mutex_lock (&self->lock);
2036 data->info = gst_player_media_info_copy (self->media_info);
2037 g_mutex_unlock (&self->lock);
2039 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
2040 media_info_updated_dispatch, data,
2041 (GDestroyNotify) free_media_info_updated_signal_data);
2045 get_caps (GstPlayer * self, gint stream_index, GType type)
2048 GstCaps *caps = NULL;
2050 if (type == GST_TYPE_PLAYER_VIDEO_INFO)
2051 g_signal_emit_by_name (G_OBJECT (self->playbin),
2052 "get-video-pad", stream_index, &pad);
2053 else if (type == GST_TYPE_PLAYER_AUDIO_INFO)
2054 g_signal_emit_by_name (G_OBJECT (self->playbin),
2055 "get-audio-pad", stream_index, &pad);
2057 g_signal_emit_by_name (G_OBJECT (self->playbin),
2058 "get-text-pad", stream_index, &pad);
2061 caps = gst_pad_get_current_caps (pad);
2062 gst_object_unref (pad);
2069 gst_player_subtitle_info_update (GstPlayer * self,
2070 GstPlayerStreamInfo * stream_info)
2072 GstPlayerSubtitleInfo *info = (GstPlayerSubtitleInfo *) stream_info;
2074 if (stream_info->tags) {
2076 /* free the old language info */
2077 g_free (info->language);
2078 info->language = NULL;
2080 /* First try to get the language full name from tag, if name is not
2081 * available then try language code. If we find the language code
2082 * then use gstreamer api to translate code to full name.
2084 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME,
2086 if (!info->language) {
2087 gchar *lang_code = NULL;
2089 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE,
2092 info->language = g_strdup (gst_tag_get_language_name (lang_code));
2097 /* If we are still failed to find language name then check if external
2098 * subtitle is loaded and compare the stream index between current sub
2099 * stream index with our stream index and if matches then declare it as
2100 * external subtitle and use the filename.
2102 if (!info->language) {
2103 gint text_index = -1;
2104 gchar *suburi = NULL;
2106 g_object_get (G_OBJECT (self->playbin), "current-suburi", &suburi, NULL);
2108 if (self->use_playbin3) {
2109 if (g_str_equal (self->subtitle_sid, stream_info->stream_id))
2110 info->language = g_path_get_basename (suburi);
2112 g_object_get (G_OBJECT (self->playbin), "current-text", &text_index,
2114 if (text_index == gst_player_stream_info_get_index (stream_info))
2115 info->language = g_path_get_basename (suburi);
2122 g_free (info->language);
2123 info->language = NULL;
2126 GST_DEBUG_OBJECT (self, "language=%s", info->language);
2130 gst_player_video_info_update (GstPlayer * self,
2131 GstPlayerStreamInfo * stream_info)
2133 GstPlayerVideoInfo *info = (GstPlayerVideoInfo *) stream_info;
2135 if (stream_info->caps) {
2138 s = gst_caps_get_structure (stream_info->caps, 0);
2144 if (gst_structure_get_int (s, "width", &width))
2145 info->width = width;
2149 if (gst_structure_get_int (s, "height", &height))
2150 info->height = height;
2154 if (gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
2155 info->framerate_num = fps_n;
2156 info->framerate_denom = fps_d;
2158 info->framerate_num = 0;
2159 info->framerate_denom = 1;
2163 if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) {
2164 info->par_num = par_n;
2165 info->par_denom = par_d;
2168 info->par_denom = 1;
2172 info->width = info->height = -1;
2173 info->par_num = info->par_denom = 1;
2174 info->framerate_num = 0;
2175 info->framerate_denom = 1;
2178 if (stream_info->tags) {
2179 guint bitrate, max_bitrate;
2181 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate))
2182 info->bitrate = bitrate;
2186 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_MAXIMUM_BITRATE,
2187 &max_bitrate) || gst_tag_list_get_uint (stream_info->tags,
2188 GST_TAG_NOMINAL_BITRATE, &max_bitrate))
2189 info->max_bitrate = max_bitrate;
2191 info->max_bitrate = -1;
2193 info->bitrate = info->max_bitrate = -1;
2196 GST_DEBUG_OBJECT (self, "width=%d height=%d fps=%.2f par=%d:%d "
2197 "bitrate=%d max_bitrate=%d", info->width, info->height,
2198 (gdouble) info->framerate_num / info->framerate_denom,
2199 info->par_num, info->par_denom, info->bitrate, info->max_bitrate);
2203 gst_player_audio_info_update (GstPlayer * self,
2204 GstPlayerStreamInfo * stream_info)
2206 GstPlayerAudioInfo *info = (GstPlayerAudioInfo *) stream_info;
2208 if (stream_info->caps) {
2211 s = gst_caps_get_structure (stream_info->caps, 0);
2213 gint rate, channels;
2215 if (gst_structure_get_int (s, "rate", &rate))
2216 info->sample_rate = rate;
2218 info->sample_rate = -1;
2220 if (gst_structure_get_int (s, "channels", &channels))
2221 info->channels = channels;
2226 info->sample_rate = -1;
2230 if (stream_info->tags) {
2231 guint bitrate, max_bitrate;
2233 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate))
2234 info->bitrate = bitrate;
2238 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_MAXIMUM_BITRATE,
2239 &max_bitrate) || gst_tag_list_get_uint (stream_info->tags,
2240 GST_TAG_NOMINAL_BITRATE, &max_bitrate))
2241 info->max_bitrate = max_bitrate;
2243 info->max_bitrate = -1;
2245 /* if we have old language the free it */
2246 g_free (info->language);
2247 info->language = NULL;
2249 /* First try to get the language full name from tag, if name is not
2250 * available then try language code. If we find the language code
2251 * then use gstreamer api to translate code to full name.
2253 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME,
2255 if (!info->language) {
2256 gchar *lang_code = NULL;
2258 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE,
2261 info->language = g_strdup (gst_tag_get_language_name (lang_code));
2266 g_free (info->language);
2267 info->language = NULL;
2268 info->max_bitrate = info->bitrate = -1;
2271 GST_DEBUG_OBJECT (self, "language=%s rate=%d channels=%d bitrate=%d "
2272 "max_bitrate=%d", info->language, info->sample_rate, info->channels,
2273 info->bitrate, info->max_bitrate);
2276 static GstPlayerStreamInfo *
2277 gst_player_stream_info_find (GstPlayerMediaInfo * media_info,
2278 GType type, gint stream_index)
2281 GstPlayerStreamInfo *info = NULL;
2286 list = gst_player_media_info_get_stream_list (media_info);
2287 for (l = list; l != NULL; l = l->next) {
2288 info = (GstPlayerStreamInfo *) l->data;
2289 if ((G_OBJECT_TYPE (info) == type) && (info->stream_index == stream_index)) {
2297 static GstPlayerStreamInfo *
2298 gst_player_stream_info_find_from_stream_id (GstPlayerMediaInfo * media_info,
2299 const gchar * stream_id)
2302 GstPlayerStreamInfo *info = NULL;
2307 list = gst_player_media_info_get_stream_list (media_info);
2308 for (l = list; l != NULL; l = l->next) {
2309 info = (GstPlayerStreamInfo *) l->data;
2310 if (g_str_equal (info->stream_id, stream_id)) {
2319 is_track_enabled (GstPlayer * self, gint pos)
2323 g_object_get (G_OBJECT (self->playbin), "flags", &flags, NULL);
2331 static GstPlayerStreamInfo *
2332 gst_player_stream_info_get_current (GstPlayer * self, const gchar * prop,
2336 GstPlayerStreamInfo *info;
2338 if (!self->media_info)
2341 g_object_get (G_OBJECT (self->playbin), prop, ¤t, NULL);
2342 g_mutex_lock (&self->lock);
2343 info = gst_player_stream_info_find (self->media_info, type, current);
2345 info = gst_player_stream_info_copy (info);
2346 g_mutex_unlock (&self->lock);
2351 static GstPlayerStreamInfo *
2352 gst_player_stream_info_get_current_from_stream_id (GstPlayer * self,
2353 const gchar * stream_id, GType type)
2355 GstPlayerStreamInfo *info;
2357 if (!self->media_info || !stream_id)
2360 g_mutex_lock (&self->lock);
2362 gst_player_stream_info_find_from_stream_id (self->media_info, stream_id);
2363 if (info && G_OBJECT_TYPE (info) == type)
2364 info = gst_player_stream_info_copy (info);
2367 g_mutex_unlock (&self->lock);
2373 stream_notify_cb (GstStreamCollection * collection, GstStream * stream,
2374 GParamSpec * pspec, GstPlayer * self)
2376 GstPlayerStreamInfo *info;
2377 const gchar *stream_id;
2378 gboolean emit_signal = FALSE;
2380 if (!self->media_info)
2383 if (G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_CAPS &&
2384 G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_TAG_LIST)
2387 stream_id = gst_stream_get_stream_id (stream);
2388 g_mutex_lock (&self->lock);
2390 gst_player_stream_info_find_from_stream_id (self->media_info, stream_id);
2392 gst_player_stream_info_update_from_stream (self, info, stream);
2395 g_mutex_unlock (&self->lock);
2398 emit_media_info_updated_signal (self);
2402 gst_player_stream_info_update (GstPlayer * self, GstPlayerStreamInfo * s)
2404 if (GST_IS_PLAYER_VIDEO_INFO (s))
2405 gst_player_video_info_update (self, s);
2406 else if (GST_IS_PLAYER_AUDIO_INFO (s))
2407 gst_player_audio_info_update (self, s);
2409 gst_player_subtitle_info_update (self, s);
2413 stream_info_get_codec (GstPlayerStreamInfo * s)
2417 gchar *codec = NULL;
2419 if (GST_IS_PLAYER_VIDEO_INFO (s))
2420 type = GST_TAG_VIDEO_CODEC;
2421 else if (GST_IS_PLAYER_AUDIO_INFO (s))
2422 type = GST_TAG_AUDIO_CODEC;
2424 type = GST_TAG_SUBTITLE_CODEC;
2426 tags = gst_player_stream_info_get_tags (s);
2428 gst_tag_list_get_string (tags, type, &codec);
2430 gst_tag_list_get_string (tags, GST_TAG_CODEC, &codec);
2435 caps = gst_player_stream_info_get_caps (s);
2437 codec = gst_pb_utils_get_codec_description (caps);
2445 gst_player_stream_info_update_tags_and_caps (GstPlayer * self,
2446 GstPlayerStreamInfo * s)
2451 stream_index = gst_player_stream_info_get_index (s);
2453 if (GST_IS_PLAYER_VIDEO_INFO (s))
2454 g_signal_emit_by_name (self->playbin, "get-video-tags",
2455 stream_index, &tags);
2456 else if (GST_IS_PLAYER_AUDIO_INFO (s))
2457 g_signal_emit_by_name (self->playbin, "get-audio-tags",
2458 stream_index, &tags);
2460 g_signal_emit_by_name (self->playbin, "get-text-tags", stream_index, &tags);
2463 gst_tag_list_unref (s->tags);
2467 gst_caps_unref (s->caps);
2468 s->caps = get_caps (self, stream_index, G_OBJECT_TYPE (s));
2471 s->codec = stream_info_get_codec (s);
2473 GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p",
2474 gst_player_stream_info_get_stream_type (s), stream_index,
2477 gst_player_stream_info_update (self, s);
2481 gst_player_streams_info_create (GstPlayer * self,
2482 GstPlayerMediaInfo * media_info, const gchar * prop, GType type)
2486 GstPlayerStreamInfo *s;
2491 g_object_get (G_OBJECT (self->playbin), prop, &total, NULL);
2493 GST_DEBUG_OBJECT (self, "%s: %d", prop, total);
2495 for (i = 0; i < total; i++) {
2496 /* check if stream already exist in the list */
2497 s = gst_player_stream_info_find (media_info, type, i);
2500 /* create a new stream info instance */
2501 s = gst_player_stream_info_new (i, type);
2503 /* add the object in stream list */
2504 media_info->stream_list = g_list_append (media_info->stream_list, s);
2506 /* based on type, add the object in its corresponding stream_ list */
2507 if (GST_IS_PLAYER_AUDIO_INFO (s))
2508 media_info->audio_stream_list = g_list_append
2509 (media_info->audio_stream_list, s);
2510 else if (GST_IS_PLAYER_VIDEO_INFO (s))
2511 media_info->video_stream_list = g_list_append
2512 (media_info->video_stream_list, s);
2514 media_info->subtitle_stream_list = g_list_append
2515 (media_info->subtitle_stream_list, s);
2517 GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d",
2518 gst_player_stream_info_get_stream_type (s), i);
2521 gst_player_stream_info_update_tags_and_caps (self, s);
2526 gst_player_stream_info_update_from_stream (GstPlayer * self,
2527 GstPlayerStreamInfo * s, GstStream * stream)
2530 gst_tag_list_unref (s->tags);
2531 s->tags = gst_stream_get_tags (stream);
2534 gst_caps_unref (s->caps);
2535 s->caps = gst_stream_get_caps (stream);
2538 s->codec = stream_info_get_codec (s);
2540 GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p",
2541 gst_player_stream_info_get_stream_type (s), s->stream_index,
2544 gst_player_stream_info_update (self, s);
2548 gst_player_streams_info_create_from_collection (GstPlayer * self,
2549 GstPlayerMediaInfo * media_info, GstStreamCollection * collection)
2553 GstPlayerStreamInfo *s;
2558 if (!media_info || !collection)
2561 total = gst_stream_collection_get_size (collection);
2563 for (i = 0; i < total; i++) {
2564 GstStream *stream = gst_stream_collection_get_stream (collection, i);
2565 GstStreamType stream_type = gst_stream_get_stream_type (stream);
2566 const gchar *stream_id = gst_stream_get_stream_id (stream);
2568 if (stream_type & GST_STREAM_TYPE_AUDIO) {
2569 s = gst_player_stream_info_new (n_audio, GST_TYPE_PLAYER_AUDIO_INFO);
2571 } else if (stream_type & GST_STREAM_TYPE_VIDEO) {
2572 s = gst_player_stream_info_new (n_video, GST_TYPE_PLAYER_VIDEO_INFO);
2574 } else if (stream_type & GST_STREAM_TYPE_TEXT) {
2575 s = gst_player_stream_info_new (n_text, GST_TYPE_PLAYER_SUBTITLE_INFO);
2578 GST_DEBUG_OBJECT (self, "Unknown type stream %d", i);
2582 s->stream_id = g_strdup (stream_id);
2584 /* add the object in stream list */
2585 media_info->stream_list = g_list_append (media_info->stream_list, s);
2587 /* based on type, add the object in its corresponding stream_ list */
2588 if (GST_IS_PLAYER_AUDIO_INFO (s))
2589 media_info->audio_stream_list = g_list_append
2590 (media_info->audio_stream_list, s);
2591 else if (GST_IS_PLAYER_VIDEO_INFO (s))
2592 media_info->video_stream_list = g_list_append
2593 (media_info->video_stream_list, s);
2595 media_info->subtitle_stream_list = g_list_append
2596 (media_info->subtitle_stream_list, s);
2598 GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d",
2599 gst_player_stream_info_get_stream_type (s), s->stream_index);
2601 gst_player_stream_info_update_from_stream (self, s, stream);
2606 video_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2608 GstPlayer *self = GST_PLAYER (user_data);
2610 g_mutex_lock (&self->lock);
2611 gst_player_streams_info_create (self, self->media_info,
2612 "n-video", GST_TYPE_PLAYER_VIDEO_INFO);
2613 g_mutex_unlock (&self->lock);
2617 audio_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2619 GstPlayer *self = GST_PLAYER (user_data);
2621 g_mutex_lock (&self->lock);
2622 gst_player_streams_info_create (self, self->media_info,
2623 "n-audio", GST_TYPE_PLAYER_AUDIO_INFO);
2624 g_mutex_unlock (&self->lock);
2628 subtitle_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2630 GstPlayer *self = GST_PLAYER (user_data);
2632 g_mutex_lock (&self->lock);
2633 gst_player_streams_info_create (self, self->media_info,
2634 "n-text", GST_TYPE_PLAYER_SUBTITLE_INFO);
2635 g_mutex_unlock (&self->lock);
2639 get_title (GstTagList * tags)
2641 gchar *title = NULL;
2643 gst_tag_list_get_string (tags, GST_TAG_TITLE, &title);
2645 gst_tag_list_get_string (tags, GST_TAG_TITLE_SORTNAME, &title);
2651 get_container_format (GstTagList * tags)
2653 gchar *container = NULL;
2655 gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container);
2657 /* TODO: If container is not available then maybe consider
2658 * parsing caps or file extension to guess the container format.
2665 get_from_tags (GstPlayer * self, GstPlayerMediaInfo * media_info,
2666 void *(*func) (GstTagList *))
2671 if (media_info->tags) {
2672 ret = func (media_info->tags);
2677 /* if global tag does not exit then try video and audio streams */
2678 GST_DEBUG_OBJECT (self, "trying video tags");
2679 for (l = gst_player_media_info_get_video_streams (media_info); l != NULL;
2683 tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data);
2691 GST_DEBUG_OBJECT (self, "trying audio tags");
2692 for (l = gst_player_media_info_get_audio_streams (media_info); l != NULL;
2696 tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data);
2704 GST_DEBUG_OBJECT (self, "failed to get the information from tags");
2709 get_cover_sample (GstTagList * tags)
2711 GstSample *cover_sample = NULL;
2713 gst_tag_list_get_sample (tags, GST_TAG_IMAGE, &cover_sample);
2715 gst_tag_list_get_sample (tags, GST_TAG_PREVIEW_IMAGE, &cover_sample);
2717 return cover_sample;
2720 static GstPlayerMediaInfo *
2721 gst_player_media_info_create (GstPlayer * self)
2723 GstPlayerMediaInfo *media_info;
2726 GST_DEBUG_OBJECT (self, "begin");
2727 media_info = gst_player_media_info_new (self->uri);
2728 media_info->duration = gst_player_get_duration (self);
2729 media_info->tags = self->global_tags;
2730 media_info->is_live = self->is_live;
2731 self->global_tags = NULL;
2733 query = gst_query_new_seeking (GST_FORMAT_TIME);
2734 if (gst_element_query (self->playbin, query))
2735 gst_query_parse_seeking (query, NULL, &media_info->seekable, NULL, NULL);
2736 gst_query_unref (query);
2738 if (self->use_playbin3 && self->collection) {
2739 gst_player_streams_info_create_from_collection (self, media_info,
2742 /* create audio/video/sub streams */
2743 gst_player_streams_info_create (self, media_info, "n-video",
2744 GST_TYPE_PLAYER_VIDEO_INFO);
2745 gst_player_streams_info_create (self, media_info, "n-audio",
2746 GST_TYPE_PLAYER_AUDIO_INFO);
2747 gst_player_streams_info_create (self, media_info, "n-text",
2748 GST_TYPE_PLAYER_SUBTITLE_INFO);
2751 media_info->title = get_from_tags (self, media_info, get_title);
2752 media_info->container =
2753 get_from_tags (self, media_info, get_container_format);
2754 media_info->image_sample = get_from_tags (self, media_info, get_cover_sample);
2756 GST_DEBUG_OBJECT (self, "uri: %s title: %s duration: %" GST_TIME_FORMAT
2757 " seekable: %s live: %s container: %s image_sample %p",
2758 media_info->uri, media_info->title, GST_TIME_ARGS (media_info->duration),
2759 media_info->seekable ? "yes" : "no", media_info->is_live ? "yes" : "no",
2760 media_info->container, media_info->image_sample);
2762 GST_DEBUG_OBJECT (self, "end");
2767 tags_changed_cb (GstPlayer * self, gint stream_index, GType type)
2769 GstPlayerStreamInfo *s;
2771 if (!self->media_info)
2774 /* update the stream information */
2775 g_mutex_lock (&self->lock);
2776 s = gst_player_stream_info_find (self->media_info, type, stream_index);
2777 gst_player_stream_info_update_tags_and_caps (self, s);
2778 g_mutex_unlock (&self->lock);
2780 emit_media_info_updated_signal (self);
2784 video_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2787 tags_changed_cb (GST_PLAYER (user_data), stream_index,
2788 GST_TYPE_PLAYER_VIDEO_INFO);
2792 audio_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2795 tags_changed_cb (GST_PLAYER (user_data), stream_index,
2796 GST_TYPE_PLAYER_AUDIO_INFO);
2800 subtitle_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2803 tags_changed_cb (GST_PLAYER (user_data), stream_index,
2804 GST_TYPE_PLAYER_SUBTITLE_INFO);
2808 volume_changed_dispatch (gpointer user_data)
2810 GstPlayer *player = user_data;
2812 if (player->inhibit_sigs)
2815 g_signal_emit (player, signals[SIGNAL_VOLUME_CHANGED], 0);
2816 g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_VOLUME]);
2820 volume_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
2823 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
2824 signals[SIGNAL_VOLUME_CHANGED], 0, NULL, NULL, NULL) != 0) {
2825 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
2826 volume_changed_dispatch, g_object_ref (self),
2827 (GDestroyNotify) g_object_unref);
2832 mute_changed_dispatch (gpointer user_data)
2834 GstPlayer *player = user_data;
2836 if (player->inhibit_sigs)
2839 g_signal_emit (player, signals[SIGNAL_MUTE_CHANGED], 0);
2840 g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_MUTE]);
2844 mute_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
2847 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
2848 signals[SIGNAL_MUTE_CHANGED], 0, NULL, NULL, NULL) != 0) {
2849 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
2850 mute_changed_dispatch, g_object_ref (self),
2851 (GDestroyNotify) g_object_unref);
2856 source_setup_cb (GstElement * playbin, GstElement * source, GstPlayer * self)
2860 user_agent = gst_player_config_get_user_agent (self->config);
2864 prop = g_object_class_find_property (G_OBJECT_GET_CLASS (source),
2866 if (prop && prop->value_type == G_TYPE_STRING) {
2867 GST_INFO_OBJECT (self, "Setting source user-agent: %s", user_agent);
2868 g_object_set (source, "user-agent", user_agent, NULL);
2871 g_free (user_agent);
2876 gst_player_main (gpointer data)
2878 GstPlayer *self = GST_PLAYER (data);
2881 GSource *bus_source;
2882 GstElement *scaletempo;
2885 GST_TRACE_OBJECT (self, "Starting main thread");
2887 g_main_context_push_thread_default (self->context);
2889 source = g_idle_source_new ();
2890 g_source_set_callback (source, (GSourceFunc) main_loop_running_cb, self,
2892 g_source_attach (source, self->context);
2893 g_source_unref (source);
2895 env = g_getenv ("GST_PLAYER_USE_PLAYBIN3");
2896 if (env && g_str_has_prefix (env, "1"))
2897 self->use_playbin3 = TRUE;
2899 if (self->use_playbin3) {
2900 GST_DEBUG_OBJECT (self, "playbin3 enabled");
2901 self->playbin = gst_element_factory_make ("playbin3", "playbin3");
2903 self->playbin = gst_element_factory_make ("playbin", "playbin");
2906 if (!self->playbin) {
2907 g_error ("GstPlayer: 'playbin' element not found, please check your setup");
2908 g_assert_not_reached ();
2911 if (self->video_renderer) {
2912 GstElement *video_sink =
2913 gst_player_video_renderer_create_video_sink (self->video_renderer,
2917 g_object_set (self->playbin, "video-sink", video_sink, NULL);
2920 scaletempo = gst_element_factory_make ("scaletempo", NULL);
2922 g_object_set (self->playbin, "audio-filter", scaletempo, NULL);
2924 g_warning ("GstPlayer: scaletempo element not available. Audio pitch "
2925 "will not be preserved during trick modes");
2928 self->bus = bus = gst_element_get_bus (self->playbin);
2929 bus_source = gst_bus_create_watch (bus);
2930 g_source_set_callback (bus_source, (GSourceFunc) gst_bus_async_signal_func,
2932 g_source_attach (bus_source, self->context);
2934 g_signal_connect (G_OBJECT (bus), "message::error", G_CALLBACK (error_cb),
2936 g_signal_connect (G_OBJECT (bus), "message::warning", G_CALLBACK (warning_cb),
2938 g_signal_connect (G_OBJECT (bus), "message::eos", G_CALLBACK (eos_cb), self);
2939 g_signal_connect (G_OBJECT (bus), "message::state-changed",
2940 G_CALLBACK (state_changed_cb), self);
2941 g_signal_connect (G_OBJECT (bus), "message::buffering",
2942 G_CALLBACK (buffering_cb), self);
2943 g_signal_connect (G_OBJECT (bus), "message::clock-lost",
2944 G_CALLBACK (clock_lost_cb), self);
2945 g_signal_connect (G_OBJECT (bus), "message::duration-changed",
2946 G_CALLBACK (duration_changed_cb), self);
2947 g_signal_connect (G_OBJECT (bus), "message::latency",
2948 G_CALLBACK (latency_cb), self);
2949 g_signal_connect (G_OBJECT (bus), "message::request-state",
2950 G_CALLBACK (request_state_cb), self);
2951 g_signal_connect (G_OBJECT (bus), "message::element",
2952 G_CALLBACK (element_cb), self);
2953 g_signal_connect (G_OBJECT (bus), "message::tag", G_CALLBACK (tags_cb), self);
2955 if (self->use_playbin3) {
2956 g_signal_connect (G_OBJECT (bus), "message::stream-collection",
2957 G_CALLBACK (stream_collection_cb), self);
2958 g_signal_connect (G_OBJECT (bus), "message::streams-selected",
2959 G_CALLBACK (streams_selected_cb), self);
2961 g_signal_connect (self->playbin, "video-changed",
2962 G_CALLBACK (video_changed_cb), self);
2963 g_signal_connect (self->playbin, "audio-changed",
2964 G_CALLBACK (audio_changed_cb), self);
2965 g_signal_connect (self->playbin, "text-changed",
2966 G_CALLBACK (subtitle_changed_cb), self);
2968 g_signal_connect (self->playbin, "video-tags-changed",
2969 G_CALLBACK (video_tags_changed_cb), self);
2970 g_signal_connect (self->playbin, "audio-tags-changed",
2971 G_CALLBACK (audio_tags_changed_cb), self);
2972 g_signal_connect (self->playbin, "text-tags-changed",
2973 G_CALLBACK (subtitle_tags_changed_cb), self);
2976 g_signal_connect (self->playbin, "notify::volume",
2977 G_CALLBACK (volume_notify_cb), self);
2978 g_signal_connect (self->playbin, "notify::mute",
2979 G_CALLBACK (mute_notify_cb), self);
2980 g_signal_connect (self->playbin, "source-setup",
2981 G_CALLBACK (source_setup_cb), self);
2983 self->target_state = GST_STATE_NULL;
2984 self->current_state = GST_STATE_NULL;
2985 change_state (self, GST_PLAYER_STATE_STOPPED);
2986 self->buffering = 100;
2987 self->is_eos = FALSE;
2988 self->is_live = FALSE;
2991 GST_TRACE_OBJECT (self, "Starting main loop");
2992 g_main_loop_run (self->loop);
2993 GST_TRACE_OBJECT (self, "Stopped main loop");
2995 g_source_destroy (bus_source);
2996 g_source_unref (bus_source);
2997 gst_object_unref (bus);
2999 remove_tick_source (self);
3000 remove_ready_timeout_source (self);
3002 g_mutex_lock (&self->lock);
3003 if (self->media_info) {
3004 g_object_unref (self->media_info);
3005 self->media_info = NULL;
3008 remove_seek_source (self);
3009 g_mutex_unlock (&self->lock);
3011 g_main_context_pop_thread_default (self->context);
3013 self->target_state = GST_STATE_NULL;
3014 self->current_state = GST_STATE_NULL;
3015 if (self->playbin) {
3016 gst_element_set_state (self->playbin, GST_STATE_NULL);
3017 gst_object_unref (self->playbin);
3018 self->playbin = NULL;
3021 GST_TRACE_OBJECT (self, "Stopped main thread");
3027 gst_player_init_once (G_GNUC_UNUSED gpointer user_data)
3029 gst_init (NULL, NULL);
3031 GST_DEBUG_CATEGORY_INIT (gst_player_debug, "gst-player", 0, "GstPlayer");
3032 gst_player_error_quark ();
3039 * @video_renderer: (transfer full) (allow-none): GstPlayerVideoRenderer to use
3040 * @signal_dispatcher: (transfer full) (allow-none): GstPlayerSignalDispatcher to use
3042 * Creates a new #GstPlayer instance that uses @signal_dispatcher to dispatch
3043 * signals to some event loop system, or emits signals directly if NULL is
3044 * passed. See gst_player_g_main_context_signal_dispatcher_new().
3046 * Video is going to be rendered by @video_renderer, or if %NULL is provided
3047 * no special video set up will be done and some default handling will be
3050 * Returns: (transfer full): a new #GstPlayer instance
3053 gst_player_new (GstPlayerVideoRenderer * video_renderer,
3054 GstPlayerSignalDispatcher * signal_dispatcher)
3056 static GOnce once = G_ONCE_INIT;
3059 g_once (&once, gst_player_init_once, NULL);
3062 g_object_new (GST_TYPE_PLAYER, "video-renderer", video_renderer,
3063 "signal-dispatcher", signal_dispatcher, NULL);
3064 gst_object_ref_sink (self);
3067 g_object_unref (video_renderer);
3068 if (signal_dispatcher)
3069 g_object_unref (signal_dispatcher);
3075 gst_player_play_internal (gpointer user_data)
3077 GstPlayer *self = GST_PLAYER (user_data);
3078 GstStateChangeReturn state_ret;
3080 GST_DEBUG_OBJECT (self, "Play");
3082 g_mutex_lock (&self->lock);
3084 g_mutex_unlock (&self->lock);
3085 return G_SOURCE_REMOVE;
3087 g_mutex_unlock (&self->lock);
3089 remove_ready_timeout_source (self);
3090 self->target_state = GST_STATE_PLAYING;
3092 if (self->current_state < GST_STATE_PAUSED)
3093 change_state (self, GST_PLAYER_STATE_BUFFERING);
3095 if (self->current_state >= GST_STATE_PAUSED && !self->is_eos
3096 && self->buffering >= 100 && !(self->seek_position != GST_CLOCK_TIME_NONE
3097 || self->seek_pending)) {
3098 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
3100 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
3103 if (state_ret == GST_STATE_CHANGE_FAILURE) {
3104 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3106 return G_SOURCE_REMOVE;
3107 } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) {
3108 self->is_live = TRUE;
3109 GST_DEBUG_OBJECT (self, "Pipeline is live");
3115 GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning");
3116 self->is_eos = FALSE;
3118 gst_element_seek_simple (self->playbin, GST_FORMAT_TIME,
3119 GST_SEEK_FLAG_FLUSH, 0);
3121 GST_ERROR_OBJECT (self, "Seek to beginning failed");
3122 gst_player_stop_internal (self, TRUE);
3123 gst_player_play_internal (self);
3127 return G_SOURCE_REMOVE;
3132 * @player: #GstPlayer instance
3134 * Request to play the loaded stream.
3137 gst_player_play (GstPlayer * self)
3139 g_return_if_fail (GST_IS_PLAYER (self));
3141 g_mutex_lock (&self->lock);
3142 self->inhibit_sigs = FALSE;
3143 g_mutex_unlock (&self->lock);
3145 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3146 gst_player_play_internal, self, NULL);
3150 gst_player_pause_internal (gpointer user_data)
3152 GstPlayer *self = GST_PLAYER (user_data);
3153 GstStateChangeReturn state_ret;
3155 GST_DEBUG_OBJECT (self, "Pause");
3157 g_mutex_lock (&self->lock);
3159 g_mutex_unlock (&self->lock);
3160 return G_SOURCE_REMOVE;
3162 g_mutex_unlock (&self->lock);
3165 remove_tick_source (self);
3166 remove_ready_timeout_source (self);
3168 self->target_state = GST_STATE_PAUSED;
3170 if (self->current_state < GST_STATE_PAUSED)
3171 change_state (self, GST_PLAYER_STATE_BUFFERING);
3173 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
3174 if (state_ret == GST_STATE_CHANGE_FAILURE) {
3175 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3176 "Failed to pause"));
3177 return G_SOURCE_REMOVE;
3178 } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) {
3179 self->is_live = TRUE;
3180 GST_DEBUG_OBJECT (self, "Pipeline is live");
3186 GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning");
3187 self->is_eos = FALSE;
3189 gst_element_seek_simple (self->playbin, GST_FORMAT_TIME,
3190 GST_SEEK_FLAG_FLUSH, 0);
3192 GST_ERROR_OBJECT (self, "Seek to beginning failed");
3193 gst_player_stop_internal (self, TRUE);
3194 gst_player_pause_internal (self);
3198 return G_SOURCE_REMOVE;
3203 * @player: #GstPlayer instance
3205 * Pauses the current stream.
3208 gst_player_pause (GstPlayer * self)
3210 g_return_if_fail (GST_IS_PLAYER (self));
3212 g_mutex_lock (&self->lock);
3213 self->inhibit_sigs = FALSE;
3214 g_mutex_unlock (&self->lock);
3216 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3217 gst_player_pause_internal, self, NULL);
3221 gst_player_stop_internal (GstPlayer * self, gboolean transient)
3223 /* directly return if we're already stopped */
3224 if (self->current_state <= GST_STATE_READY &&
3225 self->target_state <= GST_STATE_READY)
3228 GST_DEBUG_OBJECT (self, "Stop (transient %d)", transient);
3231 remove_tick_source (self);
3233 add_ready_timeout_source (self);
3235 self->target_state = GST_STATE_NULL;
3236 self->current_state = GST_STATE_READY;
3237 self->is_live = FALSE;
3238 self->is_eos = FALSE;
3239 gst_bus_set_flushing (self->bus, TRUE);
3240 gst_element_set_state (self->playbin, GST_STATE_READY);
3241 gst_bus_set_flushing (self->bus, FALSE);
3242 change_state (self, transient
3243 && self->app_state !=
3244 GST_PLAYER_STATE_STOPPED ? GST_PLAYER_STATE_BUFFERING :
3245 GST_PLAYER_STATE_STOPPED);
3246 self->buffering = 100;
3247 self->cached_duration = GST_CLOCK_TIME_NONE;
3248 g_mutex_lock (&self->lock);
3249 if (self->media_info) {
3250 g_object_unref (self->media_info);
3251 self->media_info = NULL;
3253 if (self->global_tags) {
3254 gst_tag_list_unref (self->global_tags);
3255 self->global_tags = NULL;
3257 self->seek_pending = FALSE;
3258 remove_seek_source (self);
3259 self->seek_position = GST_CLOCK_TIME_NONE;
3260 self->last_seek_time = GST_CLOCK_TIME_NONE;
3262 if (self->collection) {
3263 if (self->stream_notify_id)
3264 g_signal_handler_disconnect (self->collection, self->stream_notify_id);
3265 self->stream_notify_id = 0;
3266 gst_object_unref (self->collection);
3267 self->collection = NULL;
3269 g_free (self->video_sid);
3270 g_free (self->audio_sid);
3271 g_free (self->subtitle_sid);
3272 self->video_sid = NULL;
3273 self->audio_sid = NULL;
3274 self->subtitle_sid = NULL;
3275 g_mutex_unlock (&self->lock);
3279 gst_player_stop_internal_dispatch (gpointer user_data)
3281 GstPlayer *self = GST_PLAYER (user_data);
3283 gst_player_stop_internal (self, FALSE);
3285 return G_SOURCE_REMOVE;
3291 * @player: #GstPlayer instance
3293 * Stops playing the current stream and resets to the first position
3297 gst_player_stop (GstPlayer * self)
3299 g_return_if_fail (GST_IS_PLAYER (self));
3301 g_mutex_lock (&self->lock);
3302 self->inhibit_sigs = TRUE;
3303 g_mutex_unlock (&self->lock);
3305 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3306 gst_player_stop_internal_dispatch, self, NULL);
3309 /* Must be called with lock from main context, releases lock! */
3311 gst_player_seek_internal_locked (GstPlayer * self)
3314 GstClockTime position;
3316 GstStateChangeReturn state_ret;
3318 GstSeekFlags flags = 0;
3319 gboolean accurate = FALSE;
3321 remove_seek_source (self);
3323 /* Only seek in PAUSED */
3324 if (self->current_state < GST_STATE_PAUSED) {
3326 } else if (self->current_state != GST_STATE_PAUSED) {
3327 g_mutex_unlock (&self->lock);
3328 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
3329 if (state_ret == GST_STATE_CHANGE_FAILURE) {
3330 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3332 g_mutex_lock (&self->lock);
3335 g_mutex_lock (&self->lock);
3339 self->last_seek_time = gst_util_get_timestamp ();
3340 position = self->seek_position;
3341 self->seek_position = GST_CLOCK_TIME_NONE;
3342 self->seek_pending = TRUE;
3344 g_mutex_unlock (&self->lock);
3346 remove_tick_source (self);
3347 self->is_eos = FALSE;
3349 flags |= GST_SEEK_FLAG_FLUSH;
3351 accurate = gst_player_config_get_seek_accurate (self->config);
3354 flags |= GST_SEEK_FLAG_ACCURATE;
3356 flags &= ~GST_SEEK_FLAG_ACCURATE;
3360 flags |= GST_SEEK_FLAG_TRICKMODE;
3364 s_event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
3365 GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
3367 s_event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
3368 GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0), GST_SEEK_TYPE_SET, position);
3371 GST_DEBUG_OBJECT (self, "Seek with rate %.2lf to %" GST_TIME_FORMAT,
3372 rate, GST_TIME_ARGS (position));
3374 ret = gst_element_send_event (self->playbin, s_event);
3376 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3377 "Failed to seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (position)));
3379 g_mutex_lock (&self->lock);
3383 gst_player_seek_internal (gpointer user_data)
3385 GstPlayer *self = GST_PLAYER (user_data);
3387 g_mutex_lock (&self->lock);
3388 gst_player_seek_internal_locked (self);
3389 g_mutex_unlock (&self->lock);
3391 return G_SOURCE_REMOVE;
3395 * gst_player_set_rate:
3396 * @player: #GstPlayer instance
3397 * @rate: playback rate
3399 * Playback at specified rate
3402 gst_player_set_rate (GstPlayer * self, gdouble rate)
3404 g_return_if_fail (GST_IS_PLAYER (self));
3405 g_return_if_fail (rate != 0.0);
3407 g_object_set (self, "rate", rate, NULL);
3411 * gst_player_get_rate:
3412 * @player: #GstPlayer instance
3414 * Returns: current playback rate
3417 gst_player_get_rate (GstPlayer * self)
3421 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_RATE);
3423 g_object_get (self, "rate", &val, NULL);
3430 * @player: #GstPlayer instance
3431 * @position: position to seek in nanoseconds
3433 * Seeks the currently-playing stream to the absolute @position time
3437 gst_player_seek (GstPlayer * self, GstClockTime position)
3439 g_return_if_fail (GST_IS_PLAYER (self));
3440 g_return_if_fail (GST_CLOCK_TIME_IS_VALID (position));
3442 g_mutex_lock (&self->lock);
3443 if (self->media_info && !self->media_info->seekable) {
3444 GST_DEBUG_OBJECT (self, "Media is not seekable");
3445 g_mutex_unlock (&self->lock);
3449 self->seek_position = position;
3451 /* If there is no seek being dispatch to the main context currently do that,
3452 * otherwise we just updated the seek position so that it will be taken by
3453 * the seek handler from the main context instead of the old one.
3455 if (!self->seek_source) {
3456 GstClockTime now = gst_util_get_timestamp ();
3458 /* If no seek is pending or it was started more than 250 mseconds ago seek
3459 * immediately, otherwise wait until the 250 mseconds have passed */
3460 if (!self->seek_pending || (now - self->last_seek_time > 250 * GST_MSECOND)) {
3461 self->seek_source = g_idle_source_new ();
3462 g_source_set_callback (self->seek_source,
3463 (GSourceFunc) gst_player_seek_internal, self, NULL);
3464 GST_TRACE_OBJECT (self, "Dispatching seek to position %" GST_TIME_FORMAT,
3465 GST_TIME_ARGS (position));
3466 g_source_attach (self->seek_source, self->context);
3468 guint delay = 250000 - (now - self->last_seek_time) / 1000;
3470 /* Note that last_seek_time must be set to something at this point and
3471 * it must be smaller than 250 mseconds */
3472 self->seek_source = g_timeout_source_new (delay);
3473 g_source_set_callback (self->seek_source,
3474 (GSourceFunc) gst_player_seek_internal, self, NULL);
3476 GST_TRACE_OBJECT (self,
3477 "Delaying seek to position %" GST_TIME_FORMAT " by %u us",
3478 GST_TIME_ARGS (position), delay);
3479 g_source_attach (self->seek_source, self->context);
3482 g_mutex_unlock (&self->lock);
3486 remove_seek_source (GstPlayer * self)
3488 if (!self->seek_source)
3491 g_source_destroy (self->seek_source);
3492 g_source_unref (self->seek_source);
3493 self->seek_source = NULL;
3497 * gst_player_get_uri:
3498 * @player: #GstPlayer instance
3500 * Gets the URI of the currently-playing stream.
3502 * Returns: (transfer full): a string containing the URI of the
3503 * currently-playing stream. g_free() after usage.
3506 gst_player_get_uri (GstPlayer * self)
3510 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_URI);
3512 g_object_get (self, "uri", &val, NULL);
3518 * gst_player_set_uri:
3519 * @player: #GstPlayer instance
3520 * @uri: next URI to play.
3522 * Sets the next URI to play.
3525 gst_player_set_uri (GstPlayer * self, const gchar * val)
3527 g_return_if_fail (GST_IS_PLAYER (self));
3529 g_object_set (self, "uri", val, NULL);
3533 * gst_player_set_subtitle_uri:
3534 * @player: #GstPlayer instance
3535 * @uri: subtitle URI
3537 * Sets the external subtitle URI. This should be combined with a call to
3538 * gst_player_set_subtitle_track_enabled(@player, TRUE) so the subtitles are actually
3542 gst_player_set_subtitle_uri (GstPlayer * self, const gchar * suburi)
3544 g_return_if_fail (GST_IS_PLAYER (self));
3546 g_object_set (self, "suburi", suburi, NULL);
3550 * gst_player_get_subtitle_uri:
3551 * @player: #GstPlayer instance
3553 * current subtitle URI
3555 * Returns: (transfer full): URI of the current external subtitle.
3556 * g_free() after usage.
3559 gst_player_get_subtitle_uri (GstPlayer * self)
3563 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3565 g_object_get (self, "suburi", &val, NULL);
3571 * gst_player_get_position:
3572 * @player: #GstPlayer instance
3574 * Returns: the absolute position time, in nanoseconds, of the
3575 * currently-playing stream.
3578 gst_player_get_position (GstPlayer * self)
3582 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_POSITION);
3584 g_object_get (self, "position", &val, NULL);
3590 * gst_player_get_duration:
3591 * @player: #GstPlayer instance
3593 * Retrieves the duration of the media stream that self represents.
3595 * Returns: the duration of the currently-playing media stream, in
3599 gst_player_get_duration (GstPlayer * self)
3603 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_DURATION);
3605 g_object_get (self, "duration", &val, NULL);
3611 * gst_player_get_volume:
3612 * @player: #GstPlayer instance
3614 * Returns the current volume level, as a percentage between 0 and 1.
3616 * Returns: the volume as percentage between 0 and 1.
3619 gst_player_get_volume (GstPlayer * self)
3623 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_VOLUME);
3625 g_object_get (self, "volume", &val, NULL);
3631 * gst_player_set_volume:
3632 * @player: #GstPlayer instance
3633 * @val: the new volume level, as a percentage between 0 and 1
3635 * Sets the volume level of the stream as a percentage between 0 and 1.
3638 gst_player_set_volume (GstPlayer * self, gdouble val)
3640 g_return_if_fail (GST_IS_PLAYER (self));
3642 g_object_set (self, "volume", val, NULL);
3646 * gst_player_get_mute:
3647 * @player: #GstPlayer instance
3649 * Returns: %TRUE if the currently-playing stream is muted.
3652 gst_player_get_mute (GstPlayer * self)
3656 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_MUTE);
3658 g_object_get (self, "mute", &val, NULL);
3664 * gst_player_set_mute:
3665 * @player: #GstPlayer instance
3666 * @val: Mute state the should be set
3668 * %TRUE if the currently-playing stream should be muted.
3671 gst_player_set_mute (GstPlayer * self, gboolean val)
3673 g_return_if_fail (GST_IS_PLAYER (self));
3675 g_object_set (self, "mute", val, NULL);
3679 * gst_player_get_pipeline:
3680 * @player: #GstPlayer instance
3682 * Returns: (transfer full): The internal playbin instance
3685 gst_player_get_pipeline (GstPlayer * self)
3689 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3691 g_object_get (self, "pipeline", &val, NULL);
3697 * gst_player_get_media_info:
3698 * @player: #GstPlayer instance
3700 * A Function to get the current media info #GstPlayerMediaInfo instance.
3702 * Returns: (transfer full): media info instance.
3704 * The caller should free it with g_object_unref()
3706 GstPlayerMediaInfo *
3707 gst_player_get_media_info (GstPlayer * self)
3709 GstPlayerMediaInfo *info;
3711 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3713 if (!self->media_info)
3716 g_mutex_lock (&self->lock);
3717 info = gst_player_media_info_copy (self->media_info);
3718 g_mutex_unlock (&self->lock);
3724 * gst_player_get_current_audio_track:
3725 * @player: #GstPlayer instance
3727 * A Function to get current audio #GstPlayerAudioInfo instance.
3729 * Returns: (transfer full): current audio track.
3731 * The caller should free it with g_object_unref()
3733 GstPlayerAudioInfo *
3734 gst_player_get_current_audio_track (GstPlayer * self)
3736 GstPlayerAudioInfo *info;
3738 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3740 if (!is_track_enabled (self, GST_PLAY_FLAG_AUDIO))
3743 if (self->use_playbin3) {
3744 info = (GstPlayerAudioInfo *)
3745 gst_player_stream_info_get_current_from_stream_id (self,
3746 self->audio_sid, GST_TYPE_PLAYER_AUDIO_INFO);
3748 info = (GstPlayerAudioInfo *) gst_player_stream_info_get_current (self,
3749 "current-audio", GST_TYPE_PLAYER_AUDIO_INFO);
3756 * gst_player_get_current_video_track:
3757 * @player: #GstPlayer instance
3759 * A Function to get current video #GstPlayerVideoInfo instance.
3761 * Returns: (transfer full): current video track.
3763 * The caller should free it with g_object_unref()
3765 GstPlayerVideoInfo *
3766 gst_player_get_current_video_track (GstPlayer * self)
3768 GstPlayerVideoInfo *info;
3770 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3772 if (!is_track_enabled (self, GST_PLAY_FLAG_VIDEO))
3775 if (self->use_playbin3) {
3776 info = (GstPlayerVideoInfo *)
3777 gst_player_stream_info_get_current_from_stream_id (self,
3778 self->video_sid, GST_TYPE_PLAYER_VIDEO_INFO);
3780 info = (GstPlayerVideoInfo *) gst_player_stream_info_get_current (self,
3781 "current-video", GST_TYPE_PLAYER_VIDEO_INFO);
3788 * gst_player_get_current_subtitle_track:
3789 * @player: #GstPlayer instance
3791 * A Function to get current subtitle #GstPlayerSubtitleInfo instance.
3793 * Returns: (transfer none): current subtitle track.
3795 * The caller should free it with g_object_unref()
3797 GstPlayerSubtitleInfo *
3798 gst_player_get_current_subtitle_track (GstPlayer * self)
3800 GstPlayerSubtitleInfo *info;
3802 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3804 if (!is_track_enabled (self, GST_PLAY_FLAG_SUBTITLE))
3807 if (self->use_playbin3) {
3808 info = (GstPlayerSubtitleInfo *)
3809 gst_player_stream_info_get_current_from_stream_id (self,
3810 self->subtitle_sid, GST_TYPE_PLAYER_SUBTITLE_INFO);
3812 info = (GstPlayerSubtitleInfo *) gst_player_stream_info_get_current (self,
3813 "current-text", GST_TYPE_PLAYER_SUBTITLE_INFO);
3819 /* Must be called with lock */
3821 gst_player_select_streams (GstPlayer * self)
3823 GList *stream_list = NULL;
3824 gboolean ret = FALSE;
3826 if (self->audio_sid)
3827 stream_list = g_list_append (stream_list, g_strdup (self->audio_sid));
3828 if (self->video_sid)
3829 stream_list = g_list_append (stream_list, g_strdup (self->video_sid));
3830 if (self->subtitle_sid)
3831 stream_list = g_list_append (stream_list, g_strdup (self->subtitle_sid));
3833 g_mutex_unlock (&self->lock);
3835 ret = gst_element_send_event (self->playbin,
3836 gst_event_new_select_streams (stream_list));
3837 g_list_free_full (stream_list, g_free);
3839 GST_ERROR_OBJECT (self, "No available streams for select-streams");
3841 g_mutex_lock (&self->lock);
3847 * gst_player_set_audio_track:
3848 * @player: #GstPlayer instance
3849 * @stream_index: stream index
3851 * Returns: %TRUE or %FALSE
3853 * Sets the audio track @stream_idex.
3856 gst_player_set_audio_track (GstPlayer * self, gint stream_index)
3858 GstPlayerStreamInfo *info;
3859 gboolean ret = TRUE;
3861 g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3863 g_mutex_lock (&self->lock);
3864 info = gst_player_stream_info_find (self->media_info,
3865 GST_TYPE_PLAYER_AUDIO_INFO, stream_index);
3866 g_mutex_unlock (&self->lock);
3868 GST_ERROR_OBJECT (self, "invalid audio stream index %d", stream_index);
3872 if (self->use_playbin3) {
3873 g_mutex_lock (&self->lock);
3874 g_free (self->audio_sid);
3875 self->audio_sid = g_strdup (info->stream_id);
3876 ret = gst_player_select_streams (self);
3877 g_mutex_unlock (&self->lock);
3879 g_object_set (G_OBJECT (self->playbin), "current-audio", stream_index,
3883 GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3888 * gst_player_set_video_track:
3889 * @player: #GstPlayer instance
3890 * @stream_index: stream index
3892 * Returns: %TRUE or %FALSE
3894 * Sets the video track @stream_index.
3897 gst_player_set_video_track (GstPlayer * self, gint stream_index)
3899 GstPlayerStreamInfo *info;
3900 gboolean ret = TRUE;
3902 g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3904 /* check if stream_index exist in our internal media_info list */
3905 g_mutex_lock (&self->lock);
3906 info = gst_player_stream_info_find (self->media_info,
3907 GST_TYPE_PLAYER_VIDEO_INFO, stream_index);
3908 g_mutex_unlock (&self->lock);
3910 GST_ERROR_OBJECT (self, "invalid video stream index %d", stream_index);
3914 if (self->use_playbin3) {
3915 g_mutex_lock (&self->lock);
3916 g_free (self->video_sid);
3917 self->video_sid = g_strdup (info->stream_id);
3918 ret = gst_player_select_streams (self);
3919 g_mutex_unlock (&self->lock);
3921 g_object_set (G_OBJECT (self->playbin), "current-video", stream_index,
3925 GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3930 * gst_player_set_subtitle_track:
3931 * @player: #GstPlayer instance
3932 * @stream_index: stream index
3934 * Returns: %TRUE or %FALSE
3936 * Sets the subtitle strack @stream_index.
3939 gst_player_set_subtitle_track (GstPlayer * self, gint stream_index)
3941 GstPlayerStreamInfo *info;
3942 gboolean ret = TRUE;
3944 g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3946 g_mutex_lock (&self->lock);
3947 info = gst_player_stream_info_find (self->media_info,
3948 GST_TYPE_PLAYER_SUBTITLE_INFO, stream_index);
3949 g_mutex_unlock (&self->lock);
3951 GST_ERROR_OBJECT (self, "invalid subtitle stream index %d", stream_index);
3955 if (self->use_playbin3) {
3956 g_mutex_lock (&self->lock);
3957 g_free (self->subtitle_sid);
3958 self->subtitle_sid = g_strdup (info->stream_id);
3959 ret = gst_player_select_streams (self);
3960 g_mutex_unlock (&self->lock);
3962 g_object_set (G_OBJECT (self->playbin), "current-text", stream_index, NULL);
3965 GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3970 * gst_player_set_audio_track_enabled:
3971 * @player: #GstPlayer instance
3972 * @enabled: TRUE or FALSE
3974 * Enable or disable the current audio track.
3977 gst_player_set_audio_track_enabled (GstPlayer * self, gboolean enabled)
3979 g_return_if_fail (GST_IS_PLAYER (self));
3982 player_set_flag (self, GST_PLAY_FLAG_AUDIO);
3984 player_clear_flag (self, GST_PLAY_FLAG_AUDIO);
3986 GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
3990 * gst_player_set_video_track_enabled:
3991 * @player: #GstPlayer instance
3992 * @enabled: TRUE or FALSE
3994 * Enable or disable the current video track.
3997 gst_player_set_video_track_enabled (GstPlayer * self, gboolean enabled)
3999 g_return_if_fail (GST_IS_PLAYER (self));
4002 player_set_flag (self, GST_PLAY_FLAG_VIDEO);
4004 player_clear_flag (self, GST_PLAY_FLAG_VIDEO);
4006 GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
4010 * gst_player_set_subtitle_track_enabled:
4011 * @player: #GstPlayer instance
4012 * @enabled: TRUE or FALSE
4014 * Enable or disable the current subtitle track.
4017 gst_player_set_subtitle_track_enabled (GstPlayer * self, gboolean enabled)
4019 g_return_if_fail (GST_IS_PLAYER (self));
4022 player_set_flag (self, GST_PLAY_FLAG_SUBTITLE);
4024 player_clear_flag (self, GST_PLAY_FLAG_SUBTITLE);
4026 GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
4030 * gst_player_set_visualization:
4031 * @player: #GstPlayer instance
4032 * @name: visualization element obtained from
4033 * #gst_player_visualizations_get()
4035 * Returns: %TRUE if the visualizations was set correctly. Otherwise,
4039 gst_player_set_visualization (GstPlayer * self, const gchar * name)
4041 g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4043 g_mutex_lock (&self->lock);
4044 if (self->current_vis_element) {
4045 gst_object_unref (self->current_vis_element);
4046 self->current_vis_element = NULL;
4050 self->current_vis_element = gst_element_factory_make (name, NULL);
4051 if (!self->current_vis_element)
4052 goto error_no_element;
4053 gst_object_ref_sink (self->current_vis_element);
4055 g_object_set (self->playbin, "vis-plugin", self->current_vis_element, NULL);
4057 g_mutex_unlock (&self->lock);
4058 GST_DEBUG_OBJECT (self, "set vis-plugin to '%s'", name);
4063 g_mutex_unlock (&self->lock);
4064 GST_WARNING_OBJECT (self, "could not find visualization '%s'", name);
4069 * gst_player_get_current_visualization:
4070 * @player: #GstPlayer instance
4072 * Returns: (transfer full): Name of the currently enabled visualization.
4073 * g_free() after usage.
4076 gst_player_get_current_visualization (GstPlayer * self)
4079 GstElement *vis_plugin = NULL;
4081 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4083 if (!is_track_enabled (self, GST_PLAY_FLAG_VIS))
4086 g_object_get (self->playbin, "vis-plugin", &vis_plugin, NULL);
4089 GstElementFactory *factory = gst_element_get_factory (vis_plugin);
4091 name = g_strdup (gst_plugin_feature_get_name (factory));
4092 gst_object_unref (vis_plugin);
4095 GST_DEBUG_OBJECT (self, "vis-plugin '%s' %p", name, vis_plugin);
4101 * gst_player_set_visualization_enabled:
4102 * @player: #GstPlayer instance
4103 * @enabled: TRUE or FALSE
4105 * Enable or disable the visualization.
4108 gst_player_set_visualization_enabled (GstPlayer * self, gboolean enabled)
4110 g_return_if_fail (GST_IS_PLAYER (self));
4113 player_set_flag (self, GST_PLAY_FLAG_VIS);
4115 player_clear_flag (self, GST_PLAY_FLAG_VIS);
4117 GST_DEBUG_OBJECT (self, "visualization is '%s'",
4118 enabled ? "Enabled" : "Disabled");
4123 const gchar *label; /* channel label name */
4124 const gchar *name; /* get_name () */
4127 static const struct CBChannelMap cb_channel_map[] = {
4128 /* GST_PLAYER_COLOR_BALANCE_BRIGHTNESS */ {"BRIGHTNESS", "brightness"},
4129 /* GST_PLAYER_COLOR_BALANCE_CONTRAST */ {"CONTRAST", "contrast"},
4130 /* GST_PLAYER_COLOR_BALANCE_SATURATION */ {"SATURATION", "saturation"},
4131 /* GST_PLAYER_COLOR_BALANCE_HUE */ {"HUE", "hue"},
4134 static GstColorBalanceChannel *
4135 gst_player_color_balance_find_channel (GstPlayer * self,
4136 GstPlayerColorBalanceType type)
4138 GstColorBalanceChannel *channel;
4139 const GList *l, *channels;
4141 if (type < GST_PLAYER_COLOR_BALANCE_BRIGHTNESS ||
4142 type > GST_PLAYER_COLOR_BALANCE_HUE)
4146 gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin));
4147 for (l = channels; l; l = l->next) {
4149 if (g_strrstr (channel->label, cb_channel_map[type].label))
4157 * gst_player_has_color_balance:
4158 * @player:#GstPlayer instance
4160 * Checks whether the @player has color balance support available.
4162 * Returns: %TRUE if @player has color balance support. Otherwise,
4166 gst_player_has_color_balance (GstPlayer * self)
4168 const GList *channels;
4170 g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4172 if (!GST_IS_COLOR_BALANCE (self->playbin))
4176 gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin));
4177 return (channels != NULL);
4181 * gst_player_set_color_balance:
4182 * @player: #GstPlayer instance
4183 * @type: #GstPlayerColorBalanceType
4184 * @value: The new value for the @type, ranged [0,1]
4186 * Sets the current value of the indicated channel @type to the passed
4190 gst_player_set_color_balance (GstPlayer * self, GstPlayerColorBalanceType type,
4193 GstColorBalanceChannel *channel;
4196 g_return_if_fail (GST_IS_PLAYER (self));
4197 g_return_if_fail (value >= 0.0 && value <= 1.0);
4199 if (!GST_IS_COLOR_BALANCE (self->playbin))
4202 channel = gst_player_color_balance_find_channel (self, type);
4206 value = CLAMP (value, 0.0, 1.0);
4208 /* Convert to channel range */
4209 new_val = channel->min_value + value * ((gdouble) channel->max_value -
4210 (gdouble) channel->min_value);
4212 gst_color_balance_set_value (GST_COLOR_BALANCE (self->playbin), channel,
4217 * gst_player_get_color_balance:
4218 * @player: #GstPlayer instance
4219 * @type: #GstPlayerColorBalanceType
4221 * Retrieve the current value of the indicated @type.
4223 * Returns: The current value of @type, between [0,1]. In case of
4224 * error -1 is returned.
4227 gst_player_get_color_balance (GstPlayer * self, GstPlayerColorBalanceType type)
4229 GstColorBalanceChannel *channel;
4232 g_return_val_if_fail (GST_IS_PLAYER (self), -1);
4234 if (!GST_IS_COLOR_BALANCE (self->playbin))
4237 channel = gst_player_color_balance_find_channel (self, type);
4241 value = gst_color_balance_get_value (GST_COLOR_BALANCE (self->playbin),
4244 return ((gdouble) value -
4245 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
4246 (gdouble) channel->min_value);
4250 * gst_player_get_multiview_mode:
4251 * @player: #GstPlayer instance
4253 * Retrieve the current value of the indicated @type.
4255 * Returns: The current value of @type, Default: -1 "none"
4259 GstVideoMultiviewFramePacking
4260 gst_player_get_multiview_mode (GstPlayer * self)
4262 GstVideoMultiviewFramePacking val = GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE;
4264 g_return_val_if_fail (GST_IS_PLAYER (self),
4265 GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE);
4267 g_object_get (self, "video-multiview-mode", &val, NULL);
4273 * gst_player_set_multiview_mode:
4274 * @player: #GstPlayer instance
4275 * @mode: The new value for the @type
4277 * Sets the current value of the indicated mode @type to the passed
4283 gst_player_set_multiview_mode (GstPlayer * self,
4284 GstVideoMultiviewFramePacking mode)
4286 g_return_if_fail (GST_IS_PLAYER (self));
4288 g_object_set (self, "video-multiview-mode", mode, NULL);
4292 * gst_player_get_multiview_flags:
4293 * @player: #GstPlayer instance
4295 * Retrieve the current value of the indicated @type.
4297 * Returns: The current value of @type, Default: 0x00000000 "none
4301 GstVideoMultiviewFlags
4302 gst_player_get_multiview_flags (GstPlayer * self)
4304 GstVideoMultiviewFlags val = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
4306 g_return_val_if_fail (GST_IS_PLAYER (self), val);
4308 g_object_get (self, "video-multiview-flags", &val, NULL);
4314 * gst_player_set_multiview_flags:
4315 * @player: #GstPlayer instance
4316 * @flags: The new value for the @type
4318 * Sets the current value of the indicated mode @type to the passed
4324 gst_player_set_multiview_flags (GstPlayer * self, GstVideoMultiviewFlags flags)
4326 g_return_if_fail (GST_IS_PLAYER (self));
4328 g_object_set (self, "video-multiview-flags", flags, NULL);
4332 * gst_player_get_audio_video_offset:
4333 * @player: #GstPlayer instance
4335 * Retrieve the current value of audio-video-offset property
4337 * Returns: The current value of audio-video-offset in nanoseconds
4342 gst_player_get_audio_video_offset (GstPlayer * self)
4346 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_AUDIO_VIDEO_OFFSET);
4348 g_object_get (self, "audio-video-offset", &val, NULL);
4354 * gst_player_set_audio_video_offset:
4355 * @player: #GstPlayer instance
4356 * @offset: #gint64 in nanoseconds
4358 * Sets audio-video-offset property by value of @offset
4363 gst_player_set_audio_video_offset (GstPlayer * self, gint64 offset)
4365 g_return_if_fail (GST_IS_PLAYER (self));
4367 g_object_set (self, "audio-video-offset", offset, NULL);
4371 * gst_player_get_subtitle_video_offset:
4372 * @player: #GstPlayer instance
4374 * Retrieve the current value of subtitle-video-offset property
4376 * Returns: The current value of subtitle-video-offset in nanoseconds
4381 gst_player_get_subtitle_video_offset (GstPlayer * self)
4385 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_SUBTITLE_VIDEO_OFFSET);
4387 g_object_get (self, "subtitle-video-offset", &val, NULL);
4393 * gst_player_set_subtitle_video_offset:
4394 * @player: #GstPlayer instance
4395 * @offset: #gint64 in nanoseconds
4397 * Sets subtitle-video-offset property by value of @offset
4402 gst_player_set_subtitle_video_offset (GstPlayer * self, gint64 offset)
4404 g_return_if_fail (GST_IS_PLAYER (self));
4406 g_object_set (self, "subtitle-video-offset", offset, NULL);
4410 #define C_ENUM(v) ((gint) v)
4411 #define C_FLAGS(v) ((guint) v)
4414 gst_player_color_balance_type_get_type (void)
4416 static gsize id = 0;
4417 static const GEnumValue values[] = {
4418 {C_ENUM (GST_PLAYER_COLOR_BALANCE_HUE), "GST_PLAYER_COLOR_BALANCE_HUE",
4420 {C_ENUM (GST_PLAYER_COLOR_BALANCE_BRIGHTNESS),
4421 "GST_PLAYER_COLOR_BALANCE_BRIGHTNESS", "brightness"},
4422 {C_ENUM (GST_PLAYER_COLOR_BALANCE_SATURATION),
4423 "GST_PLAYER_COLOR_BALANCE_SATURATION", "saturation"},
4424 {C_ENUM (GST_PLAYER_COLOR_BALANCE_CONTRAST),
4425 "GST_PLAYER_COLOR_BALANCE_CONTRAST", "contrast"},
4429 if (g_once_init_enter (&id)) {
4430 GType tmp = g_enum_register_static ("GstPlayerColorBalanceType", values);
4431 g_once_init_leave (&id, tmp);
4438 * gst_player_color_balance_type_get_name:
4439 * @type: a #GstPlayerColorBalanceType
4441 * Gets a string representing the given color balance type.
4443 * Returns: (transfer none): a string with the name of the color
4447 gst_player_color_balance_type_get_name (GstPlayerColorBalanceType type)
4449 g_return_val_if_fail (type >= GST_PLAYER_COLOR_BALANCE_BRIGHTNESS &&
4450 type <= GST_PLAYER_COLOR_BALANCE_HUE, NULL);
4452 return cb_channel_map[type].name;
4456 gst_player_state_get_type (void)
4458 static gsize id = 0;
4459 static const GEnumValue values[] = {
4460 {C_ENUM (GST_PLAYER_STATE_STOPPED), "GST_PLAYER_STATE_STOPPED", "stopped"},
4461 {C_ENUM (GST_PLAYER_STATE_BUFFERING), "GST_PLAYER_STATE_BUFFERING",
4463 {C_ENUM (GST_PLAYER_STATE_PAUSED), "GST_PLAYER_STATE_PAUSED", "paused"},
4464 {C_ENUM (GST_PLAYER_STATE_PLAYING), "GST_PLAYER_STATE_PLAYING", "playing"},
4468 if (g_once_init_enter (&id)) {
4469 GType tmp = g_enum_register_static ("GstPlayerState", values);
4470 g_once_init_leave (&id, tmp);
4477 * gst_player_state_get_name:
4478 * @state: a #GstPlayerState
4480 * Gets a string representing the given state.
4482 * Returns: (transfer none): a string with the name of the state.
4485 gst_player_state_get_name (GstPlayerState state)
4488 case GST_PLAYER_STATE_STOPPED:
4490 case GST_PLAYER_STATE_BUFFERING:
4492 case GST_PLAYER_STATE_PAUSED:
4494 case GST_PLAYER_STATE_PLAYING:
4498 g_assert_not_reached ();
4503 gst_player_error_get_type (void)
4505 static gsize id = 0;
4506 static const GEnumValue values[] = {
4507 {C_ENUM (GST_PLAYER_ERROR_FAILED), "GST_PLAYER_ERROR_FAILED", "failed"},
4511 if (g_once_init_enter (&id)) {
4512 GType tmp = g_enum_register_static ("GstPlayerError", values);
4513 g_once_init_leave (&id, tmp);
4520 * gst_player_error_get_name:
4521 * @error: a #GstPlayerError
4523 * Gets a string representing the given error.
4525 * Returns: (transfer none): a string with the given error.
4528 gst_player_error_get_name (GstPlayerError error)
4531 case GST_PLAYER_ERROR_FAILED:
4535 g_assert_not_reached ();
4540 * gst_player_set_config:
4541 * @player: #GstPlayer instance
4542 * @config: (transfer full): a #GstStructure
4544 * Set the configuration of the player. If the player is already configured, and
4545 * the configuration haven't change, this function will return %TRUE. If the
4546 * player is not in the GST_PLAYER_STATE_STOPPED, this method will return %FALSE
4547 * and active configuration will remain.
4549 * @config is a #GstStructure that contains the configuration parameters for
4552 * This function takes ownership of @config.
4554 * Returns: %TRUE when the configuration could be set.
4558 gst_player_set_config (GstPlayer * self, GstStructure * config)
4560 g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4561 g_return_val_if_fail (config != NULL, FALSE);
4563 g_mutex_lock (&self->lock);
4565 if (self->app_state != GST_PLAYER_STATE_STOPPED) {
4566 GST_INFO_OBJECT (self, "can't change config while player is %s",
4567 gst_player_state_get_name (self->app_state));
4568 g_mutex_unlock (&self->lock);
4573 gst_structure_free (self->config);
4574 self->config = config;
4575 g_mutex_unlock (&self->lock);
4581 * gst_player_get_config:
4582 * @player: #GstPlayer instance
4584 * Get a copy of the current configuration of the player. This configuration
4585 * can either be modified and used for the gst_player_set_config() call
4586 * or it must be freed after usage.
4588 * Returns: (transfer full): a copy of the current configuration of @player. Use
4589 * gst_structure_free() after usage or gst_player_set_config().
4594 gst_player_get_config (GstPlayer * self)
4598 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4600 g_mutex_lock (&self->lock);
4601 ret = gst_structure_copy (self->config);
4602 g_mutex_unlock (&self->lock);
4608 * gst_player_config_set_user_agent:
4609 * @config: a #GstPlayer configuration
4610 * @agent: the string to use as user agent
4612 * Set the user agent to pass to the server if @player needs to connect
4613 * to a server during playback. This is typically used when playing HTTP
4619 gst_player_config_set_user_agent (GstStructure * config, const gchar * agent)
4621 g_return_if_fail (config != NULL);
4622 g_return_if_fail (agent != NULL);
4624 gst_structure_id_set (config,
4625 CONFIG_QUARK (USER_AGENT), G_TYPE_STRING, agent, NULL);
4629 * gst_player_config_get_user_agent:
4630 * @config: a #GstPlayer configuration
4632 * Return the user agent which has been configured using
4633 * gst_player_config_set_user_agent() if any.
4635 * Returns: (transfer full): the configured agent, or %NULL
4639 gst_player_config_get_user_agent (const GstStructure * config)
4641 gchar *agent = NULL;
4643 g_return_val_if_fail (config != NULL, NULL);
4645 gst_structure_id_get (config,
4646 CONFIG_QUARK (USER_AGENT), G_TYPE_STRING, &agent, NULL);
4652 * gst_player_config_set_position_update_interval:
4653 * @config: a #GstPlayer configuration
4654 * @interval: interval in ms
4656 * set interval in milliseconds between two position-updated signals.
4657 * pass 0 to stop updating the position.
4661 gst_player_config_set_position_update_interval (GstStructure * config,
4664 g_return_if_fail (config != NULL);
4665 g_return_if_fail (interval <= 10000);
4667 gst_structure_id_set (config,
4668 CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, interval, NULL);
4672 * gst_player_config_get_position_update_interval:
4673 * @config: a #GstPlayer configuration
4675 * Returns: current position update interval in milliseconds
4680 gst_player_config_get_position_update_interval (const GstStructure * config)
4682 guint interval = DEFAULT_POSITION_UPDATE_INTERVAL_MS;
4684 g_return_val_if_fail (config != NULL, DEFAULT_POSITION_UPDATE_INTERVAL_MS);
4686 gst_structure_id_get (config,
4687 CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, &interval, NULL);
4693 * gst_player_config_set_seek_accurate:
4694 * @config: a #GstPlayer configuration
4695 * @accurate: accurate seek or not
4697 * Enable or disable accurate seeking. When enabled, elements will try harder
4698 * to seek as accurately as possible to the requested seek position. Generally
4699 * it will be slower especially for formats that don't have any indexes or
4700 * timestamp markers in the stream.
4702 * If accurate seeking is disabled, elements will seek as close as the request
4703 * position without slowing down seeking too much.
4705 * Accurate seeking is disabled by default.
4710 gst_player_config_set_seek_accurate (GstStructure * config, gboolean accurate)
4712 g_return_if_fail (config != NULL);
4714 gst_structure_id_set (config,
4715 CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, accurate, NULL);
4719 * gst_player_config_get_seek_accurate:
4720 * @config: a #GstPlayer configuration
4722 * Returns: %TRUE if accurate seeking is enabled
4727 gst_player_config_get_seek_accurate (const GstStructure * config)
4729 gboolean accurate = FALSE;
4731 g_return_val_if_fail (config != NULL, FALSE);
4733 gst_structure_id_get (config,
4734 CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, &accurate, NULL);
4740 * gst_player_get_video_snapshot:
4741 * @player: #GstPlayer instance
4742 * @format: output format of the video snapshot
4743 * @config: (allow-none): Additional configuration
4745 * Get a snapshot of the currently selected video stream, if any. The format can be
4746 * selected with @format and optional configuration is possible with @config
4747 * Currently supported settings are:
4748 * - width, height of type G_TYPE_INT
4749 * - pixel-aspect-ratio of type GST_TYPE_FRACTION
4750 * Except for GST_PLAYER_THUMBNAIL_RAW_NATIVE format, if no config is set, pixel-aspect-ratio would be 1/1
4752 * Returns: (transfer full): Current video snapshot sample or %NULL on failure
4757 gst_player_get_video_snapshot (GstPlayer * self,
4758 GstPlayerSnapshotFormat format, const GstStructure * config)
4760 gint video_tracks = 0;
4761 GstSample *sample = NULL;
4762 GstCaps *caps = NULL;
4767 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4769 g_object_get (self->playbin, "n-video", &video_tracks, NULL);
4770 if (video_tracks == 0) {
4771 GST_DEBUG_OBJECT (self, "total video track num is 0");
4776 case GST_PLAYER_THUMBNAIL_RAW_xRGB:
4777 caps = gst_caps_new_simple ("video/x-raw",
4778 "format", G_TYPE_STRING, "xRGB", NULL);
4780 case GST_PLAYER_THUMBNAIL_RAW_BGRx:
4781 caps = gst_caps_new_simple ("video/x-raw",
4782 "format", G_TYPE_STRING, "BGRx", NULL);
4784 case GST_PLAYER_THUMBNAIL_JPG:
4785 caps = gst_caps_new_empty_simple ("image/jpeg");
4787 case GST_PLAYER_THUMBNAIL_PNG:
4788 caps = gst_caps_new_empty_simple ("image/png");
4790 case GST_PLAYER_THUMBNAIL_RAW_NATIVE:
4792 caps = gst_caps_new_empty_simple ("video/x-raw");
4796 if (NULL != config) {
4797 if (!gst_structure_get_int (config, "width", &width))
4799 if (!gst_structure_get_int (config, "height", &height))
4801 if (!gst_structure_get_fraction (config, "pixel-aspect-ratio", &par_n,
4803 if (format != GST_PLAYER_THUMBNAIL_RAW_NATIVE) {
4813 if (width > 0 && height > 0) {
4814 gst_caps_set_simple (caps, "width", G_TYPE_INT, width,
4815 "height", G_TYPE_INT, height, NULL);
4818 if (format != GST_PLAYER_THUMBNAIL_RAW_NATIVE) {
4819 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
4820 par_n, par_d, NULL);
4821 } else if (NULL != config && par_n != 0 && par_d != 0) {
4822 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
4823 par_n, par_d, NULL);
4826 g_signal_emit_by_name (self->playbin, "convert-sample", caps, &sample);
4827 gst_caps_unref (caps);
4829 GST_WARNING_OBJECT (self, "Failed to retrieve or convert video frame");