documentation: fixed a heap o' typos
[platform/upstream/gstreamer.git] / gst-libs / gst / player / gstplayer.c
1 /* GStreamer
2  *
3  * Copyright (C) 2014-2015 Sebastian Dröge <sebastian@centricular.com>
4  * Copyright (C) 2015 Brijesh Singh <brijesh.ksingh@gmail.com>
5  *
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.
10  *
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.
15  *
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.
20  */
21
22 /**
23  * SECTION:gstplayer
24  * @title: GstPlayer
25  * @short_description: Player
26  * @symbols:
27  * - GstPlayer
28  *
29  */
30
31 /* TODO:
32  *
33  * - Equalizer
34  * - Gapless playback
35  * - Frame stepping
36  * - Subtitle font, connection speed
37  * - Deinterlacing
38  * - Buffering control (-> progressive downloading)
39  * - Playlist/queue object
40  * - Custom video sink (e.g. embed in GL scene)
41  *
42  */
43
44 #ifdef HAVE_CONFIG_H
45 #include "config.h"
46 #endif
47
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"
52
53 #include <gst/gst.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>
58
59 #include <string.h>
60
61 GST_DEBUG_CATEGORY_STATIC (gst_player_debug);
62 #define GST_CAT_DEFAULT gst_player_debug
63
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
73
74 /**
75  * gst_player_error_quark:
76  */
77 GQuark
78 gst_player_error_quark (void)
79 {
80   return g_quark_from_static_string ("gst-player-error-quark");
81 }
82
83 static GQuark QUARK_CONFIG;
84
85 /* Keep ConfigQuarkId and _config_quark_strings ordered and synced */
86 typedef enum
87 {
88   CONFIG_QUARK_USER_AGENT = 0,
89   CONFIG_QUARK_POSITION_INTERVAL_UPDATE,
90   CONFIG_QUARK_ACCURATE_SEEK,
91
92   CONFIG_QUARK_MAX
93 } ConfigQuarkId;
94
95 static const gchar *_config_quark_strings[] = {
96   "user-agent",
97   "position-interval-update",
98   "accurate-seek",
99 };
100
101 GQuark _config_quark_table[CONFIG_QUARK_MAX];
102
103 #define CONFIG_QUARK(q) _config_quark_table[CONFIG_QUARK_##q]
104
105 enum
106 {
107   PROP_0,
108   PROP_VIDEO_RENDERER,
109   PROP_SIGNAL_DISPATCHER,
110   PROP_URI,
111   PROP_SUBURI,
112   PROP_POSITION,
113   PROP_DURATION,
114   PROP_MEDIA_INFO,
115   PROP_CURRENT_AUDIO_TRACK,
116   PROP_CURRENT_VIDEO_TRACK,
117   PROP_CURRENT_SUBTITLE_TRACK,
118   PROP_VOLUME,
119   PROP_MUTE,
120   PROP_RATE,
121   PROP_PIPELINE,
122   PROP_VIDEO_MULTIVIEW_MODE,
123   PROP_VIDEO_MULTIVIEW_FLAGS,
124   PROP_AUDIO_VIDEO_OFFSET,
125   PROP_SUBTITLE_VIDEO_OFFSET,
126   PROP_LAST
127 };
128
129 enum
130 {
131   SIGNAL_URI_LOADED,
132   SIGNAL_POSITION_UPDATED,
133   SIGNAL_DURATION_CHANGED,
134   SIGNAL_STATE_CHANGED,
135   SIGNAL_BUFFERING,
136   SIGNAL_END_OF_STREAM,
137   SIGNAL_ERROR,
138   SIGNAL_WARNING,
139   SIGNAL_VIDEO_DIMENSIONS_CHANGED,
140   SIGNAL_MEDIA_INFO_UPDATED,
141   SIGNAL_VOLUME_CHANGED,
142   SIGNAL_MUTE_CHANGED,
143   SIGNAL_SEEK_DONE,
144   SIGNAL_LAST
145 };
146
147 enum
148 {
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)
153 };
154
155 struct _GstPlayer
156 {
157   GstObject parent;
158
159   GstPlayerVideoRenderer *video_renderer;
160   GstPlayerSignalDispatcher *signal_dispatcher;
161
162   gchar *uri;
163   gchar *redirect_uri;
164   gchar *suburi;
165
166   GThread *thread;
167   GMutex lock;
168   GCond cond;
169   GMainContext *context;
170   GMainLoop *loop;
171
172   GstElement *playbin;
173   GstBus *bus;
174   GstState target_state, current_state;
175   gboolean is_live, is_eos;
176   GSource *tick_source, *ready_timeout_source;
177   GstClockTime cached_duration;
178
179   gdouble rate;
180
181   GstPlayerState app_state;
182   gint buffering;
183
184   GstTagList *global_tags;
185   GstPlayerMediaInfo *media_info;
186
187   GstElement *current_vis_element;
188
189   GstStructure *config;
190
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;
200
201   /* For playbin3 */
202   gboolean use_playbin3;
203   GstStreamCollection *collection;
204   gchar *video_sid;
205   gchar *audio_sid;
206   gchar *subtitle_sid;
207   gulong stream_notify_id;
208 };
209
210 struct _GstPlayerClass
211 {
212   GstObjectClass parent_class;
213 };
214
215 #define parent_class gst_player_parent_class
216 G_DEFINE_TYPE (GstPlayer, gst_player, GST_TYPE_OBJECT);
217
218 static guint signals[SIGNAL_LAST] = { 0, };
219 static GParamSpec *param_specs[PROP_LAST] = { NULL, };
220
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);
228
229 static gpointer gst_player_main (gpointer data);
230
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);
238
239 static GstPlayerMediaInfo *gst_player_media_info_create (GstPlayer * self);
240
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);
251
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);
258
259 /* For playbin3 */
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);
270
271 static void emit_media_info_updated_signal (GstPlayer * self);
272
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);
278
279 static void remove_seek_source (GstPlayer * self);
280
281 static void
282 gst_player_init (GstPlayer * self)
283 {
284   GST_TRACE_OBJECT (self, "Initializing");
285
286   self = gst_player_get_instance_private (self);
287
288   g_mutex_init (&self->lock);
289   g_cond_init (&self->cond);
290
291   self->context = g_main_context_new ();
292   self->loop = g_main_loop_new (self->context, FALSE);
293
294   /* *INDENT-OFF* */
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,
298       NULL);
299   /* *INDENT-ON* */
300
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;
305
306   GST_TRACE_OBJECT (self, "Initialized");
307 }
308
309 static void
310 config_quark_initialize (void)
311 {
312   gint i;
313
314   QUARK_CONFIG = g_quark_from_static_string ("player-config");
315
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);
319
320   for (i = 0; i < CONFIG_QUARK_MAX; i++) {
321     _config_quark_table[i] =
322         g_quark_from_static_string (_config_quark_strings[i]);
323   }
324 }
325
326 static void
327 gst_player_class_init (GstPlayerClass * klass)
328 {
329   GObjectClass *gobject_class = (GObjectClass *) klass;
330
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;
336
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);
342
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);
348
349   param_specs[PROP_URI] = g_param_spec_string ("uri", "URI", "Current URI",
350       DEFAULT_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
351
352   param_specs[PROP_SUBURI] = g_param_spec_string ("suburi", "Subtitle URI",
353       "Current Subtitle URI", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
354
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);
359
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);
364
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);
369
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);
374
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);
379
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);
384
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);
388
389   param_specs[PROP_MUTE] =
390       g_param_spec_boolean ("mute", "Mute", "Mute",
391       DEFAULT_MUTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
392
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);
397
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);
401
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);
409
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);
416
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);
421
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);
426
427   g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
428
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);
433
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);
438
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);
443
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);
448
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);
453
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);
458
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);
463
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);
468
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);
473
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);
478
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);
483
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);
488
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);
493
494   config_quark_initialize ();
495 }
496
497 static void
498 gst_player_dispose (GObject * object)
499 {
500   GstPlayer *self = GST_PLAYER (object);
501
502   GST_TRACE_OBJECT (self, "Stopping main thread");
503
504   if (self->loop) {
505     g_main_loop_quit (self->loop);
506
507     if (self->thread != g_thread_self ())
508       g_thread_join (self->thread);
509     else
510       g_thread_unref (self->thread);
511     self->thread = NULL;
512
513     g_main_loop_unref (self->loop);
514     self->loop = NULL;
515
516     g_main_context_unref (self->context);
517     self->context = NULL;
518   }
519
520   G_OBJECT_CLASS (parent_class)->dispose (object);
521 }
522
523 static void
524 gst_player_finalize (GObject * object)
525 {
526   GstPlayer *self = GST_PLAYER (object);
527
528   GST_TRACE_OBJECT (self, "Finalizing");
529
530   g_free (self->uri);
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);
544   if (self->config)
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);
550
551   G_OBJECT_CLASS (parent_class)->finalize (object);
552 }
553
554 static void
555 gst_player_constructed (GObject * object)
556 {
557   GstPlayer *self = GST_PLAYER (object);
558
559   GST_TRACE_OBJECT (self, "Constructed");
560
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);
566
567   G_OBJECT_CLASS (parent_class)->constructed (object);
568 }
569
570 typedef struct
571 {
572   GstPlayer *player;
573   gchar *uri;
574 } UriLoadedSignalData;
575
576 static void
577 uri_loaded_dispatch (gpointer user_data)
578 {
579   UriLoadedSignalData *data = user_data;
580
581   g_signal_emit (data->player, signals[SIGNAL_URI_LOADED], 0, data->uri);
582 }
583
584 static void
585 uri_loaded_signal_data_free (UriLoadedSignalData * data)
586 {
587   g_object_unref (data->player);
588   g_free (data->uri);
589   g_free (data);
590 }
591
592 static gboolean
593 gst_player_set_uri_internal (gpointer user_data)
594 {
595   GstPlayer *self = user_data;
596
597   gst_player_stop_internal (self, FALSE);
598
599   g_mutex_lock (&self->lock);
600
601   GST_DEBUG_OBJECT (self, "Changing URI to '%s'", GST_STR_NULL (self->uri));
602
603   g_object_set (self->playbin, "uri", self->uri, NULL);
604
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);
608
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);
614   }
615
616   g_object_set (self->playbin, "suburi", NULL, NULL);
617
618   g_mutex_unlock (&self->lock);
619
620   return G_SOURCE_REMOVE;
621 }
622
623 static gboolean
624 gst_player_set_suburi_internal (gpointer user_data)
625 {
626   GstPlayer *self = user_data;
627   GstClockTime position;
628   GstState target_state;
629
630   /* save the state and position */
631   target_state = self->target_state;
632   position = gst_player_get_position (self);
633
634   gst_player_stop_internal (self, TRUE);
635   g_mutex_lock (&self->lock);
636
637   GST_DEBUG_OBJECT (self, "Changing SUBURI to '%s'",
638       GST_STR_NULL (self->suburi));
639
640   g_object_set (self->playbin, "suburi", self->suburi, NULL);
641
642   g_mutex_unlock (&self->lock);
643
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);
651
652   return G_SOURCE_REMOVE;
653 }
654
655 static void
656 gst_player_set_rate_internal (GstPlayer * self)
657 {
658   self->seek_position = gst_player_get_position (self);
659
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.
663    */
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);
671     }
672   }
673 }
674
675 static void
676 gst_player_set_property (GObject * object, guint prop_id,
677     const GValue * value, GParamSpec * pspec)
678 {
679   GstPlayer *self = GST_PLAYER (object);
680
681   switch (prop_id) {
682     case PROP_VIDEO_RENDERER:
683       self->video_renderer = g_value_dup_object (value);
684       break;
685     case PROP_SIGNAL_DISPATCHER:
686       self->signal_dispatcher = g_value_dup_object (value);
687       break;
688     case PROP_URI:{
689       g_mutex_lock (&self->lock);
690       g_free (self->uri);
691       g_free (self->redirect_uri);
692       self->redirect_uri = NULL;
693
694       g_free (self->suburi);
695       self->suburi = NULL;
696
697       self->uri = g_value_dup_string (value);
698       GST_DEBUG_OBJECT (self, "Set uri=%s", self->uri);
699       g_mutex_unlock (&self->lock);
700
701       g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
702           gst_player_set_uri_internal, self, NULL);
703       break;
704     }
705     case PROP_SUBURI:{
706       g_mutex_lock (&self->lock);
707       g_free (self->suburi);
708
709       self->suburi = g_value_dup_string (value);
710       GST_DEBUG_OBJECT (self, "Set suburi=%s", self->suburi);
711       g_mutex_unlock (&self->lock);
712
713       g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
714           gst_player_set_suburi_internal, self, NULL);
715       break;
716     }
717     case PROP_VOLUME:
718       GST_DEBUG_OBJECT (self, "Set volume=%lf", g_value_get_double (value));
719       g_object_set_property (G_OBJECT (self->playbin), "volume", value);
720       break;
721     case PROP_RATE:
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);
727       break;
728     case PROP_MUTE:
729       GST_DEBUG_OBJECT (self, "Set mute=%d", g_value_get_boolean (value));
730       g_object_set_property (G_OBJECT (self->playbin), "mute", value);
731       break;
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",
736           value);
737       break;
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",
742           value);
743       break;
744     case PROP_AUDIO_VIDEO_OFFSET:
745       g_object_set_property (G_OBJECT (self->playbin), "av-offset", value);
746       break;
747     case PROP_SUBTITLE_VIDEO_OFFSET:
748       g_object_set_property (G_OBJECT (self->playbin), "text-offset", value);
749       break;
750     default:
751       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
752       break;
753   }
754 }
755
756 static void
757 gst_player_get_property (GObject * object, guint prop_id,
758     GValue * value, GParamSpec * pspec)
759 {
760   GstPlayer *self = GST_PLAYER (object);
761
762   switch (prop_id) {
763     case PROP_URI:
764       g_mutex_lock (&self->lock);
765       g_value_set_string (value, self->uri);
766       g_mutex_unlock (&self->lock);
767       break;
768     case PROP_SUBURI:
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));
774       break;
775     case PROP_POSITION:{
776       gint64 position = GST_CLOCK_TIME_NONE;
777
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)));
782       break;
783     }
784     case PROP_DURATION:{
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)));
788       break;
789     }
790     case PROP_MEDIA_INFO:{
791       GstPlayerMediaInfo *media_info = gst_player_get_media_info (self);
792       g_value_take_object (value, media_info);
793       break;
794     }
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);
799       break;
800     }
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);
805       break;
806     }
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);
811       break;
812     }
813     case PROP_VOLUME:
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));
817       break;
818     case PROP_RATE:
819       g_mutex_lock (&self->lock);
820       g_value_set_double (value, self->rate);
821       g_mutex_unlock (&self->lock);
822       break;
823     case PROP_MUTE:
824       g_object_get_property (G_OBJECT (self->playbin), "mute", value);
825       GST_TRACE_OBJECT (self, "Returning mute=%d", g_value_get_boolean (value));
826       break;
827     case PROP_PIPELINE:
828       g_value_set_object (value, self->playbin);
829       break;
830     case PROP_VIDEO_MULTIVIEW_MODE:{
831       g_object_get_property (G_OBJECT (self->playbin), "video-multiview-mode",
832           value);
833       GST_TRACE_OBJECT (self, "Return multiview mode=%d",
834           g_value_get_enum (value));
835       break;
836     }
837     case PROP_VIDEO_MULTIVIEW_FLAGS:{
838       g_object_get_property (G_OBJECT (self->playbin), "video-multiview-flags",
839           value);
840       GST_TRACE_OBJECT (self, "Return multiview flags=%x",
841           g_value_get_flags (value));
842       break;
843     }
844     case PROP_AUDIO_VIDEO_OFFSET:
845       g_object_get_property (G_OBJECT (self->playbin), "av-offset", value);
846       break;
847     case PROP_SUBTITLE_VIDEO_OFFSET:
848       g_object_get_property (G_OBJECT (self->playbin), "text-offset", value);
849       break;
850     default:
851       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
852       break;
853   }
854 }
855
856 static gboolean
857 main_loop_running_cb (gpointer user_data)
858 {
859   GstPlayer *self = GST_PLAYER (user_data);
860
861   GST_TRACE_OBJECT (self, "Main loop running now");
862
863   g_mutex_lock (&self->lock);
864   g_cond_signal (&self->cond);
865   g_mutex_unlock (&self->lock);
866
867   return G_SOURCE_REMOVE;
868 }
869
870 typedef struct
871 {
872   GstPlayer *player;
873   GstPlayerState state;
874 } StateChangedSignalData;
875
876 static void
877 state_changed_dispatch (gpointer user_data)
878 {
879   StateChangedSignalData *data = user_data;
880
881   if (data->player->inhibit_sigs && data->state != GST_PLAYER_STATE_STOPPED
882       && data->state != GST_PLAYER_STATE_PAUSED)
883     return;
884
885   g_signal_emit (data->player, signals[SIGNAL_STATE_CHANGED], 0, data->state);
886 }
887
888 static void
889 state_changed_signal_data_free (StateChangedSignalData * data)
890 {
891   g_object_unref (data->player);
892   g_free (data);
893 }
894
895 static void
896 change_state (GstPlayer * self, GstPlayerState state)
897 {
898   if (state == self->app_state)
899     return;
900
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;
905
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);
909
910     data->player = g_object_ref (self);
911     data->state = state;
912     gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
913         state_changed_dispatch, data,
914         (GDestroyNotify) state_changed_signal_data_free);
915   }
916 }
917
918 typedef struct
919 {
920   GstPlayer *player;
921   GstClockTime position;
922 } PositionUpdatedSignalData;
923
924 static void
925 position_updated_dispatch (gpointer user_data)
926 {
927   PositionUpdatedSignalData *data = user_data;
928
929   if (data->player->inhibit_sigs)
930     return;
931
932   if (data->player->target_state >= GST_STATE_PAUSED) {
933     g_signal_emit (data->player, signals[SIGNAL_POSITION_UPDATED], 0,
934         data->position);
935     g_object_notify_by_pspec (G_OBJECT (data->player),
936         param_specs[PROP_POSITION]);
937   }
938 }
939
940 static void
941 position_updated_signal_data_free (PositionUpdatedSignalData * data)
942 {
943   g_object_unref (data->player);
944   g_free (data);
945 }
946
947 static gboolean
948 tick_cb (gpointer user_data)
949 {
950   GstPlayer *self = GST_PLAYER (user_data);
951   gint64 position;
952
953   if (self->target_state >= GST_STATE_PAUSED
954       && gst_element_query_position (self->playbin, GST_FORMAT_TIME,
955           &position)) {
956     GST_LOG_OBJECT (self, "Position %" GST_TIME_FORMAT,
957         GST_TIME_ARGS (position));
958
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);
962
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);
968     }
969   }
970
971   return G_SOURCE_CONTINUE;
972 }
973
974 static void
975 add_tick_source (GstPlayer * self)
976 {
977   guint position_update_interval_ms;
978
979   if (self->tick_source)
980     return;
981
982   position_update_interval_ms =
983       gst_player_config_get_position_update_interval (self->config);
984   if (!position_update_interval_ms)
985     return;
986
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);
990 }
991
992 static void
993 remove_tick_source (GstPlayer * self)
994 {
995   if (!self->tick_source)
996     return;
997
998   g_source_destroy (self->tick_source);
999   g_source_unref (self->tick_source);
1000   self->tick_source = NULL;
1001 }
1002
1003 static gboolean
1004 ready_timeout_cb (gpointer user_data)
1005 {
1006   GstPlayer *self = user_data;
1007
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);
1013   }
1014
1015   return G_SOURCE_REMOVE;
1016 }
1017
1018 static void
1019 add_ready_timeout_source (GstPlayer * self)
1020 {
1021   if (self->ready_timeout_source)
1022     return;
1023
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);
1028 }
1029
1030 static void
1031 remove_ready_timeout_source (GstPlayer * self)
1032 {
1033   if (!self->ready_timeout_source)
1034     return;
1035
1036   g_source_destroy (self->ready_timeout_source);
1037   g_source_unref (self->ready_timeout_source);
1038   self->ready_timeout_source = NULL;
1039 }
1040
1041 typedef struct
1042 {
1043   GstPlayer *player;
1044   GError *err;
1045 } ErrorSignalData;
1046
1047 static void
1048 error_dispatch (gpointer user_data)
1049 {
1050   ErrorSignalData *data = user_data;
1051
1052   if (data->player->inhibit_sigs)
1053     return;
1054
1055   g_signal_emit (data->player, signals[SIGNAL_ERROR], 0, data->err);
1056 }
1057
1058 static void
1059 free_error_signal_data (ErrorSignalData * data)
1060 {
1061   g_object_unref (data->player);
1062   g_clear_error (&data->err);
1063   g_free (data);
1064 }
1065
1066 static void
1067 emit_error (GstPlayer * self, GError * err)
1068 {
1069   GST_ERROR_OBJECT (self, "Error: %s (%s, %d)", err->message,
1070       g_quark_to_string (err->domain), err->code);
1071
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);
1075
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);
1080   }
1081
1082   g_error_free (err);
1083
1084   remove_tick_source (self);
1085   remove_ready_timeout_source (self);
1086
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;
1094
1095   g_mutex_lock (&self->lock);
1096   if (self->media_info) {
1097     g_object_unref (self->media_info);
1098     self->media_info = NULL;
1099   }
1100
1101   if (self->global_tags) {
1102     gst_tag_list_unref (self->global_tags);
1103     self->global_tags = NULL;
1104   }
1105
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);
1111 }
1112
1113 static void
1114 dump_dot_file (GstPlayer * self, const gchar * name)
1115 {
1116   gchar *full_name;
1117
1118   full_name = g_strdup_printf ("gst-player.%p.%s", self, name);
1119
1120   GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->playbin),
1121       GST_DEBUG_GRAPH_SHOW_ALL, full_name);
1122
1123   g_free (full_name);
1124 }
1125
1126 typedef struct
1127 {
1128   GstPlayer *player;
1129   GError *err;
1130 } WarningSignalData;
1131
1132 static void
1133 warning_dispatch (gpointer user_data)
1134 {
1135   WarningSignalData *data = user_data;
1136
1137   if (data->player->inhibit_sigs)
1138     return;
1139
1140   g_signal_emit (data->player, signals[SIGNAL_WARNING], 0, data->err);
1141 }
1142
1143 static void
1144 free_warning_signal_data (WarningSignalData * data)
1145 {
1146   g_object_unref (data->player);
1147   g_clear_error (&data->err);
1148   g_free (data);
1149 }
1150
1151 static void
1152 emit_warning (GstPlayer * self, GError * err)
1153 {
1154   GST_ERROR_OBJECT (self, "Warning: %s (%s, %d)", err->message,
1155       g_quark_to_string (err->domain), err->code);
1156
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);
1160
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);
1165   }
1166
1167   g_error_free (err);
1168 }
1169
1170 static void
1171 error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1172 {
1173   GstPlayer *self = GST_PLAYER (user_data);
1174   GError *err, *player_err;
1175   gchar *name, *debug, *message, *full_message;
1176
1177   dump_dot_file (self, "error");
1178
1179   gst_message_parse_error (msg, &err, &debug);
1180
1181   name = gst_object_get_path_string (msg->src);
1182   message = gst_error_get_message (err->domain, err->code);
1183
1184   if (debug)
1185     full_message =
1186         g_strdup_printf ("Error from element %s: %s\n%s\n%s", name, message,
1187         err->message, debug);
1188   else
1189     full_message =
1190         g_strdup_printf ("Error from element %s: %s\n%s", name, message,
1191         err->message);
1192
1193   GST_ERROR_OBJECT (self, "ERROR: from element %s: %s\n", name, err->message);
1194   if (debug != NULL)
1195     GST_ERROR_OBJECT (self, "Additional debug info:\n%s\n", debug);
1196
1197   player_err =
1198       g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1199       full_message);
1200   emit_error (self, player_err);
1201
1202   g_clear_error (&err);
1203   g_free (debug);
1204   g_free (name);
1205   g_free (full_message);
1206   g_free (message);
1207 }
1208
1209 static void
1210 warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1211 {
1212   GstPlayer *self = GST_PLAYER (user_data);
1213   GError *err, *player_err;
1214   gchar *name, *debug, *message, *full_message;
1215
1216   dump_dot_file (self, "warning");
1217
1218   gst_message_parse_warning (msg, &err, &debug);
1219
1220   name = gst_object_get_path_string (msg->src);
1221   message = gst_error_get_message (err->domain, err->code);
1222
1223   if (debug)
1224     full_message =
1225         g_strdup_printf ("Warning from element %s: %s\n%s\n%s", name, message,
1226         err->message, debug);
1227   else
1228     full_message =
1229         g_strdup_printf ("Warning from element %s: %s\n%s", name, message,
1230         err->message);
1231
1232   GST_WARNING_OBJECT (self, "WARNING: from element %s: %s\n", name,
1233       err->message);
1234   if (debug != NULL)
1235     GST_WARNING_OBJECT (self, "Additional debug info:\n%s\n", debug);
1236
1237   player_err =
1238       g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1239       full_message);
1240   emit_warning (self, player_err);
1241
1242   g_clear_error (&err);
1243   g_free (debug);
1244   g_free (name);
1245   g_free (full_message);
1246   g_free (message);
1247 }
1248
1249 static void
1250 eos_dispatch (gpointer user_data)
1251 {
1252   GstPlayer *player = user_data;
1253
1254   if (player->inhibit_sigs)
1255     return;
1256
1257   g_signal_emit (player, signals[SIGNAL_END_OF_STREAM], 0);
1258 }
1259
1260 static void
1261 eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1262     gpointer user_data)
1263 {
1264   GstPlayer *self = GST_PLAYER (user_data);
1265
1266   GST_DEBUG_OBJECT (self, "End of stream");
1267
1268   tick_cb (self);
1269   remove_tick_source (self);
1270
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);
1275   }
1276   change_state (self, GST_PLAYER_STATE_STOPPED);
1277   self->buffering = 100;
1278   self->is_eos = TRUE;
1279 }
1280
1281 typedef struct
1282 {
1283   GstPlayer *player;
1284   gint percent;
1285 } BufferingSignalData;
1286
1287 static void
1288 buffering_dispatch (gpointer user_data)
1289 {
1290   BufferingSignalData *data = user_data;
1291
1292   if (data->player->inhibit_sigs)
1293     return;
1294
1295   if (data->player->target_state >= GST_STATE_PAUSED) {
1296     g_signal_emit (data->player, signals[SIGNAL_BUFFERING], 0, data->percent);
1297   }
1298 }
1299
1300 static void
1301 buffering_signal_data_free (BufferingSignalData * data)
1302 {
1303   g_object_unref (data->player);
1304   g_free (data);
1305 }
1306
1307 static void
1308 buffering_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1309 {
1310   GstPlayer *self = GST_PLAYER (user_data);
1311   gint percent;
1312
1313   if (self->target_state < GST_STATE_PAUSED)
1314     return;
1315   if (self->is_live)
1316     return;
1317
1318   gst_message_parse_buffering (msg, &percent);
1319   GST_LOG_OBJECT (self, "Buffering %d%%", percent);
1320
1321   if (percent < 100 && self->target_state >= GST_STATE_PAUSED) {
1322     GstStateChangeReturn state_ret;
1323
1324     GST_DEBUG_OBJECT (self, "Waiting for buffering to finish");
1325     state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
1326
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"));
1330       return;
1331     }
1332
1333     change_state (self, GST_PLAYER_STATE_BUFFERING);
1334   }
1335
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);
1340
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);
1346     }
1347
1348     self->buffering = percent;
1349   }
1350
1351
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);
1356
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;
1361
1362     g_mutex_unlock (&self->lock);
1363
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);
1372
1373     GST_DEBUG_OBJECT (self, "Buffering finished - staying PAUSED");
1374     change_state (self, GST_PLAYER_STATE_PAUSED);
1375   } else {
1376     g_mutex_unlock (&self->lock);
1377   }
1378 }
1379
1380 static void
1381 clock_lost_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1382     gpointer user_data)
1383 {
1384   GstPlayer *self = GST_PLAYER (user_data);
1385   GstStateChangeReturn state_ret;
1386
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);
1392
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"));
1396   }
1397 }
1398
1399 typedef struct
1400 {
1401   GstPlayer *player;
1402   gint width, height;
1403 } VideoDimensionsChangedSignalData;
1404
1405 static void
1406 video_dimensions_changed_dispatch (gpointer user_data)
1407 {
1408   VideoDimensionsChangedSignalData *data = user_data;
1409
1410   if (data->player->inhibit_sigs)
1411     return;
1412
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);
1416   }
1417 }
1418
1419 static void
1420 video_dimensions_changed_signal_data_free (VideoDimensionsChangedSignalData *
1421     data)
1422 {
1423   g_object_unref (data->player);
1424   g_free (data);
1425 }
1426
1427 static void
1428 check_video_dimensions_changed (GstPlayer * self)
1429 {
1430   GstElement *video_sink;
1431   GstPad *video_sink_pad;
1432   GstCaps *caps;
1433   GstVideoInfo info;
1434   gint width = 0, height = 0;
1435
1436   g_object_get (self->playbin, "video-sink", &video_sink, NULL);
1437   if (!video_sink)
1438     goto out;
1439
1440   video_sink_pad = gst_element_get_static_pad (video_sink, "sink");
1441   if (!video_sink_pad) {
1442     gst_object_unref (video_sink);
1443     goto out;
1444   }
1445
1446   caps = gst_pad_get_current_caps (video_sink_pad);
1447
1448   if (caps) {
1449     if (gst_video_info_from_caps (&info, caps)) {
1450       info.width = info.width * info.par_n / info.par_d;
1451
1452       GST_DEBUG_OBJECT (self, "Video dimensions changed: %dx%d", info.width,
1453           info.height);
1454       width = info.width;
1455       height = info.height;
1456     }
1457
1458     gst_caps_unref (caps);
1459   }
1460   gst_object_unref (video_sink_pad);
1461   gst_object_unref (video_sink);
1462
1463 out:
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);
1468
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);
1475   }
1476 }
1477
1478 static void
1479 notify_caps_cb (G_GNUC_UNUSED GObject * object,
1480     G_GNUC_UNUSED GParamSpec * pspec, gpointer user_data)
1481 {
1482   GstPlayer *self = GST_PLAYER (user_data);
1483
1484   check_video_dimensions_changed (self);
1485 }
1486
1487 typedef struct
1488 {
1489   GstPlayer *player;
1490   GstClockTime duration;
1491 } DurationChangedSignalData;
1492
1493 static void
1494 duration_changed_dispatch (gpointer user_data)
1495 {
1496   DurationChangedSignalData *data = user_data;
1497
1498   if (data->player->inhibit_sigs)
1499     return;
1500
1501   if (data->player->target_state >= GST_STATE_PAUSED) {
1502     g_signal_emit (data->player, signals[SIGNAL_DURATION_CHANGED], 0,
1503         data->duration);
1504     g_object_notify_by_pspec (G_OBJECT (data->player),
1505         param_specs[PROP_DURATION]);
1506   }
1507 }
1508
1509 static void
1510 duration_changed_signal_data_free (DurationChangedSignalData * data)
1511 {
1512   g_object_unref (data->player);
1513   g_free (data);
1514 }
1515
1516 static void
1517 emit_duration_changed (GstPlayer * self, GstClockTime duration)
1518 {
1519   gboolean updated = FALSE;
1520
1521   if (self->cached_duration == duration)
1522     return;
1523
1524   GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT,
1525       GST_TIME_ARGS (duration));
1526
1527   self->cached_duration = duration;
1528   g_mutex_lock (&self->lock);
1529   if (self->media_info) {
1530     self->media_info->duration = duration;
1531     updated = TRUE;
1532   }
1533   g_mutex_unlock (&self->lock);
1534   if (updated) {
1535     emit_media_info_updated_signal (self);
1536   }
1537
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);
1541
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);
1547   }
1548 }
1549
1550 typedef struct
1551 {
1552   GstPlayer *player;
1553   GstClockTime position;
1554 } SeekDoneSignalData;
1555
1556 static void
1557 seek_done_dispatch (gpointer user_data)
1558 {
1559   SeekDoneSignalData *data = user_data;
1560
1561   if (data->player->inhibit_sigs)
1562     return;
1563
1564   g_signal_emit (data->player, signals[SIGNAL_SEEK_DONE], 0, data->position);
1565 }
1566
1567 static void
1568 seek_done_signal_data_free (SeekDoneSignalData * data)
1569 {
1570   g_object_unref (data->player);
1571   g_free (data);
1572 }
1573
1574 static void
1575 emit_seek_done (GstPlayer * self)
1576 {
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);
1580
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);
1585   }
1586 }
1587
1588 static void
1589 state_changed_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1590     gpointer user_data)
1591 {
1592   GstPlayer *self = GST_PLAYER (user_data);
1593   GstState old_state, new_state, pending_state;
1594
1595   gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
1596
1597   if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->playbin)) {
1598     gchar *transition_name;
1599
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));
1604
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);
1610
1611     self->current_state = new_state;
1612
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;
1618
1619       GST_DEBUG_OBJECT (self, "Initial PAUSED - pre-rolled");
1620
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);
1627
1628       g_object_get (self->playbin, "video-sink", &video_sink, NULL);
1629
1630       if (video_sink) {
1631         video_sink_pad = gst_element_get_static_pad (video_sink, "sink");
1632
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);
1637         }
1638         gst_object_unref (video_sink);
1639       }
1640
1641       check_video_dimensions_changed (self);
1642       if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME,
1643               &duration)) {
1644         emit_duration_changed (self, duration);
1645       } else {
1646         self->cached_duration = GST_CLOCK_TIME_NONE;
1647       }
1648     }
1649
1650     if (new_state == GST_STATE_PAUSED
1651         && pending_state == GST_STATE_VOID_PENDING) {
1652       remove_tick_source (self);
1653
1654       g_mutex_lock (&self->lock);
1655       if (self->seek_pending) {
1656         self->seek_pending = FALSE;
1657
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);
1666         } else {
1667           GST_DEBUG_OBJECT (self, "Seek finished");
1668           emit_seek_done (self);
1669         }
1670       }
1671
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);
1678
1679         tick_cb (self);
1680
1681         if (self->target_state >= GST_STATE_PLAYING && self->buffering == 100) {
1682           GstStateChangeReturn state_ret;
1683
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);
1690         }
1691       } else {
1692         g_mutex_unlock (&self->lock);
1693       }
1694     } else if (new_state == GST_STATE_PLAYING
1695         && pending_state == GST_STATE_VOID_PENDING) {
1696
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);
1702       }
1703     } else if (new_state == GST_STATE_READY && old_state > GST_STATE_READY) {
1704       change_state (self, GST_PLAYER_STATE_STOPPED);
1705     } else {
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);
1709     }
1710   }
1711 }
1712
1713 static void
1714 duration_changed_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1715     gpointer user_data)
1716 {
1717   GstPlayer *self = GST_PLAYER (user_data);
1718   gint64 duration = GST_CLOCK_TIME_NONE;
1719
1720   if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME, &duration)) {
1721     emit_duration_changed (self, duration);
1722   }
1723 }
1724
1725 static void
1726 latency_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1727     gpointer user_data)
1728 {
1729   GstPlayer *self = GST_PLAYER (user_data);
1730
1731   GST_DEBUG_OBJECT (self, "Latency changed");
1732
1733   gst_bin_recalculate_latency (GST_BIN (self->playbin));
1734 }
1735
1736 static void
1737 request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1738     gpointer user_data)
1739 {
1740   GstPlayer *self = GST_PLAYER (user_data);
1741   GstState state;
1742   GstStateChangeReturn state_ret;
1743
1744   gst_message_parse_request_state (msg, &state);
1745
1746   GST_DEBUG_OBJECT (self, "State %s requested",
1747       gst_element_state_get_name (state));
1748
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)));
1755 }
1756
1757 static void
1758 media_info_update (GstPlayer * self, GstPlayerMediaInfo * info)
1759 {
1760   g_free (info->title);
1761   info->title = get_from_tags (self, info, get_title);
1762
1763   g_free (info->container);
1764   info->container = get_from_tags (self, info, get_container_format);
1765
1766   if (info->image_sample)
1767     gst_sample_unref (info->image_sample);
1768   info->image_sample = get_from_tags (self, info, get_cover_sample);
1769
1770   GST_DEBUG_OBJECT (self, "title: %s, container: %s "
1771       "image_sample: %p", info->title, info->container, info->image_sample);
1772 }
1773
1774 static void
1775 tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1776 {
1777   GstPlayer *self = GST_PLAYER (user_data);
1778   GstTagList *tags = NULL;
1779
1780   gst_message_parse_tag (msg, &tags);
1781
1782   GST_DEBUG_OBJECT (self, "received %s tags",
1783       gst_tag_list_get_scope (tags) ==
1784       GST_TAG_SCOPE_GLOBAL ? "global" : "stream");
1785
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);
1795     } else {
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);
1800     }
1801   }
1802
1803   gst_tag_list_unref (tags);
1804 }
1805
1806 static void
1807 element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1808 {
1809   GstPlayer *self = GST_PLAYER (user_data);
1810   const GstStructure *s;
1811
1812   s = gst_message_get_structure (msg);
1813   if (gst_structure_has_name (s, "redirect")) {
1814     const gchar *new_location;
1815
1816     new_location = gst_structure_get_string (s, "new-location");
1817     if (!new_location) {
1818       const GValue *locations_list, *location_val;
1819       guint i, size;
1820
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;
1825
1826         location_val = gst_value_list_get_value (locations_list, i);
1827         if (!GST_VALUE_HOLDS_STRUCTURE (location_val))
1828           continue;
1829
1830         location_s = (const GstStructure *) g_value_get_boxed (location_val);
1831         if (!gst_structure_has_name (location_s, "redirect"))
1832           continue;
1833
1834         new_location = gst_structure_get_string (location_s, "new-location");
1835         if (new_location)
1836           break;
1837       }
1838     }
1839
1840     if (new_location) {
1841       GstState target_state;
1842
1843       GST_DEBUG_OBJECT (self, "Redirect to '%s'", new_location);
1844
1845       /* Remember target state and restore after setting the URI */
1846       target_state = self->target_state;
1847
1848       gst_player_stop_internal (self, TRUE);
1849
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);
1855
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);
1860     }
1861   }
1862 }
1863
1864 /* Must be called with lock */
1865 static gboolean
1866 update_stream_collection (GstPlayer * self, GstStreamCollection * collection)
1867 {
1868   if (self->collection && self->collection == collection)
1869     return FALSE;
1870
1871   if (self->collection && self->stream_notify_id)
1872     g_signal_handler_disconnect (self->collection, self->stream_notify_id);
1873
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);
1879   }
1880
1881   self->stream_notify_id =
1882       g_signal_connect (self->collection, "stream-notify",
1883       G_CALLBACK (stream_notify_cb), self);
1884
1885   return TRUE;
1886 }
1887
1888 static void
1889 stream_collection_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1890     gpointer user_data)
1891 {
1892   GstPlayer *self = GST_PLAYER (user_data);
1893   GstStreamCollection *collection = NULL;
1894   gboolean updated = FALSE;
1895
1896   gst_message_parse_stream_collection (msg, &collection);
1897
1898   if (!collection)
1899     return;
1900
1901   g_mutex_lock (&self->lock);
1902   updated = update_stream_collection (self, collection);
1903   gst_object_unref (collection);
1904   g_mutex_unlock (&self->lock);
1905
1906   if (self->media_info && updated)
1907     emit_media_info_updated_signal (self);
1908 }
1909
1910 static void
1911 streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1912     gpointer user_data)
1913 {
1914   GstPlayer *self = GST_PLAYER (user_data);
1915   GstStreamCollection *collection = NULL;
1916   gboolean updated = FALSE;
1917   guint i, len;
1918
1919   gst_message_parse_streams_selected (msg, &collection);
1920
1921   if (!collection)
1922     return;
1923
1924   g_mutex_lock (&self->lock);
1925   updated = update_stream_collection (self, collection);
1926   gst_object_unref (collection);
1927
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;
1934
1935   len = gst_message_streams_selected_get_size (msg);
1936   for (i = 0; i < len; i++) {
1937     GstStream *stream;
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;
1950     else {
1951       GST_WARNING_OBJECT (self,
1952           "Unknown stream-id %s with type 0x%x", stream_id, stream_type);
1953       continue;
1954     }
1955
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));
1960       continue;
1961     }
1962
1963     *current_sid = g_strdup (stream_id);
1964   }
1965   g_mutex_unlock (&self->lock);
1966
1967   if (self->media_info && updated)
1968     emit_media_info_updated_signal (self);
1969 }
1970
1971 static void
1972 player_set_flag (GstPlayer * self, gint pos)
1973 {
1974   gint flags;
1975
1976   g_object_get (self->playbin, "flags", &flags, NULL);
1977   flags |= pos;
1978   g_object_set (self->playbin, "flags", flags, NULL);
1979
1980   GST_DEBUG_OBJECT (self, "setting flags=%#x", flags);
1981 }
1982
1983 static void
1984 player_clear_flag (GstPlayer * self, gint pos)
1985 {
1986   gint flags;
1987
1988   g_object_get (self->playbin, "flags", &flags, NULL);
1989   flags &= ~pos;
1990   g_object_set (self->playbin, "flags", flags, NULL);
1991
1992   GST_DEBUG_OBJECT (self, "setting flags=%#x", flags);
1993 }
1994
1995 typedef struct
1996 {
1997   GstPlayer *player;
1998   GstPlayerMediaInfo *info;
1999 } MediaInfoUpdatedSignalData;
2000
2001 static void
2002 media_info_updated_dispatch (gpointer user_data)
2003 {
2004   MediaInfoUpdatedSignalData *data = user_data;
2005
2006   if (data->player->inhibit_sigs)
2007     return;
2008
2009   if (data->player->target_state >= GST_STATE_PAUSED) {
2010     g_signal_emit (data->player, signals[SIGNAL_MEDIA_INFO_UPDATED], 0,
2011         data->info);
2012   }
2013 }
2014
2015 static void
2016 free_media_info_updated_signal_data (MediaInfoUpdatedSignalData * data)
2017 {
2018   g_object_unref (data->player);
2019   g_object_unref (data->info);
2020   g_free (data);
2021 }
2022
2023 /*
2024  * emit_media_info_updated_signal:
2025  *
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.
2029  */
2030 static void
2031 emit_media_info_updated_signal (GstPlayer * self)
2032 {
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);
2038
2039   gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
2040       media_info_updated_dispatch, data,
2041       (GDestroyNotify) free_media_info_updated_signal_data);
2042 }
2043
2044 static GstCaps *
2045 get_caps (GstPlayer * self, gint stream_index, GType type)
2046 {
2047   GstPad *pad = NULL;
2048   GstCaps *caps = NULL;
2049
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);
2056   else
2057     g_signal_emit_by_name (G_OBJECT (self->playbin),
2058         "get-text-pad", stream_index, &pad);
2059
2060   if (pad) {
2061     caps = gst_pad_get_current_caps (pad);
2062     gst_object_unref (pad);
2063   }
2064
2065   return caps;
2066 }
2067
2068 static void
2069 gst_player_subtitle_info_update (GstPlayer * self,
2070     GstPlayerStreamInfo * stream_info)
2071 {
2072   GstPlayerSubtitleInfo *info = (GstPlayerSubtitleInfo *) stream_info;
2073
2074   if (stream_info->tags) {
2075
2076     /* free the old language info */
2077     g_free (info->language);
2078     info->language = NULL;
2079
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.
2083      */
2084     gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME,
2085         &info->language);
2086     if (!info->language) {
2087       gchar *lang_code = NULL;
2088
2089       gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE,
2090           &lang_code);
2091       if (lang_code) {
2092         info->language = g_strdup (gst_tag_get_language_name (lang_code));
2093         g_free (lang_code);
2094       }
2095     }
2096
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.
2101      */
2102     if (!info->language) {
2103       gint text_index = -1;
2104       gchar *suburi = NULL;
2105
2106       g_object_get (G_OBJECT (self->playbin), "current-suburi", &suburi, NULL);
2107       if (suburi) {
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);
2111         } else {
2112           g_object_get (G_OBJECT (self->playbin), "current-text", &text_index,
2113               NULL);
2114           if (text_index == gst_player_stream_info_get_index (stream_info))
2115             info->language = g_path_get_basename (suburi);
2116         }
2117         g_free (suburi);
2118       }
2119     }
2120
2121   } else {
2122     g_free (info->language);
2123     info->language = NULL;
2124   }
2125
2126   GST_DEBUG_OBJECT (self, "language=%s", info->language);
2127 }
2128
2129 static void
2130 gst_player_video_info_update (GstPlayer * self,
2131     GstPlayerStreamInfo * stream_info)
2132 {
2133   GstPlayerVideoInfo *info = (GstPlayerVideoInfo *) stream_info;
2134
2135   if (stream_info->caps) {
2136     GstStructure *s;
2137
2138     s = gst_caps_get_structure (stream_info->caps, 0);
2139     if (s) {
2140       gint width, height;
2141       gint fps_n, fps_d;
2142       gint par_n, par_d;
2143
2144       if (gst_structure_get_int (s, "width", &width))
2145         info->width = width;
2146       else
2147         info->width = -1;
2148
2149       if (gst_structure_get_int (s, "height", &height))
2150         info->height = height;
2151       else
2152         info->height = -1;
2153
2154       if (gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
2155         info->framerate_num = fps_n;
2156         info->framerate_denom = fps_d;
2157       } else {
2158         info->framerate_num = 0;
2159         info->framerate_denom = 1;
2160       }
2161
2162
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;
2166       } else {
2167         info->par_num = 1;
2168         info->par_denom = 1;
2169       }
2170     }
2171   } else {
2172     info->width = info->height = -1;
2173     info->par_num = info->par_denom = 1;
2174     info->framerate_num = 0;
2175     info->framerate_denom = 1;
2176   }
2177
2178   if (stream_info->tags) {
2179     guint bitrate, max_bitrate;
2180
2181     if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate))
2182       info->bitrate = bitrate;
2183     else
2184       info->bitrate = -1;
2185
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;
2190     else
2191       info->max_bitrate = -1;
2192   } else {
2193     info->bitrate = info->max_bitrate = -1;
2194   }
2195
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);
2200 }
2201
2202 static void
2203 gst_player_audio_info_update (GstPlayer * self,
2204     GstPlayerStreamInfo * stream_info)
2205 {
2206   GstPlayerAudioInfo *info = (GstPlayerAudioInfo *) stream_info;
2207
2208   if (stream_info->caps) {
2209     GstStructure *s;
2210
2211     s = gst_caps_get_structure (stream_info->caps, 0);
2212     if (s) {
2213       gint rate, channels;
2214
2215       if (gst_structure_get_int (s, "rate", &rate))
2216         info->sample_rate = rate;
2217       else
2218         info->sample_rate = -1;
2219
2220       if (gst_structure_get_int (s, "channels", &channels))
2221         info->channels = channels;
2222       else
2223         info->channels = 0;
2224     }
2225   } else {
2226     info->sample_rate = -1;
2227     info->channels = 0;
2228   }
2229
2230   if (stream_info->tags) {
2231     guint bitrate, max_bitrate;
2232
2233     if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate))
2234       info->bitrate = bitrate;
2235     else
2236       info->bitrate = -1;
2237
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;
2242     else
2243       info->max_bitrate = -1;
2244
2245     /* if we have old language the free it */
2246     g_free (info->language);
2247     info->language = NULL;
2248
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.
2252      */
2253     gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME,
2254         &info->language);
2255     if (!info->language) {
2256       gchar *lang_code = NULL;
2257
2258       gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE,
2259           &lang_code);
2260       if (lang_code) {
2261         info->language = g_strdup (gst_tag_get_language_name (lang_code));
2262         g_free (lang_code);
2263       }
2264     }
2265   } else {
2266     g_free (info->language);
2267     info->language = NULL;
2268     info->max_bitrate = info->bitrate = -1;
2269   }
2270
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);
2274 }
2275
2276 static GstPlayerStreamInfo *
2277 gst_player_stream_info_find (GstPlayerMediaInfo * media_info,
2278     GType type, gint stream_index)
2279 {
2280   GList *list, *l;
2281   GstPlayerStreamInfo *info = NULL;
2282
2283   if (!media_info)
2284     return NULL;
2285
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)) {
2290       return info;
2291     }
2292   }
2293
2294   return NULL;
2295 }
2296
2297 static GstPlayerStreamInfo *
2298 gst_player_stream_info_find_from_stream_id (GstPlayerMediaInfo * media_info,
2299     const gchar * stream_id)
2300 {
2301   GList *list, *l;
2302   GstPlayerStreamInfo *info = NULL;
2303
2304   if (!media_info)
2305     return NULL;
2306
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)) {
2311       return info;
2312     }
2313   }
2314
2315   return NULL;
2316 }
2317
2318 static gboolean
2319 is_track_enabled (GstPlayer * self, gint pos)
2320 {
2321   gint flags;
2322
2323   g_object_get (G_OBJECT (self->playbin), "flags", &flags, NULL);
2324
2325   if ((flags & pos))
2326     return TRUE;
2327
2328   return FALSE;
2329 }
2330
2331 static GstPlayerStreamInfo *
2332 gst_player_stream_info_get_current (GstPlayer * self, const gchar * prop,
2333     GType type)
2334 {
2335   gint current;
2336   GstPlayerStreamInfo *info;
2337
2338   if (!self->media_info)
2339     return NULL;
2340
2341   g_object_get (G_OBJECT (self->playbin), prop, &current, NULL);
2342   g_mutex_lock (&self->lock);
2343   info = gst_player_stream_info_find (self->media_info, type, current);
2344   if (info)
2345     info = gst_player_stream_info_copy (info);
2346   g_mutex_unlock (&self->lock);
2347
2348   return info;
2349 }
2350
2351 static GstPlayerStreamInfo *
2352 gst_player_stream_info_get_current_from_stream_id (GstPlayer * self,
2353     const gchar * stream_id, GType type)
2354 {
2355   GstPlayerStreamInfo *info;
2356
2357   if (!self->media_info || !stream_id)
2358     return NULL;
2359
2360   g_mutex_lock (&self->lock);
2361   info =
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);
2365   else
2366     info = NULL;
2367   g_mutex_unlock (&self->lock);
2368
2369   return info;
2370 }
2371
2372 static void
2373 stream_notify_cb (GstStreamCollection * collection, GstStream * stream,
2374     GParamSpec * pspec, GstPlayer * self)
2375 {
2376   GstPlayerStreamInfo *info;
2377   const gchar *stream_id;
2378   gboolean emit_signal = FALSE;
2379
2380   if (!self->media_info)
2381     return;
2382
2383   if (G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_CAPS &&
2384       G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_TAG_LIST)
2385     return;
2386
2387   stream_id = gst_stream_get_stream_id (stream);
2388   g_mutex_lock (&self->lock);
2389   info =
2390       gst_player_stream_info_find_from_stream_id (self->media_info, stream_id);
2391   if (info) {
2392     gst_player_stream_info_update_from_stream (self, info, stream);
2393     emit_signal = TRUE;
2394   }
2395   g_mutex_unlock (&self->lock);
2396
2397   if (emit_signal)
2398     emit_media_info_updated_signal (self);
2399 }
2400
2401 static void
2402 gst_player_stream_info_update (GstPlayer * self, GstPlayerStreamInfo * s)
2403 {
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);
2408   else
2409     gst_player_subtitle_info_update (self, s);
2410 }
2411
2412 static gchar *
2413 stream_info_get_codec (GstPlayerStreamInfo * s)
2414 {
2415   const gchar *type;
2416   GstTagList *tags;
2417   gchar *codec = NULL;
2418
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;
2423   else
2424     type = GST_TAG_SUBTITLE_CODEC;
2425
2426   tags = gst_player_stream_info_get_tags (s);
2427   if (tags) {
2428     gst_tag_list_get_string (tags, type, &codec);
2429     if (!codec)
2430       gst_tag_list_get_string (tags, GST_TAG_CODEC, &codec);
2431   }
2432
2433   if (!codec) {
2434     GstCaps *caps;
2435     caps = gst_player_stream_info_get_caps (s);
2436     if (caps) {
2437       codec = gst_pb_utils_get_codec_description (caps);
2438     }
2439   }
2440
2441   return codec;
2442 }
2443
2444 static void
2445 gst_player_stream_info_update_tags_and_caps (GstPlayer * self,
2446     GstPlayerStreamInfo * s)
2447 {
2448   GstTagList *tags;
2449   gint stream_index;
2450
2451   stream_index = gst_player_stream_info_get_index (s);
2452
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);
2459   else
2460     g_signal_emit_by_name (self->playbin, "get-text-tags", stream_index, &tags);
2461
2462   if (s->tags)
2463     gst_tag_list_unref (s->tags);
2464   s->tags = tags;
2465
2466   if (s->caps)
2467     gst_caps_unref (s->caps);
2468   s->caps = get_caps (self, stream_index, G_OBJECT_TYPE (s));
2469
2470   g_free (s->codec);
2471   s->codec = stream_info_get_codec (s);
2472
2473   GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p",
2474       gst_player_stream_info_get_stream_type (s), stream_index,
2475       s->tags, s->caps);
2476
2477   gst_player_stream_info_update (self, s);
2478 }
2479
2480 static void
2481 gst_player_streams_info_create (GstPlayer * self,
2482     GstPlayerMediaInfo * media_info, const gchar * prop, GType type)
2483 {
2484   gint i;
2485   gint total = -1;
2486   GstPlayerStreamInfo *s;
2487
2488   if (!media_info)
2489     return;
2490
2491   g_object_get (G_OBJECT (self->playbin), prop, &total, NULL);
2492
2493   GST_DEBUG_OBJECT (self, "%s: %d", prop, total);
2494
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);
2498
2499     if (!s) {
2500       /* create a new stream info instance */
2501       s = gst_player_stream_info_new (i, type);
2502
2503       /* add the object in stream list */
2504       media_info->stream_list = g_list_append (media_info->stream_list, s);
2505
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);
2513       else
2514         media_info->subtitle_stream_list = g_list_append
2515             (media_info->subtitle_stream_list, s);
2516
2517       GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d",
2518           gst_player_stream_info_get_stream_type (s), i);
2519     }
2520
2521     gst_player_stream_info_update_tags_and_caps (self, s);
2522   }
2523 }
2524
2525 static void
2526 gst_player_stream_info_update_from_stream (GstPlayer * self,
2527     GstPlayerStreamInfo * s, GstStream * stream)
2528 {
2529   if (s->tags)
2530     gst_tag_list_unref (s->tags);
2531   s->tags = gst_stream_get_tags (stream);
2532
2533   if (s->caps)
2534     gst_caps_unref (s->caps);
2535   s->caps = gst_stream_get_caps (stream);
2536
2537   g_free (s->codec);
2538   s->codec = stream_info_get_codec (s);
2539
2540   GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p",
2541       gst_player_stream_info_get_stream_type (s), s->stream_index,
2542       s->tags, s->caps);
2543
2544   gst_player_stream_info_update (self, s);
2545 }
2546
2547 static void
2548 gst_player_streams_info_create_from_collection (GstPlayer * self,
2549     GstPlayerMediaInfo * media_info, GstStreamCollection * collection)
2550 {
2551   guint i;
2552   guint total;
2553   GstPlayerStreamInfo *s;
2554   guint n_audio = 0;
2555   guint n_video = 0;
2556   guint n_text = 0;
2557
2558   if (!media_info || !collection)
2559     return;
2560
2561   total = gst_stream_collection_get_size (collection);
2562
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);
2567
2568     if (stream_type & GST_STREAM_TYPE_AUDIO) {
2569       s = gst_player_stream_info_new (n_audio, GST_TYPE_PLAYER_AUDIO_INFO);
2570       n_audio++;
2571     } else if (stream_type & GST_STREAM_TYPE_VIDEO) {
2572       s = gst_player_stream_info_new (n_video, GST_TYPE_PLAYER_VIDEO_INFO);
2573       n_video++;
2574     } else if (stream_type & GST_STREAM_TYPE_TEXT) {
2575       s = gst_player_stream_info_new (n_text, GST_TYPE_PLAYER_SUBTITLE_INFO);
2576       n_text++;
2577     } else {
2578       GST_DEBUG_OBJECT (self, "Unknown type stream %d", i);
2579       continue;
2580     }
2581
2582     s->stream_id = g_strdup (stream_id);
2583
2584     /* add the object in stream list */
2585     media_info->stream_list = g_list_append (media_info->stream_list, s);
2586
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);
2594     else
2595       media_info->subtitle_stream_list = g_list_append
2596           (media_info->subtitle_stream_list, s);
2597
2598     GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d",
2599         gst_player_stream_info_get_stream_type (s), s->stream_index);
2600
2601     gst_player_stream_info_update_from_stream (self, s, stream);
2602   }
2603 }
2604
2605 static void
2606 video_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2607 {
2608   GstPlayer *self = GST_PLAYER (user_data);
2609
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);
2614 }
2615
2616 static void
2617 audio_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2618 {
2619   GstPlayer *self = GST_PLAYER (user_data);
2620
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);
2625 }
2626
2627 static void
2628 subtitle_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2629 {
2630   GstPlayer *self = GST_PLAYER (user_data);
2631
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);
2636 }
2637
2638 static void *
2639 get_title (GstTagList * tags)
2640 {
2641   gchar *title = NULL;
2642
2643   gst_tag_list_get_string (tags, GST_TAG_TITLE, &title);
2644   if (!title)
2645     gst_tag_list_get_string (tags, GST_TAG_TITLE_SORTNAME, &title);
2646
2647   return title;
2648 }
2649
2650 static void *
2651 get_container_format (GstTagList * tags)
2652 {
2653   gchar *container = NULL;
2654
2655   gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container);
2656
2657   /* TODO: If container is not available then maybe consider
2658    * parsing caps or file extension to guess the container format.
2659    */
2660
2661   return container;
2662 }
2663
2664 static void *
2665 get_from_tags (GstPlayer * self, GstPlayerMediaInfo * media_info,
2666     void *(*func) (GstTagList *))
2667 {
2668   GList *l;
2669   void *ret = NULL;
2670
2671   if (media_info->tags) {
2672     ret = func (media_info->tags);
2673     if (ret)
2674       return ret;
2675   }
2676
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;
2680       l = l->next) {
2681     GstTagList *tags;
2682
2683     tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data);
2684     if (tags)
2685       ret = func (tags);
2686
2687     if (ret)
2688       return ret;
2689   }
2690
2691   GST_DEBUG_OBJECT (self, "trying audio tags");
2692   for (l = gst_player_media_info_get_audio_streams (media_info); l != NULL;
2693       l = l->next) {
2694     GstTagList *tags;
2695
2696     tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data);
2697     if (tags)
2698       ret = func (tags);
2699
2700     if (ret)
2701       return ret;
2702   }
2703
2704   GST_DEBUG_OBJECT (self, "failed to get the information from tags");
2705   return NULL;
2706 }
2707
2708 static void *
2709 get_cover_sample (GstTagList * tags)
2710 {
2711   GstSample *cover_sample = NULL;
2712
2713   gst_tag_list_get_sample (tags, GST_TAG_IMAGE, &cover_sample);
2714   if (!cover_sample)
2715     gst_tag_list_get_sample (tags, GST_TAG_PREVIEW_IMAGE, &cover_sample);
2716
2717   return cover_sample;
2718 }
2719
2720 static GstPlayerMediaInfo *
2721 gst_player_media_info_create (GstPlayer * self)
2722 {
2723   GstPlayerMediaInfo *media_info;
2724   GstQuery *query;
2725
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;
2732
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);
2737
2738   if (self->use_playbin3 && self->collection) {
2739     gst_player_streams_info_create_from_collection (self, media_info,
2740         self->collection);
2741   } else {
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);
2749   }
2750
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);
2755
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);
2761
2762   GST_DEBUG_OBJECT (self, "end");
2763   return media_info;
2764 }
2765
2766 static void
2767 tags_changed_cb (GstPlayer * self, gint stream_index, GType type)
2768 {
2769   GstPlayerStreamInfo *s;
2770
2771   if (!self->media_info)
2772     return;
2773
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);
2779
2780   emit_media_info_updated_signal (self);
2781 }
2782
2783 static void
2784 video_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2785     gpointer user_data)
2786 {
2787   tags_changed_cb (GST_PLAYER (user_data), stream_index,
2788       GST_TYPE_PLAYER_VIDEO_INFO);
2789 }
2790
2791 static void
2792 audio_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2793     gpointer user_data)
2794 {
2795   tags_changed_cb (GST_PLAYER (user_data), stream_index,
2796       GST_TYPE_PLAYER_AUDIO_INFO);
2797 }
2798
2799 static void
2800 subtitle_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2801     gpointer user_data)
2802 {
2803   tags_changed_cb (GST_PLAYER (user_data), stream_index,
2804       GST_TYPE_PLAYER_SUBTITLE_INFO);
2805 }
2806
2807 static void
2808 volume_changed_dispatch (gpointer user_data)
2809 {
2810   GstPlayer *player = user_data;
2811
2812   if (player->inhibit_sigs)
2813     return;
2814
2815   g_signal_emit (player, signals[SIGNAL_VOLUME_CHANGED], 0);
2816   g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_VOLUME]);
2817 }
2818
2819 static void
2820 volume_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
2821     GstPlayer * self)
2822 {
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);
2828   }
2829 }
2830
2831 static void
2832 mute_changed_dispatch (gpointer user_data)
2833 {
2834   GstPlayer *player = user_data;
2835
2836   if (player->inhibit_sigs)
2837     return;
2838
2839   g_signal_emit (player, signals[SIGNAL_MUTE_CHANGED], 0);
2840   g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_MUTE]);
2841 }
2842
2843 static void
2844 mute_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
2845     GstPlayer * self)
2846 {
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);
2852   }
2853 }
2854
2855 static void
2856 source_setup_cb (GstElement * playbin, GstElement * source, GstPlayer * self)
2857 {
2858   gchar *user_agent;
2859
2860   user_agent = gst_player_config_get_user_agent (self->config);
2861   if (user_agent) {
2862     GParamSpec *prop;
2863
2864     prop = g_object_class_find_property (G_OBJECT_GET_CLASS (source),
2865         "user-agent");
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);
2869     }
2870
2871     g_free (user_agent);
2872   }
2873 }
2874
2875 static gpointer
2876 gst_player_main (gpointer data)
2877 {
2878   GstPlayer *self = GST_PLAYER (data);
2879   GstBus *bus;
2880   GSource *source;
2881   GSource *bus_source;
2882   GstElement *scaletempo;
2883   const gchar *env;
2884
2885   GST_TRACE_OBJECT (self, "Starting main thread");
2886
2887   g_main_context_push_thread_default (self->context);
2888
2889   source = g_idle_source_new ();
2890   g_source_set_callback (source, (GSourceFunc) main_loop_running_cb, self,
2891       NULL);
2892   g_source_attach (source, self->context);
2893   g_source_unref (source);
2894
2895   env = g_getenv ("GST_PLAYER_USE_PLAYBIN3");
2896   if (env && g_str_has_prefix (env, "1"))
2897     self->use_playbin3 = TRUE;
2898
2899   if (self->use_playbin3) {
2900     GST_DEBUG_OBJECT (self, "playbin3 enabled");
2901     self->playbin = gst_element_factory_make ("playbin3", "playbin3");
2902   } else {
2903     self->playbin = gst_element_factory_make ("playbin", "playbin");
2904   }
2905
2906   if (!self->playbin) {
2907     g_error ("GstPlayer: 'playbin' element not found, please check your setup");
2908     g_assert_not_reached ();
2909   }
2910
2911   if (self->video_renderer) {
2912     GstElement *video_sink =
2913         gst_player_video_renderer_create_video_sink (self->video_renderer,
2914         self);
2915
2916     if (video_sink)
2917       g_object_set (self->playbin, "video-sink", video_sink, NULL);
2918   }
2919
2920   scaletempo = gst_element_factory_make ("scaletempo", NULL);
2921   if (scaletempo) {
2922     g_object_set (self->playbin, "audio-filter", scaletempo, NULL);
2923   } else {
2924     g_warning ("GstPlayer: scaletempo element not available. Audio pitch "
2925         "will not be preserved during trick modes");
2926   }
2927
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,
2931       NULL, NULL);
2932   g_source_attach (bus_source, self->context);
2933
2934   g_signal_connect (G_OBJECT (bus), "message::error", G_CALLBACK (error_cb),
2935       self);
2936   g_signal_connect (G_OBJECT (bus), "message::warning", G_CALLBACK (warning_cb),
2937       self);
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);
2954
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);
2960   } else {
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);
2967
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);
2974   }
2975
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);
2982
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;
2989   self->rate = 1.0;
2990
2991   GST_TRACE_OBJECT (self, "Starting main loop");
2992   g_main_loop_run (self->loop);
2993   GST_TRACE_OBJECT (self, "Stopped main loop");
2994
2995   g_source_destroy (bus_source);
2996   g_source_unref (bus_source);
2997   gst_object_unref (bus);
2998
2999   remove_tick_source (self);
3000   remove_ready_timeout_source (self);
3001
3002   g_mutex_lock (&self->lock);
3003   if (self->media_info) {
3004     g_object_unref (self->media_info);
3005     self->media_info = NULL;
3006   }
3007
3008   remove_seek_source (self);
3009   g_mutex_unlock (&self->lock);
3010
3011   g_main_context_pop_thread_default (self->context);
3012
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;
3019   }
3020
3021   GST_TRACE_OBJECT (self, "Stopped main thread");
3022
3023   return NULL;
3024 }
3025
3026 static gpointer
3027 gst_player_init_once (G_GNUC_UNUSED gpointer user_data)
3028 {
3029   gst_init (NULL, NULL);
3030
3031   GST_DEBUG_CATEGORY_INIT (gst_player_debug, "gst-player", 0, "GstPlayer");
3032   gst_player_error_quark ();
3033
3034   return NULL;
3035 }
3036
3037 /**
3038  * gst_player_new:
3039  * @video_renderer: (transfer full) (allow-none): GstPlayerVideoRenderer to use
3040  * @signal_dispatcher: (transfer full) (allow-none): GstPlayerSignalDispatcher to use
3041  *
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().
3045  *
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
3048  * performed.
3049  *
3050  * Returns: (transfer full): a new #GstPlayer instance
3051  */
3052 GstPlayer *
3053 gst_player_new (GstPlayerVideoRenderer * video_renderer,
3054     GstPlayerSignalDispatcher * signal_dispatcher)
3055 {
3056   static GOnce once = G_ONCE_INIT;
3057   GstPlayer *self;
3058
3059   g_once (&once, gst_player_init_once, NULL);
3060
3061   self =
3062       g_object_new (GST_TYPE_PLAYER, "video-renderer", video_renderer,
3063       "signal-dispatcher", signal_dispatcher, NULL);
3064   gst_object_ref_sink (self);
3065
3066   if (video_renderer)
3067     g_object_unref (video_renderer);
3068   if (signal_dispatcher)
3069     g_object_unref (signal_dispatcher);
3070
3071   return self;
3072 }
3073
3074 static gboolean
3075 gst_player_play_internal (gpointer user_data)
3076 {
3077   GstPlayer *self = GST_PLAYER (user_data);
3078   GstStateChangeReturn state_ret;
3079
3080   GST_DEBUG_OBJECT (self, "Play");
3081
3082   g_mutex_lock (&self->lock);
3083   if (!self->uri) {
3084     g_mutex_unlock (&self->lock);
3085     return G_SOURCE_REMOVE;
3086   }
3087   g_mutex_unlock (&self->lock);
3088
3089   remove_ready_timeout_source (self);
3090   self->target_state = GST_STATE_PLAYING;
3091
3092   if (self->current_state < GST_STATE_PAUSED)
3093     change_state (self, GST_PLAYER_STATE_BUFFERING);
3094
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);
3099   } else {
3100     state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
3101   }
3102
3103   if (state_ret == GST_STATE_CHANGE_FAILURE) {
3104     emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3105             "Failed to play"));
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");
3110   }
3111
3112   if (self->is_eos) {
3113     gboolean ret;
3114
3115     GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning");
3116     self->is_eos = FALSE;
3117     ret =
3118         gst_element_seek_simple (self->playbin, GST_FORMAT_TIME,
3119         GST_SEEK_FLAG_FLUSH, 0);
3120     if (!ret) {
3121       GST_ERROR_OBJECT (self, "Seek to beginning failed");
3122       gst_player_stop_internal (self, TRUE);
3123       gst_player_play_internal (self);
3124     }
3125   }
3126
3127   return G_SOURCE_REMOVE;
3128 }
3129
3130 /**
3131  * gst_player_play:
3132  * @player: #GstPlayer instance
3133  *
3134  * Request to play the loaded stream.
3135  */
3136 void
3137 gst_player_play (GstPlayer * self)
3138 {
3139   g_return_if_fail (GST_IS_PLAYER (self));
3140
3141   g_mutex_lock (&self->lock);
3142   self->inhibit_sigs = FALSE;
3143   g_mutex_unlock (&self->lock);
3144
3145   g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3146       gst_player_play_internal, self, NULL);
3147 }
3148
3149 static gboolean
3150 gst_player_pause_internal (gpointer user_data)
3151 {
3152   GstPlayer *self = GST_PLAYER (user_data);
3153   GstStateChangeReturn state_ret;
3154
3155   GST_DEBUG_OBJECT (self, "Pause");
3156
3157   g_mutex_lock (&self->lock);
3158   if (!self->uri) {
3159     g_mutex_unlock (&self->lock);
3160     return G_SOURCE_REMOVE;
3161   }
3162   g_mutex_unlock (&self->lock);
3163
3164   tick_cb (self);
3165   remove_tick_source (self);
3166   remove_ready_timeout_source (self);
3167
3168   self->target_state = GST_STATE_PAUSED;
3169
3170   if (self->current_state < GST_STATE_PAUSED)
3171     change_state (self, GST_PLAYER_STATE_BUFFERING);
3172
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");
3181   }
3182
3183   if (self->is_eos) {
3184     gboolean ret;
3185
3186     GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning");
3187     self->is_eos = FALSE;
3188     ret =
3189         gst_element_seek_simple (self->playbin, GST_FORMAT_TIME,
3190         GST_SEEK_FLAG_FLUSH, 0);
3191     if (!ret) {
3192       GST_ERROR_OBJECT (self, "Seek to beginning failed");
3193       gst_player_stop_internal (self, TRUE);
3194       gst_player_pause_internal (self);
3195     }
3196   }
3197
3198   return G_SOURCE_REMOVE;
3199 }
3200
3201 /**
3202  * gst_player_pause:
3203  * @player: #GstPlayer instance
3204  *
3205  * Pauses the current stream.
3206  */
3207 void
3208 gst_player_pause (GstPlayer * self)
3209 {
3210   g_return_if_fail (GST_IS_PLAYER (self));
3211
3212   g_mutex_lock (&self->lock);
3213   self->inhibit_sigs = FALSE;
3214   g_mutex_unlock (&self->lock);
3215
3216   g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3217       gst_player_pause_internal, self, NULL);
3218 }
3219
3220 static void
3221 gst_player_stop_internal (GstPlayer * self, gboolean transient)
3222 {
3223   /* directly return if we're already stopped */
3224   if (self->current_state <= GST_STATE_READY &&
3225       self->target_state <= GST_STATE_READY)
3226     return;
3227
3228   GST_DEBUG_OBJECT (self, "Stop (transient %d)", transient);
3229
3230   tick_cb (self);
3231   remove_tick_source (self);
3232
3233   add_ready_timeout_source (self);
3234
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;
3252   }
3253   if (self->global_tags) {
3254     gst_tag_list_unref (self->global_tags);
3255     self->global_tags = NULL;
3256   }
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;
3261   self->rate = 1.0;
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;
3268   }
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);
3276 }
3277
3278 static gboolean
3279 gst_player_stop_internal_dispatch (gpointer user_data)
3280 {
3281   GstPlayer *self = GST_PLAYER (user_data);
3282
3283   gst_player_stop_internal (self, FALSE);
3284
3285   return G_SOURCE_REMOVE;
3286 }
3287
3288
3289 /**
3290  * gst_player_stop:
3291  * @player: #GstPlayer instance
3292  *
3293  * Stops playing the current stream and resets to the first position
3294  * in the stream.
3295  */
3296 void
3297 gst_player_stop (GstPlayer * self)
3298 {
3299   g_return_if_fail (GST_IS_PLAYER (self));
3300
3301   g_mutex_lock (&self->lock);
3302   self->inhibit_sigs = TRUE;
3303   g_mutex_unlock (&self->lock);
3304
3305   g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3306       gst_player_stop_internal_dispatch, self, NULL);
3307 }
3308
3309 /* Must be called with lock from main context, releases lock! */
3310 static void
3311 gst_player_seek_internal_locked (GstPlayer * self)
3312 {
3313   gboolean ret;
3314   GstClockTime position;
3315   gdouble rate;
3316   GstStateChangeReturn state_ret;
3317   GstEvent *s_event;
3318   GstSeekFlags flags = 0;
3319   gboolean accurate = FALSE;
3320
3321   remove_seek_source (self);
3322
3323   /* Only seek in PAUSED */
3324   if (self->current_state < GST_STATE_PAUSED) {
3325     return;
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,
3331               "Failed to seek"));
3332       g_mutex_lock (&self->lock);
3333       return;
3334     }
3335     g_mutex_lock (&self->lock);
3336     return;
3337   }
3338
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;
3343   rate = self->rate;
3344   g_mutex_unlock (&self->lock);
3345
3346   remove_tick_source (self);
3347   self->is_eos = FALSE;
3348
3349   flags |= GST_SEEK_FLAG_FLUSH;
3350
3351   accurate = gst_player_config_get_seek_accurate (self->config);
3352
3353   if (accurate) {
3354     flags |= GST_SEEK_FLAG_ACCURATE;
3355   } else {
3356     flags &= ~GST_SEEK_FLAG_ACCURATE;
3357   }
3358
3359   if (rate != 1.0) {
3360     flags |= GST_SEEK_FLAG_TRICKMODE;
3361   }
3362
3363   if (rate >= 0.0) {
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);
3366   } else {
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);
3369   }
3370
3371   GST_DEBUG_OBJECT (self, "Seek with rate %.2lf to %" GST_TIME_FORMAT,
3372       rate, GST_TIME_ARGS (position));
3373
3374   ret = gst_element_send_event (self->playbin, s_event);
3375   if (!ret)
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)));
3378
3379   g_mutex_lock (&self->lock);
3380 }
3381
3382 static gboolean
3383 gst_player_seek_internal (gpointer user_data)
3384 {
3385   GstPlayer *self = GST_PLAYER (user_data);
3386
3387   g_mutex_lock (&self->lock);
3388   gst_player_seek_internal_locked (self);
3389   g_mutex_unlock (&self->lock);
3390
3391   return G_SOURCE_REMOVE;
3392 }
3393
3394 /**
3395  * gst_player_set_rate:
3396  * @player: #GstPlayer instance
3397  * @rate: playback rate
3398  *
3399  * Playback at specified rate
3400  */
3401 void
3402 gst_player_set_rate (GstPlayer * self, gdouble rate)
3403 {
3404   g_return_if_fail (GST_IS_PLAYER (self));
3405   g_return_if_fail (rate != 0.0);
3406
3407   g_object_set (self, "rate", rate, NULL);
3408 }
3409
3410 /**
3411  * gst_player_get_rate:
3412  * @player: #GstPlayer instance
3413  *
3414  * Returns: current playback rate
3415  */
3416 gdouble
3417 gst_player_get_rate (GstPlayer * self)
3418 {
3419   gdouble val;
3420
3421   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_RATE);
3422
3423   g_object_get (self, "rate", &val, NULL);
3424
3425   return val;
3426 }
3427
3428 /**
3429  * gst_player_seek:
3430  * @player: #GstPlayer instance
3431  * @position: position to seek in nanoseconds
3432  *
3433  * Seeks the currently-playing stream to the absolute @position time
3434  * in nanoseconds.
3435  */
3436 void
3437 gst_player_seek (GstPlayer * self, GstClockTime position)
3438 {
3439   g_return_if_fail (GST_IS_PLAYER (self));
3440   g_return_if_fail (GST_CLOCK_TIME_IS_VALID (position));
3441
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);
3446     return;
3447   }
3448
3449   self->seek_position = position;
3450
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.
3454    */
3455   if (!self->seek_source) {
3456     GstClockTime now = gst_util_get_timestamp ();
3457
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);
3467     } else {
3468       guint delay = 250000 - (now - self->last_seek_time) / 1000;
3469
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);
3475
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);
3480     }
3481   }
3482   g_mutex_unlock (&self->lock);
3483 }
3484
3485 static void
3486 remove_seek_source (GstPlayer * self)
3487 {
3488   if (!self->seek_source)
3489     return;
3490
3491   g_source_destroy (self->seek_source);
3492   g_source_unref (self->seek_source);
3493   self->seek_source = NULL;
3494 }
3495
3496 /**
3497  * gst_player_get_uri:
3498  * @player: #GstPlayer instance
3499  *
3500  * Gets the URI of the currently-playing stream.
3501  *
3502  * Returns: (transfer full): a string containing the URI of the
3503  * currently-playing stream. g_free() after usage.
3504  */
3505 gchar *
3506 gst_player_get_uri (GstPlayer * self)
3507 {
3508   gchar *val;
3509
3510   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_URI);
3511
3512   g_object_get (self, "uri", &val, NULL);
3513
3514   return val;
3515 }
3516
3517 /**
3518  * gst_player_set_uri:
3519  * @player: #GstPlayer instance
3520  * @uri: next URI to play.
3521  *
3522  * Sets the next URI to play.
3523  */
3524 void
3525 gst_player_set_uri (GstPlayer * self, const gchar * val)
3526 {
3527   g_return_if_fail (GST_IS_PLAYER (self));
3528
3529   g_object_set (self, "uri", val, NULL);
3530 }
3531
3532 /**
3533  * gst_player_set_subtitle_uri:
3534  * @player: #GstPlayer instance
3535  * @uri: subtitle URI
3536  *
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
3539  * rendered.
3540  */
3541 void
3542 gst_player_set_subtitle_uri (GstPlayer * self, const gchar * suburi)
3543 {
3544   g_return_if_fail (GST_IS_PLAYER (self));
3545
3546   g_object_set (self, "suburi", suburi, NULL);
3547 }
3548
3549 /**
3550  * gst_player_get_subtitle_uri:
3551  * @player: #GstPlayer instance
3552  *
3553  * current subtitle URI
3554  *
3555  * Returns: (transfer full): URI of the current external subtitle.
3556  *   g_free() after usage.
3557  */
3558 gchar *
3559 gst_player_get_subtitle_uri (GstPlayer * self)
3560 {
3561   gchar *val = NULL;
3562
3563   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3564
3565   g_object_get (self, "suburi", &val, NULL);
3566
3567   return val;
3568 }
3569
3570 /**
3571  * gst_player_get_position:
3572  * @player: #GstPlayer instance
3573  *
3574  * Returns: the absolute position time, in nanoseconds, of the
3575  * currently-playing stream.
3576  */
3577 GstClockTime
3578 gst_player_get_position (GstPlayer * self)
3579 {
3580   GstClockTime val;
3581
3582   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_POSITION);
3583
3584   g_object_get (self, "position", &val, NULL);
3585
3586   return val;
3587 }
3588
3589 /**
3590  * gst_player_get_duration:
3591  * @player: #GstPlayer instance
3592  *
3593  * Retrieves the duration of the media stream that self represents.
3594  *
3595  * Returns: the duration of the currently-playing media stream, in
3596  * nanoseconds.
3597  */
3598 GstClockTime
3599 gst_player_get_duration (GstPlayer * self)
3600 {
3601   GstClockTime val;
3602
3603   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_DURATION);
3604
3605   g_object_get (self, "duration", &val, NULL);
3606
3607   return val;
3608 }
3609
3610 /**
3611  * gst_player_get_volume:
3612  * @player: #GstPlayer instance
3613  *
3614  * Returns the current volume level, as a percentage between 0 and 1.
3615  *
3616  * Returns: the volume as percentage between 0 and 1.
3617  */
3618 gdouble
3619 gst_player_get_volume (GstPlayer * self)
3620 {
3621   gdouble val;
3622
3623   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_VOLUME);
3624
3625   g_object_get (self, "volume", &val, NULL);
3626
3627   return val;
3628 }
3629
3630 /**
3631  * gst_player_set_volume:
3632  * @player: #GstPlayer instance
3633  * @val: the new volume level, as a percentage between 0 and 1
3634  *
3635  * Sets the volume level of the stream as a percentage between 0 and 1.
3636  */
3637 void
3638 gst_player_set_volume (GstPlayer * self, gdouble val)
3639 {
3640   g_return_if_fail (GST_IS_PLAYER (self));
3641
3642   g_object_set (self, "volume", val, NULL);
3643 }
3644
3645 /**
3646  * gst_player_get_mute:
3647  * @player: #GstPlayer instance
3648  *
3649  * Returns: %TRUE if the currently-playing stream is muted.
3650  */
3651 gboolean
3652 gst_player_get_mute (GstPlayer * self)
3653 {
3654   gboolean val;
3655
3656   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_MUTE);
3657
3658   g_object_get (self, "mute", &val, NULL);
3659
3660   return val;
3661 }
3662
3663 /**
3664  * gst_player_set_mute:
3665  * @player: #GstPlayer instance
3666  * @val: Mute state the should be set
3667  *
3668  * %TRUE if the currently-playing stream should be muted.
3669  */
3670 void
3671 gst_player_set_mute (GstPlayer * self, gboolean val)
3672 {
3673   g_return_if_fail (GST_IS_PLAYER (self));
3674
3675   g_object_set (self, "mute", val, NULL);
3676 }
3677
3678 /**
3679  * gst_player_get_pipeline:
3680  * @player: #GstPlayer instance
3681  *
3682  * Returns: (transfer full): The internal playbin instance
3683  */
3684 GstElement *
3685 gst_player_get_pipeline (GstPlayer * self)
3686 {
3687   GstElement *val;
3688
3689   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3690
3691   g_object_get (self, "pipeline", &val, NULL);
3692
3693   return val;
3694 }
3695
3696 /**
3697  * gst_player_get_media_info:
3698  * @player: #GstPlayer instance
3699  *
3700  * A Function to get the current media info #GstPlayerMediaInfo instance.
3701  *
3702  * Returns: (transfer full): media info instance.
3703  *
3704  * The caller should free it with g_object_unref()
3705  */
3706 GstPlayerMediaInfo *
3707 gst_player_get_media_info (GstPlayer * self)
3708 {
3709   GstPlayerMediaInfo *info;
3710
3711   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3712
3713   if (!self->media_info)
3714     return NULL;
3715
3716   g_mutex_lock (&self->lock);
3717   info = gst_player_media_info_copy (self->media_info);
3718   g_mutex_unlock (&self->lock);
3719
3720   return info;
3721 }
3722
3723 /**
3724  * gst_player_get_current_audio_track:
3725  * @player: #GstPlayer instance
3726  *
3727  * A Function to get current audio #GstPlayerAudioInfo instance.
3728  *
3729  * Returns: (transfer full): current audio track.
3730  *
3731  * The caller should free it with g_object_unref()
3732  */
3733 GstPlayerAudioInfo *
3734 gst_player_get_current_audio_track (GstPlayer * self)
3735 {
3736   GstPlayerAudioInfo *info;
3737
3738   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3739
3740   if (!is_track_enabled (self, GST_PLAY_FLAG_AUDIO))
3741     return NULL;
3742
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);
3747   } else {
3748     info = (GstPlayerAudioInfo *) gst_player_stream_info_get_current (self,
3749         "current-audio", GST_TYPE_PLAYER_AUDIO_INFO);
3750   }
3751
3752   return info;
3753 }
3754
3755 /**
3756  * gst_player_get_current_video_track:
3757  * @player: #GstPlayer instance
3758  *
3759  * A Function to get current video #GstPlayerVideoInfo instance.
3760  *
3761  * Returns: (transfer full): current video track.
3762  *
3763  * The caller should free it with g_object_unref()
3764  */
3765 GstPlayerVideoInfo *
3766 gst_player_get_current_video_track (GstPlayer * self)
3767 {
3768   GstPlayerVideoInfo *info;
3769
3770   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3771
3772   if (!is_track_enabled (self, GST_PLAY_FLAG_VIDEO))
3773     return NULL;
3774
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);
3779   } else {
3780     info = (GstPlayerVideoInfo *) gst_player_stream_info_get_current (self,
3781         "current-video", GST_TYPE_PLAYER_VIDEO_INFO);
3782   }
3783
3784   return info;
3785 }
3786
3787 /**
3788  * gst_player_get_current_subtitle_track:
3789  * @player: #GstPlayer instance
3790  *
3791  * A Function to get current subtitle #GstPlayerSubtitleInfo instance.
3792  *
3793  * Returns: (transfer none): current subtitle track.
3794  *
3795  * The caller should free it with g_object_unref()
3796  */
3797 GstPlayerSubtitleInfo *
3798 gst_player_get_current_subtitle_track (GstPlayer * self)
3799 {
3800   GstPlayerSubtitleInfo *info;
3801
3802   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3803
3804   if (!is_track_enabled (self, GST_PLAY_FLAG_SUBTITLE))
3805     return NULL;
3806
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);
3811   } else {
3812     info = (GstPlayerSubtitleInfo *) gst_player_stream_info_get_current (self,
3813         "current-text", GST_TYPE_PLAYER_SUBTITLE_INFO);
3814   }
3815
3816   return info;
3817 }
3818
3819 /* Must be called with lock */
3820 static gboolean
3821 gst_player_select_streams (GstPlayer * self)
3822 {
3823   GList *stream_list = NULL;
3824   gboolean ret = FALSE;
3825
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));
3832
3833   g_mutex_unlock (&self->lock);
3834   if (stream_list) {
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);
3838   } else {
3839     GST_ERROR_OBJECT (self, "No available streams for select-streams");
3840   }
3841   g_mutex_lock (&self->lock);
3842
3843   return ret;
3844 }
3845
3846 /**
3847  * gst_player_set_audio_track:
3848  * @player: #GstPlayer instance
3849  * @stream_index: stream index
3850  *
3851  * Returns: %TRUE or %FALSE
3852  *
3853  * Sets the audio track @stream_idex.
3854  */
3855 gboolean
3856 gst_player_set_audio_track (GstPlayer * self, gint stream_index)
3857 {
3858   GstPlayerStreamInfo *info;
3859   gboolean ret = TRUE;
3860
3861   g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3862
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);
3867   if (!info) {
3868     GST_ERROR_OBJECT (self, "invalid audio stream index %d", stream_index);
3869     return FALSE;
3870   }
3871
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);
3878   } else {
3879     g_object_set (G_OBJECT (self->playbin), "current-audio", stream_index,
3880         NULL);
3881   }
3882
3883   GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3884   return ret;
3885 }
3886
3887 /**
3888  * gst_player_set_video_track:
3889  * @player: #GstPlayer instance
3890  * @stream_index: stream index
3891  *
3892  * Returns: %TRUE or %FALSE
3893  *
3894  * Sets the video track @stream_index.
3895  */
3896 gboolean
3897 gst_player_set_video_track (GstPlayer * self, gint stream_index)
3898 {
3899   GstPlayerStreamInfo *info;
3900   gboolean ret = TRUE;
3901
3902   g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3903
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);
3909   if (!info) {
3910     GST_ERROR_OBJECT (self, "invalid video stream index %d", stream_index);
3911     return FALSE;
3912   }
3913
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);
3920   } else {
3921     g_object_set (G_OBJECT (self->playbin), "current-video", stream_index,
3922         NULL);
3923   }
3924
3925   GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3926   return ret;
3927 }
3928
3929 /**
3930  * gst_player_set_subtitle_track:
3931  * @player: #GstPlayer instance
3932  * @stream_index: stream index
3933  *
3934  * Returns: %TRUE or %FALSE
3935  *
3936  * Sets the subtitle stack @stream_index.
3937  */
3938 gboolean
3939 gst_player_set_subtitle_track (GstPlayer * self, gint stream_index)
3940 {
3941   GstPlayerStreamInfo *info;
3942   gboolean ret = TRUE;
3943
3944   g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3945
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);
3950   if (!info) {
3951     GST_ERROR_OBJECT (self, "invalid subtitle stream index %d", stream_index);
3952     return FALSE;
3953   }
3954
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);
3961   } else {
3962     g_object_set (G_OBJECT (self->playbin), "current-text", stream_index, NULL);
3963   }
3964
3965   GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3966   return ret;
3967 }
3968
3969 /**
3970  * gst_player_set_audio_track_enabled:
3971  * @player: #GstPlayer instance
3972  * @enabled: TRUE or FALSE
3973  *
3974  * Enable or disable the current audio track.
3975  */
3976 void
3977 gst_player_set_audio_track_enabled (GstPlayer * self, gboolean enabled)
3978 {
3979   g_return_if_fail (GST_IS_PLAYER (self));
3980
3981   if (enabled)
3982     player_set_flag (self, GST_PLAY_FLAG_AUDIO);
3983   else
3984     player_clear_flag (self, GST_PLAY_FLAG_AUDIO);
3985
3986   GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
3987 }
3988
3989 /**
3990  * gst_player_set_video_track_enabled:
3991  * @player: #GstPlayer instance
3992  * @enabled: TRUE or FALSE
3993  *
3994  * Enable or disable the current video track.
3995  */
3996 void
3997 gst_player_set_video_track_enabled (GstPlayer * self, gboolean enabled)
3998 {
3999   g_return_if_fail (GST_IS_PLAYER (self));
4000
4001   if (enabled)
4002     player_set_flag (self, GST_PLAY_FLAG_VIDEO);
4003   else
4004     player_clear_flag (self, GST_PLAY_FLAG_VIDEO);
4005
4006   GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
4007 }
4008
4009 /**
4010  * gst_player_set_subtitle_track_enabled:
4011  * @player: #GstPlayer instance
4012  * @enabled: TRUE or FALSE
4013  *
4014  * Enable or disable the current subtitle track.
4015  */
4016 void
4017 gst_player_set_subtitle_track_enabled (GstPlayer * self, gboolean enabled)
4018 {
4019   g_return_if_fail (GST_IS_PLAYER (self));
4020
4021   if (enabled)
4022     player_set_flag (self, GST_PLAY_FLAG_SUBTITLE);
4023   else
4024     player_clear_flag (self, GST_PLAY_FLAG_SUBTITLE);
4025
4026   GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
4027 }
4028
4029 /**
4030  * gst_player_set_visualization:
4031  * @player: #GstPlayer instance
4032  * @name: visualization element obtained from
4033  * #gst_player_visualizations_get()
4034  *
4035  * Returns: %TRUE if the visualizations was set correctly. Otherwise,
4036  * %FALSE.
4037  */
4038 gboolean
4039 gst_player_set_visualization (GstPlayer * self, const gchar * name)
4040 {
4041   g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4042
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;
4047   }
4048
4049   if (name) {
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);
4054   }
4055   g_object_set (self->playbin, "vis-plugin", self->current_vis_element, NULL);
4056
4057   g_mutex_unlock (&self->lock);
4058   GST_DEBUG_OBJECT (self, "set vis-plugin to '%s'", name);
4059
4060   return TRUE;
4061
4062 error_no_element:
4063   g_mutex_unlock (&self->lock);
4064   GST_WARNING_OBJECT (self, "could not find visualization '%s'", name);
4065   return FALSE;
4066 }
4067
4068 /**
4069  * gst_player_get_current_visualization:
4070  * @player: #GstPlayer instance
4071  *
4072  * Returns: (transfer full): Name of the currently enabled visualization.
4073  *   g_free() after usage.
4074  */
4075 gchar *
4076 gst_player_get_current_visualization (GstPlayer * self)
4077 {
4078   gchar *name = NULL;
4079   GstElement *vis_plugin = NULL;
4080
4081   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4082
4083   if (!is_track_enabled (self, GST_PLAY_FLAG_VIS))
4084     return NULL;
4085
4086   g_object_get (self->playbin, "vis-plugin", &vis_plugin, NULL);
4087
4088   if (vis_plugin) {
4089     GstElementFactory *factory = gst_element_get_factory (vis_plugin);
4090     if (factory)
4091       name = g_strdup (gst_plugin_feature_get_name (factory));
4092     gst_object_unref (vis_plugin);
4093   }
4094
4095   GST_DEBUG_OBJECT (self, "vis-plugin '%s' %p", name, vis_plugin);
4096
4097   return name;
4098 }
4099
4100 /**
4101  * gst_player_set_visualization_enabled:
4102  * @player: #GstPlayer instance
4103  * @enabled: TRUE or FALSE
4104  *
4105  * Enable or disable the visualization.
4106  */
4107 void
4108 gst_player_set_visualization_enabled (GstPlayer * self, gboolean enabled)
4109 {
4110   g_return_if_fail (GST_IS_PLAYER (self));
4111
4112   if (enabled)
4113     player_set_flag (self, GST_PLAY_FLAG_VIS);
4114   else
4115     player_clear_flag (self, GST_PLAY_FLAG_VIS);
4116
4117   GST_DEBUG_OBJECT (self, "visualization is '%s'",
4118       enabled ? "Enabled" : "Disabled");
4119 }
4120
4121 struct CBChannelMap
4122 {
4123   const gchar *label;           /* channel label name */
4124   const gchar *name;            /* get_name () */
4125 };
4126
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"},
4132 };
4133
4134 static GstColorBalanceChannel *
4135 gst_player_color_balance_find_channel (GstPlayer * self,
4136     GstPlayerColorBalanceType type)
4137 {
4138   GstColorBalanceChannel *channel;
4139   const GList *l, *channels;
4140
4141   if (type < GST_PLAYER_COLOR_BALANCE_BRIGHTNESS ||
4142       type > GST_PLAYER_COLOR_BALANCE_HUE)
4143     return NULL;
4144
4145   channels =
4146       gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin));
4147   for (l = channels; l; l = l->next) {
4148     channel = l->data;
4149     if (g_strrstr (channel->label, cb_channel_map[type].label))
4150       return channel;
4151   }
4152
4153   return NULL;
4154 }
4155
4156 /**
4157  * gst_player_has_color_balance:
4158  * @player:#GstPlayer instance
4159  *
4160  * Checks whether the @player has color balance support available.
4161  *
4162  * Returns: %TRUE if @player has color balance support. Otherwise,
4163  *   %FALSE.
4164  */
4165 gboolean
4166 gst_player_has_color_balance (GstPlayer * self)
4167 {
4168   const GList *channels;
4169
4170   g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4171
4172   if (!GST_IS_COLOR_BALANCE (self->playbin))
4173     return FALSE;
4174
4175   channels =
4176       gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin));
4177   return (channels != NULL);
4178 }
4179
4180 /**
4181  * gst_player_set_color_balance:
4182  * @player: #GstPlayer instance
4183  * @type: #GstPlayerColorBalanceType
4184  * @value: The new value for the @type, ranged [0,1]
4185  *
4186  * Sets the current value of the indicated channel @type to the passed
4187  * value.
4188  */
4189 void
4190 gst_player_set_color_balance (GstPlayer * self, GstPlayerColorBalanceType type,
4191     gdouble value)
4192 {
4193   GstColorBalanceChannel *channel;
4194   gdouble new_val;
4195
4196   g_return_if_fail (GST_IS_PLAYER (self));
4197   g_return_if_fail (value >= 0.0 && value <= 1.0);
4198
4199   if (!GST_IS_COLOR_BALANCE (self->playbin))
4200     return;
4201
4202   channel = gst_player_color_balance_find_channel (self, type);
4203   if (!channel)
4204     return;
4205
4206   value = CLAMP (value, 0.0, 1.0);
4207
4208   /* Convert to channel range */
4209   new_val = channel->min_value + value * ((gdouble) channel->max_value -
4210       (gdouble) channel->min_value);
4211
4212   gst_color_balance_set_value (GST_COLOR_BALANCE (self->playbin), channel,
4213       new_val);
4214 }
4215
4216 /**
4217  * gst_player_get_color_balance:
4218  * @player: #GstPlayer instance
4219  * @type: #GstPlayerColorBalanceType
4220  *
4221  * Retrieve the current value of the indicated @type.
4222  *
4223  * Returns: The current value of @type, between [0,1]. In case of
4224  *   error -1 is returned.
4225  */
4226 gdouble
4227 gst_player_get_color_balance (GstPlayer * self, GstPlayerColorBalanceType type)
4228 {
4229   GstColorBalanceChannel *channel;
4230   gint value;
4231
4232   g_return_val_if_fail (GST_IS_PLAYER (self), -1);
4233
4234   if (!GST_IS_COLOR_BALANCE (self->playbin))
4235     return -1;
4236
4237   channel = gst_player_color_balance_find_channel (self, type);
4238   if (!channel)
4239     return -1;
4240
4241   value = gst_color_balance_get_value (GST_COLOR_BALANCE (self->playbin),
4242       channel);
4243
4244   return ((gdouble) value -
4245       (gdouble) channel->min_value) / ((gdouble) channel->max_value -
4246       (gdouble) channel->min_value);
4247 }
4248
4249 /**
4250  * gst_player_get_multiview_mode:
4251  * @player: #GstPlayer instance
4252  *
4253  * Retrieve the current value of the indicated @type.
4254  *
4255  * Returns: The current value of @type, Default: -1 "none"
4256  *
4257  * Since: 1.10
4258  */
4259 GstVideoMultiviewFramePacking
4260 gst_player_get_multiview_mode (GstPlayer * self)
4261 {
4262   GstVideoMultiviewFramePacking val = GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE;
4263
4264   g_return_val_if_fail (GST_IS_PLAYER (self),
4265       GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE);
4266
4267   g_object_get (self, "video-multiview-mode", &val, NULL);
4268
4269   return val;
4270 }
4271
4272 /**
4273  * gst_player_set_multiview_mode:
4274  * @player: #GstPlayer instance
4275  * @mode: The new value for the @type
4276  *
4277  * Sets the current value of the indicated mode @type to the passed
4278  * value.
4279  *
4280  * Since: 1.10
4281  */
4282 void
4283 gst_player_set_multiview_mode (GstPlayer * self,
4284     GstVideoMultiviewFramePacking mode)
4285 {
4286   g_return_if_fail (GST_IS_PLAYER (self));
4287
4288   g_object_set (self, "video-multiview-mode", mode, NULL);
4289 }
4290
4291 /**
4292  * gst_player_get_multiview_flags:
4293  * @player: #GstPlayer instance
4294  *
4295  * Retrieve the current value of the indicated @type.
4296  *
4297  * Returns: The current value of @type, Default: 0x00000000 "none
4298  *
4299  * Since: 1.10
4300  */
4301 GstVideoMultiviewFlags
4302 gst_player_get_multiview_flags (GstPlayer * self)
4303 {
4304   GstVideoMultiviewFlags val = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
4305
4306   g_return_val_if_fail (GST_IS_PLAYER (self), val);
4307
4308   g_object_get (self, "video-multiview-flags", &val, NULL);
4309
4310   return val;
4311 }
4312
4313 /**
4314  * gst_player_set_multiview_flags:
4315  * @player: #GstPlayer instance
4316  * @flags: The new value for the @type
4317  *
4318  * Sets the current value of the indicated mode @type to the passed
4319  * value.
4320  *
4321  * Since: 1.10
4322  */
4323 void
4324 gst_player_set_multiview_flags (GstPlayer * self, GstVideoMultiviewFlags flags)
4325 {
4326   g_return_if_fail (GST_IS_PLAYER (self));
4327
4328   g_object_set (self, "video-multiview-flags", flags, NULL);
4329 }
4330
4331 /**
4332  * gst_player_get_audio_video_offset:
4333  * @player: #GstPlayer instance
4334  *
4335  * Retrieve the current value of audio-video-offset property
4336  *
4337  * Returns: The current value of audio-video-offset in nanoseconds
4338  *
4339  * Since: 1.10
4340  */
4341 gint64
4342 gst_player_get_audio_video_offset (GstPlayer * self)
4343 {
4344   gint64 val = 0;
4345
4346   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_AUDIO_VIDEO_OFFSET);
4347
4348   g_object_get (self, "audio-video-offset", &val, NULL);
4349
4350   return val;
4351 }
4352
4353 /**
4354  * gst_player_set_audio_video_offset:
4355  * @player: #GstPlayer instance
4356  * @offset: #gint64 in nanoseconds
4357  *
4358  * Sets audio-video-offset property by value of @offset
4359  *
4360  * Since: 1.10
4361  */
4362 void
4363 gst_player_set_audio_video_offset (GstPlayer * self, gint64 offset)
4364 {
4365   g_return_if_fail (GST_IS_PLAYER (self));
4366
4367   g_object_set (self, "audio-video-offset", offset, NULL);
4368 }
4369
4370 /**
4371  * gst_player_get_subtitle_video_offset:
4372  * @player: #GstPlayer instance
4373  *
4374  * Retrieve the current value of subtitle-video-offset property
4375  *
4376  * Returns: The current value of subtitle-video-offset in nanoseconds
4377  *
4378  * Since: 1.16
4379  */
4380 gint64
4381 gst_player_get_subtitle_video_offset (GstPlayer * self)
4382 {
4383   gint64 val = 0;
4384
4385   g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_SUBTITLE_VIDEO_OFFSET);
4386
4387   g_object_get (self, "subtitle-video-offset", &val, NULL);
4388
4389   return val;
4390 }
4391
4392 /**
4393  * gst_player_set_subtitle_video_offset:
4394  * @player: #GstPlayer instance
4395  * @offset: #gint64 in nanoseconds
4396  *
4397  * Sets subtitle-video-offset property by value of @offset
4398  *
4399  * Since: 1.16
4400  */
4401 void
4402 gst_player_set_subtitle_video_offset (GstPlayer * self, gint64 offset)
4403 {
4404   g_return_if_fail (GST_IS_PLAYER (self));
4405
4406   g_object_set (self, "subtitle-video-offset", offset, NULL);
4407 }
4408
4409
4410 #define C_ENUM(v) ((gint) v)
4411 #define C_FLAGS(v) ((guint) v)
4412
4413 GType
4414 gst_player_color_balance_type_get_type (void)
4415 {
4416   static gsize id = 0;
4417   static const GEnumValue values[] = {
4418     {C_ENUM (GST_PLAYER_COLOR_BALANCE_HUE), "GST_PLAYER_COLOR_BALANCE_HUE",
4419         "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"},
4426     {0, NULL, NULL}
4427   };
4428
4429   if (g_once_init_enter (&id)) {
4430     GType tmp = g_enum_register_static ("GstPlayerColorBalanceType", values);
4431     g_once_init_leave (&id, tmp);
4432   }
4433
4434   return (GType) id;
4435 }
4436
4437 /**
4438  * gst_player_color_balance_type_get_name:
4439  * @type: a #GstPlayerColorBalanceType
4440  *
4441  * Gets a string representing the given color balance type.
4442  *
4443  * Returns: (transfer none): a string with the name of the color
4444  *   balance type.
4445  */
4446 const gchar *
4447 gst_player_color_balance_type_get_name (GstPlayerColorBalanceType type)
4448 {
4449   g_return_val_if_fail (type >= GST_PLAYER_COLOR_BALANCE_BRIGHTNESS &&
4450       type <= GST_PLAYER_COLOR_BALANCE_HUE, NULL);
4451
4452   return cb_channel_map[type].name;
4453 }
4454
4455 GType
4456 gst_player_state_get_type (void)
4457 {
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",
4462         "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"},
4465     {0, NULL, NULL}
4466   };
4467
4468   if (g_once_init_enter (&id)) {
4469     GType tmp = g_enum_register_static ("GstPlayerState", values);
4470     g_once_init_leave (&id, tmp);
4471   }
4472
4473   return (GType) id;
4474 }
4475
4476 /**
4477  * gst_player_state_get_name:
4478  * @state: a #GstPlayerState
4479  *
4480  * Gets a string representing the given state.
4481  *
4482  * Returns: (transfer none): a string with the name of the state.
4483  */
4484 const gchar *
4485 gst_player_state_get_name (GstPlayerState state)
4486 {
4487   switch (state) {
4488     case GST_PLAYER_STATE_STOPPED:
4489       return "stopped";
4490     case GST_PLAYER_STATE_BUFFERING:
4491       return "buffering";
4492     case GST_PLAYER_STATE_PAUSED:
4493       return "paused";
4494     case GST_PLAYER_STATE_PLAYING:
4495       return "playing";
4496   }
4497
4498   g_assert_not_reached ();
4499   return NULL;
4500 }
4501
4502 GType
4503 gst_player_error_get_type (void)
4504 {
4505   static gsize id = 0;
4506   static const GEnumValue values[] = {
4507     {C_ENUM (GST_PLAYER_ERROR_FAILED), "GST_PLAYER_ERROR_FAILED", "failed"},
4508     {0, NULL, NULL}
4509   };
4510
4511   if (g_once_init_enter (&id)) {
4512     GType tmp = g_enum_register_static ("GstPlayerError", values);
4513     g_once_init_leave (&id, tmp);
4514   }
4515
4516   return (GType) id;
4517 }
4518
4519 /**
4520  * gst_player_error_get_name:
4521  * @error: a #GstPlayerError
4522  *
4523  * Gets a string representing the given error.
4524  *
4525  * Returns: (transfer none): a string with the given error.
4526  */
4527 const gchar *
4528 gst_player_error_get_name (GstPlayerError error)
4529 {
4530   switch (error) {
4531     case GST_PLAYER_ERROR_FAILED:
4532       return "failed";
4533   }
4534
4535   g_assert_not_reached ();
4536   return NULL;
4537 }
4538
4539 /**
4540  * gst_player_set_config:
4541  * @player: #GstPlayer instance
4542  * @config: (transfer full): a #GstStructure
4543  *
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.
4548  *
4549  * @config is a #GstStructure that contains the configuration parameters for
4550  * the player.
4551  *
4552  * This function takes ownership of @config.
4553  *
4554  * Returns: %TRUE when the configuration could be set.
4555  * Since: 1.10
4556  */
4557 gboolean
4558 gst_player_set_config (GstPlayer * self, GstStructure * config)
4559 {
4560   g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4561   g_return_val_if_fail (config != NULL, FALSE);
4562
4563   g_mutex_lock (&self->lock);
4564
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);
4569     return FALSE;
4570   }
4571
4572   if (self->config)
4573     gst_structure_free (self->config);
4574   self->config = config;
4575   g_mutex_unlock (&self->lock);
4576
4577   return TRUE;
4578 }
4579
4580 /**
4581  * gst_player_get_config:
4582  * @player: #GstPlayer instance
4583  *
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.
4587  *
4588  * Returns: (transfer full): a copy of the current configuration of @player. Use
4589  * gst_structure_free() after usage or gst_player_set_config().
4590  *
4591  * Since: 1.10
4592  */
4593 GstStructure *
4594 gst_player_get_config (GstPlayer * self)
4595 {
4596   GstStructure *ret;
4597
4598   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4599
4600   g_mutex_lock (&self->lock);
4601   ret = gst_structure_copy (self->config);
4602   g_mutex_unlock (&self->lock);
4603
4604   return ret;
4605 }
4606
4607 /**
4608  * gst_player_config_set_user_agent:
4609  * @config: a #GstPlayer configuration
4610  * @agent: the string to use as user agent
4611  *
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
4614  * or RTSP streams.
4615  *
4616  * Since: 1.10
4617  */
4618 void
4619 gst_player_config_set_user_agent (GstStructure * config, const gchar * agent)
4620 {
4621   g_return_if_fail (config != NULL);
4622   g_return_if_fail (agent != NULL);
4623
4624   gst_structure_id_set (config,
4625       CONFIG_QUARK (USER_AGENT), G_TYPE_STRING, agent, NULL);
4626 }
4627
4628 /**
4629  * gst_player_config_get_user_agent:
4630  * @config: a #GstPlayer configuration
4631  *
4632  * Return the user agent which has been configured using
4633  * gst_player_config_set_user_agent() if any.
4634  *
4635  * Returns: (transfer full): the configured agent, or %NULL
4636  * Since: 1.10
4637  */
4638 gchar *
4639 gst_player_config_get_user_agent (const GstStructure * config)
4640 {
4641   gchar *agent = NULL;
4642
4643   g_return_val_if_fail (config != NULL, NULL);
4644
4645   gst_structure_id_get (config,
4646       CONFIG_QUARK (USER_AGENT), G_TYPE_STRING, &agent, NULL);
4647
4648   return agent;
4649 }
4650
4651 /**
4652  * gst_player_config_set_position_update_interval:
4653  * @config: a #GstPlayer configuration
4654  * @interval: interval in ms
4655  *
4656  * set interval in milliseconds between two position-updated signals.
4657  * pass 0 to stop updating the position.
4658  * Since: 1.10
4659  */
4660 void
4661 gst_player_config_set_position_update_interval (GstStructure * config,
4662     guint interval)
4663 {
4664   g_return_if_fail (config != NULL);
4665   g_return_if_fail (interval <= 10000);
4666
4667   gst_structure_id_set (config,
4668       CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, interval, NULL);
4669 }
4670
4671 /**
4672  * gst_player_config_get_position_update_interval:
4673  * @config: a #GstPlayer configuration
4674  *
4675  * Returns: current position update interval in milliseconds
4676  *
4677  * Since: 1.10
4678  */
4679 guint
4680 gst_player_config_get_position_update_interval (const GstStructure * config)
4681 {
4682   guint interval = DEFAULT_POSITION_UPDATE_INTERVAL_MS;
4683
4684   g_return_val_if_fail (config != NULL, DEFAULT_POSITION_UPDATE_INTERVAL_MS);
4685
4686   gst_structure_id_get (config,
4687       CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, &interval, NULL);
4688
4689   return interval;
4690 }
4691
4692 /**
4693  * gst_player_config_set_seek_accurate:
4694  * @config: a #GstPlayer configuration
4695  * @accurate: accurate seek or not
4696  *
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.
4701  *
4702  * If accurate seeking is disabled, elements will seek as close as the request
4703  * position without slowing down seeking too much.
4704  *
4705  * Accurate seeking is disabled by default.
4706  *
4707  * Since: 1.12
4708  */
4709 void
4710 gst_player_config_set_seek_accurate (GstStructure * config, gboolean accurate)
4711 {
4712   g_return_if_fail (config != NULL);
4713
4714   gst_structure_id_set (config,
4715       CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, accurate, NULL);
4716 }
4717
4718 /**
4719  * gst_player_config_get_seek_accurate:
4720  * @config: a #GstPlayer configuration
4721  *
4722  * Returns: %TRUE if accurate seeking is enabled
4723  *
4724  * Since: 1.12
4725  */
4726 gboolean
4727 gst_player_config_get_seek_accurate (const GstStructure * config)
4728 {
4729   gboolean accurate = FALSE;
4730
4731   g_return_val_if_fail (config != NULL, FALSE);
4732
4733   gst_structure_id_get (config,
4734       CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, &accurate, NULL);
4735
4736   return accurate;
4737 }
4738
4739 /**
4740  * gst_player_get_video_snapshot:
4741  * @player: #GstPlayer instance
4742  * @format: output format of the video snapshot
4743  * @config: (allow-none): Additional configuration
4744  *
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
4751  *
4752  * Returns: (transfer full):  Current video snapshot sample or %NULL on failure
4753  *
4754  * Since: 1.12
4755  */
4756 GstSample *
4757 gst_player_get_video_snapshot (GstPlayer * self,
4758     GstPlayerSnapshotFormat format, const GstStructure * config)
4759 {
4760   gint video_tracks = 0;
4761   GstSample *sample = NULL;
4762   GstCaps *caps = NULL;
4763   gint width = -1;
4764   gint height = -1;
4765   gint par_n = 1;
4766   gint par_d = 1;
4767   g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4768
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");
4772     return NULL;
4773   }
4774
4775   switch (format) {
4776     case GST_PLAYER_THUMBNAIL_RAW_xRGB:
4777       caps = gst_caps_new_simple ("video/x-raw",
4778           "format", G_TYPE_STRING, "xRGB", NULL);
4779       break;
4780     case GST_PLAYER_THUMBNAIL_RAW_BGRx:
4781       caps = gst_caps_new_simple ("video/x-raw",
4782           "format", G_TYPE_STRING, "BGRx", NULL);
4783       break;
4784     case GST_PLAYER_THUMBNAIL_JPG:
4785       caps = gst_caps_new_empty_simple ("image/jpeg");
4786       break;
4787     case GST_PLAYER_THUMBNAIL_PNG:
4788       caps = gst_caps_new_empty_simple ("image/png");
4789       break;
4790     case GST_PLAYER_THUMBNAIL_RAW_NATIVE:
4791     default:
4792       caps = gst_caps_new_empty_simple ("video/x-raw");
4793       break;
4794   }
4795
4796   if (NULL != config) {
4797     if (!gst_structure_get_int (config, "width", &width))
4798       width = -1;
4799     if (!gst_structure_get_int (config, "height", &height))
4800       height = -1;
4801     if (!gst_structure_get_fraction (config, "pixel-aspect-ratio", &par_n,
4802             &par_d)) {
4803       if (format != GST_PLAYER_THUMBNAIL_RAW_NATIVE) {
4804         par_n = 1;
4805         par_d = 1;
4806       } else {
4807         par_n = 0;
4808         par_d = 0;
4809       }
4810     }
4811   }
4812
4813   if (width > 0 && height > 0) {
4814     gst_caps_set_simple (caps, "width", G_TYPE_INT, width,
4815         "height", G_TYPE_INT, height, NULL);
4816   }
4817
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);
4824   }
4825
4826   g_signal_emit_by_name (self->playbin, "convert-sample", caps, &sample);
4827   gst_caps_unref (caps);
4828   if (!sample) {
4829     GST_WARNING_OBJECT (self, "Failed to retrieve or convert video frame");
4830     return NULL;
4831   }
4832
4833   return sample;
4834 }