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 *
27 */
28
29 /* TODO:
30 *
31 * - Equalizer
32 * - Gapless playback
33 * - Frame stepping
34 * - Subtitle font, connection speed
35 * - Deinterlacing
36 * - Buffering control (-> progressive downloading)
37 * - Playlist/queue object
38 * - Custom video sink (e.g. embed in GL scene)
39 *
40 */
41
42 #ifdef HAVE_CONFIG_H
43 #include "config.h"
44 #endif
45
46 #include "gstplayer.h"
47 #include "gstplayer-signal-dispatcher-private.h"
48 #include "gstplayer-video-renderer-private.h"
49 #include "gstplayer-media-info-private.h"
50
51 #include <gst/gst.h>
52 #include <gst/video/video.h>
53 #include <gst/video/colorbalance.h>
54 #include <gst/tag/tag.h>
55 #include <gst/pbutils/descriptions.h>
56
57 #include <string.h>
58
59 GST_DEBUG_CATEGORY_STATIC (gst_player_debug);
60 #define GST_CAT_DEFAULT gst_player_debug
61
62 #define DEFAULT_URI NULL
63 #define DEFAULT_POSITION GST_CLOCK_TIME_NONE
64 #define DEFAULT_DURATION GST_CLOCK_TIME_NONE
65 #define DEFAULT_VOLUME 1.0
66 #define DEFAULT_MUTE FALSE
67 #define DEFAULT_RATE 1.0
68 #define DEFAULT_POSITION_UPDATE_INTERVAL_MS 100
69 #define DEFAULT_AUDIO_VIDEO_OFFSET 0
70 #define DEFAULT_SUBTITLE_VIDEO_OFFSET 0
71
72 GQuark
gst_player_error_quark(void)73 gst_player_error_quark (void)
74 {
75 return g_quark_from_static_string ("gst-player-error-quark");
76 }
77
78 static GQuark QUARK_CONFIG;
79
80 /* Keep ConfigQuarkId and _config_quark_strings ordered and synced */
81 typedef enum
82 {
83 CONFIG_QUARK_USER_AGENT = 0,
84 CONFIG_QUARK_POSITION_INTERVAL_UPDATE,
85 CONFIG_QUARK_ACCURATE_SEEK,
86
87 CONFIG_QUARK_MAX
88 } ConfigQuarkId;
89
90 static const gchar *_config_quark_strings[] = {
91 "user-agent",
92 "position-interval-update",
93 "accurate-seek",
94 };
95
96 GQuark _config_quark_table[CONFIG_QUARK_MAX];
97
98 #define CONFIG_QUARK(q) _config_quark_table[CONFIG_QUARK_##q]
99
100 enum
101 {
102 PROP_0,
103 PROP_VIDEO_RENDERER,
104 PROP_SIGNAL_DISPATCHER,
105 PROP_URI,
106 PROP_SUBURI,
107 PROP_POSITION,
108 PROP_DURATION,
109 PROP_MEDIA_INFO,
110 PROP_CURRENT_AUDIO_TRACK,
111 PROP_CURRENT_VIDEO_TRACK,
112 PROP_CURRENT_SUBTITLE_TRACK,
113 PROP_VOLUME,
114 PROP_MUTE,
115 PROP_RATE,
116 PROP_PIPELINE,
117 PROP_VIDEO_MULTIVIEW_MODE,
118 PROP_VIDEO_MULTIVIEW_FLAGS,
119 PROP_AUDIO_VIDEO_OFFSET,
120 PROP_SUBTITLE_VIDEO_OFFSET,
121 PROP_LAST
122 };
123
124 enum
125 {
126 SIGNAL_URI_LOADED,
127 SIGNAL_POSITION_UPDATED,
128 SIGNAL_DURATION_CHANGED,
129 SIGNAL_STATE_CHANGED,
130 SIGNAL_BUFFERING,
131 SIGNAL_END_OF_STREAM,
132 SIGNAL_ERROR,
133 SIGNAL_WARNING,
134 SIGNAL_VIDEO_DIMENSIONS_CHANGED,
135 SIGNAL_MEDIA_INFO_UPDATED,
136 SIGNAL_VOLUME_CHANGED,
137 SIGNAL_MUTE_CHANGED,
138 SIGNAL_SEEK_DONE,
139 SIGNAL_LAST
140 };
141
142 enum
143 {
144 GST_PLAY_FLAG_VIDEO = (1 << 0),
145 GST_PLAY_FLAG_AUDIO = (1 << 1),
146 GST_PLAY_FLAG_SUBTITLE = (1 << 2),
147 GST_PLAY_FLAG_VIS = (1 << 3)
148 };
149
150 struct _GstPlayer
151 {
152 GstObject parent;
153
154 GstPlayerVideoRenderer *video_renderer;
155 GstPlayerSignalDispatcher *signal_dispatcher;
156
157 gchar *uri;
158 gchar *redirect_uri;
159 gchar *suburi;
160
161 GThread *thread;
162 GMutex lock;
163 GCond cond;
164 GMainContext *context;
165 GMainLoop *loop;
166
167 GstElement *playbin;
168 GstBus *bus;
169 GstState target_state, current_state;
170 gboolean is_live, is_eos;
171 GSource *tick_source, *ready_timeout_source;
172 GstClockTime cached_duration;
173
174 gdouble rate;
175
176 GstPlayerState app_state;
177 gint buffering;
178
179 GstTagList *global_tags;
180 GstPlayerMediaInfo *media_info;
181
182 GstElement *current_vis_element;
183
184 GstStructure *config;
185
186 /* Protected by lock */
187 gboolean seek_pending; /* Only set from main context */
188 GstClockTime last_seek_time; /* Only set from main context */
189 GSource *seek_source;
190 GstClockTime seek_position;
191 /* If TRUE, all signals are inhibited except the
192 * state-changed:GST_PLAYER_STATE_STOPPED/PAUSED. This ensures that no signal
193 * is emitted after gst_player_stop/pause() has been called by the user. */
194 gboolean inhibit_sigs;
195
196 /* For playbin3 */
197 gboolean use_playbin3;
198 GstStreamCollection *collection;
199 gchar *video_sid;
200 gchar *audio_sid;
201 gchar *subtitle_sid;
202 gulong stream_notify_id;
203 };
204
205 struct _GstPlayerClass
206 {
207 GstObjectClass parent_class;
208 };
209
210 #define parent_class gst_player_parent_class
211 G_DEFINE_TYPE (GstPlayer, gst_player, GST_TYPE_OBJECT);
212
213 static guint signals[SIGNAL_LAST] = { 0, };
214 static GParamSpec *param_specs[PROP_LAST] = { NULL, };
215
216 static void gst_player_dispose (GObject * object);
217 static void gst_player_finalize (GObject * object);
218 static void gst_player_set_property (GObject * object, guint prop_id,
219 const GValue * value, GParamSpec * pspec);
220 static void gst_player_get_property (GObject * object, guint prop_id,
221 GValue * value, GParamSpec * pspec);
222 static void gst_player_constructed (GObject * object);
223
224 static gpointer gst_player_main (gpointer data);
225
226 static void gst_player_seek_internal_locked (GstPlayer * self);
227 static void gst_player_stop_internal (GstPlayer * self, gboolean transient);
228 static gboolean gst_player_pause_internal (gpointer user_data);
229 static gboolean gst_player_play_internal (gpointer user_data);
230 static gboolean gst_player_seek_internal (gpointer user_data);
231 static void gst_player_set_rate_internal (GstPlayer * self);
232 static void change_state (GstPlayer * self, GstPlayerState state);
233
234 static GstPlayerMediaInfo *gst_player_media_info_create (GstPlayer * self);
235
236 static void gst_player_streams_info_create (GstPlayer * self,
237 GstPlayerMediaInfo * media_info, const gchar * prop, GType type);
238 static void gst_player_stream_info_update (GstPlayer * self,
239 GstPlayerStreamInfo * s);
240 static void gst_player_stream_info_update_tags_and_caps (GstPlayer * self,
241 GstPlayerStreamInfo * s);
242 static GstPlayerStreamInfo *gst_player_stream_info_find (GstPlayerMediaInfo *
243 media_info, GType type, gint stream_index);
244 static GstPlayerStreamInfo *gst_player_stream_info_get_current (GstPlayer *
245 self, const gchar * prop, GType type);
246
247 static void gst_player_video_info_update (GstPlayer * self,
248 GstPlayerStreamInfo * stream_info);
249 static void gst_player_audio_info_update (GstPlayer * self,
250 GstPlayerStreamInfo * stream_info);
251 static void gst_player_subtitle_info_update (GstPlayer * self,
252 GstPlayerStreamInfo * stream_info);
253
254 /* For playbin3 */
255 static void gst_player_streams_info_create_from_collection (GstPlayer * self,
256 GstPlayerMediaInfo * media_info, GstStreamCollection * collection);
257 static void gst_player_stream_info_update_from_stream (GstPlayer * self,
258 GstPlayerStreamInfo * s, GstStream * stream);
259 static GstPlayerStreamInfo *gst_player_stream_info_find_from_stream_id
260 (GstPlayerMediaInfo * media_info, const gchar * stream_id);
261 static GstPlayerStreamInfo *gst_player_stream_info_get_current_from_stream_id
262 (GstPlayer * self, const gchar * stream_id, GType type);
263 static void stream_notify_cb (GstStreamCollection * collection,
264 GstStream * stream, GParamSpec * pspec, GstPlayer * self);
265
266 static void emit_media_info_updated_signal (GstPlayer * self);
267
268 static void *get_title (GstTagList * tags);
269 static void *get_container_format (GstTagList * tags);
270 static void *get_from_tags (GstPlayer * self, GstPlayerMediaInfo * media_info,
271 void *(*func) (GstTagList *));
272 static void *get_cover_sample (GstTagList * tags);
273
274 static void remove_seek_source (GstPlayer * self);
275
276 static void
gst_player_init(GstPlayer * self)277 gst_player_init (GstPlayer * self)
278 {
279 GST_TRACE_OBJECT (self, "Initializing");
280
281 self = gst_player_get_instance_private (self);
282
283 g_mutex_init (&self->lock);
284 g_cond_init (&self->cond);
285
286 self->context = g_main_context_new ();
287 self->loop = g_main_loop_new (self->context, FALSE);
288
289 /* *INDENT-OFF* */
290 self->config = gst_structure_new_id (QUARK_CONFIG,
291 CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, DEFAULT_POSITION_UPDATE_INTERVAL_MS,
292 CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, FALSE,
293 NULL);
294 /* *INDENT-ON* */
295
296 self->seek_pending = FALSE;
297 self->seek_position = GST_CLOCK_TIME_NONE;
298 self->last_seek_time = GST_CLOCK_TIME_NONE;
299 self->inhibit_sigs = FALSE;
300
301 GST_TRACE_OBJECT (self, "Initialized");
302 }
303
304 static void
config_quark_initialize(void)305 config_quark_initialize (void)
306 {
307 gint i;
308
309 QUARK_CONFIG = g_quark_from_static_string ("player-config");
310
311 if (G_N_ELEMENTS (_config_quark_strings) != CONFIG_QUARK_MAX)
312 g_warning ("the quark table is not consistent! %d != %d",
313 (int) G_N_ELEMENTS (_config_quark_strings), CONFIG_QUARK_MAX);
314
315 for (i = 0; i < CONFIG_QUARK_MAX; i++) {
316 _config_quark_table[i] =
317 g_quark_from_static_string (_config_quark_strings[i]);
318 }
319 }
320
321 static void
gst_player_class_init(GstPlayerClass * klass)322 gst_player_class_init (GstPlayerClass * klass)
323 {
324 GObjectClass *gobject_class = (GObjectClass *) klass;
325
326 gobject_class->set_property = gst_player_set_property;
327 gobject_class->get_property = gst_player_get_property;
328 gobject_class->dispose = gst_player_dispose;
329 gobject_class->finalize = gst_player_finalize;
330 gobject_class->constructed = gst_player_constructed;
331
332 param_specs[PROP_VIDEO_RENDERER] =
333 g_param_spec_object ("video-renderer",
334 "Video Renderer", "Video renderer to use for rendering videos",
335 GST_TYPE_PLAYER_VIDEO_RENDERER,
336 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
337
338 param_specs[PROP_SIGNAL_DISPATCHER] =
339 g_param_spec_object ("signal-dispatcher",
340 "Signal Dispatcher", "Dispatcher for the signals to e.g. event loops",
341 GST_TYPE_PLAYER_SIGNAL_DISPATCHER,
342 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
343
344 param_specs[PROP_URI] = g_param_spec_string ("uri", "URI", "Current URI",
345 DEFAULT_URI, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
346
347 param_specs[PROP_SUBURI] = g_param_spec_string ("suburi", "Subtitle URI",
348 "Current Subtitle URI", NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
349
350 param_specs[PROP_POSITION] =
351 g_param_spec_uint64 ("position", "Position", "Current Position",
352 0, G_MAXUINT64, DEFAULT_POSITION,
353 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
354
355 param_specs[PROP_MEDIA_INFO] =
356 g_param_spec_object ("media-info", "Media Info",
357 "Current media information", GST_TYPE_PLAYER_MEDIA_INFO,
358 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
359
360 param_specs[PROP_CURRENT_AUDIO_TRACK] =
361 g_param_spec_object ("current-audio-track", "Current Audio Track",
362 "Current audio track information", GST_TYPE_PLAYER_AUDIO_INFO,
363 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
364
365 param_specs[PROP_CURRENT_VIDEO_TRACK] =
366 g_param_spec_object ("current-video-track", "Current Video Track",
367 "Current video track information", GST_TYPE_PLAYER_VIDEO_INFO,
368 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
369
370 param_specs[PROP_CURRENT_SUBTITLE_TRACK] =
371 g_param_spec_object ("current-subtitle-track", "Current Subtitle Track",
372 "Current audio subtitle information", GST_TYPE_PLAYER_SUBTITLE_INFO,
373 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
374
375 param_specs[PROP_DURATION] =
376 g_param_spec_uint64 ("duration", "Duration", "Duration",
377 0, G_MAXUINT64, DEFAULT_DURATION,
378 G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
379
380 param_specs[PROP_VOLUME] =
381 g_param_spec_double ("volume", "Volume", "Volume",
382 0, 10.0, DEFAULT_VOLUME, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
383
384 param_specs[PROP_MUTE] =
385 g_param_spec_boolean ("mute", "Mute", "Mute",
386 DEFAULT_MUTE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
387
388 param_specs[PROP_PIPELINE] =
389 g_param_spec_object ("pipeline", "Pipeline",
390 "GStreamer pipeline that is used",
391 GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
392
393 param_specs[PROP_RATE] =
394 g_param_spec_double ("rate", "rate", "Playback rate",
395 -64.0, 64.0, DEFAULT_RATE, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
396
397 param_specs[PROP_VIDEO_MULTIVIEW_MODE] =
398 g_param_spec_enum ("video-multiview-mode",
399 "Multiview Mode Override",
400 "Re-interpret a video stream as one of several frame-packed stereoscopic modes.",
401 GST_TYPE_VIDEO_MULTIVIEW_FRAME_PACKING,
402 GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE,
403 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
404
405 param_specs[PROP_VIDEO_MULTIVIEW_FLAGS] =
406 g_param_spec_flags ("video-multiview-flags",
407 "Multiview Flags Override",
408 "Override details of the multiview frame layout",
409 GST_TYPE_VIDEO_MULTIVIEW_FLAGS, GST_VIDEO_MULTIVIEW_FLAGS_NONE,
410 G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
411
412 param_specs[PROP_AUDIO_VIDEO_OFFSET] =
413 g_param_spec_int64 ("audio-video-offset", "Audio Video Offset",
414 "The synchronisation offset between audio and video in nanoseconds",
415 G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
416
417 param_specs[PROP_SUBTITLE_VIDEO_OFFSET] =
418 g_param_spec_int64 ("subtitle-video-offset", "Text Video Offset",
419 "The synchronisation offset between text and video in nanoseconds",
420 G_MININT64, G_MAXINT64, 0, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
421
422 g_object_class_install_properties (gobject_class, PROP_LAST, param_specs);
423
424 signals[SIGNAL_URI_LOADED] =
425 g_signal_new ("uri-loaded", G_TYPE_FROM_CLASS (klass),
426 G_SIGNAL_RUN_LAST | G_SIGNAL_NO_RECURSE | G_SIGNAL_NO_HOOKS, 0, NULL,
427 NULL, NULL, G_TYPE_NONE, 1, G_TYPE_STRING);
428
429 signals[SIGNAL_POSITION_UPDATED] =
430 g_signal_new ("position-updated", 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, GST_TYPE_CLOCK_TIME);
433
434 signals[SIGNAL_DURATION_CHANGED] =
435 g_signal_new ("duration-changed", 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_STATE_CHANGED] =
440 g_signal_new ("state-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_PLAYER_STATE);
443
444 signals[SIGNAL_BUFFERING] =
445 g_signal_new ("buffering", 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, G_TYPE_INT);
448
449 signals[SIGNAL_END_OF_STREAM] =
450 g_signal_new ("end-of-stream", 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, 0, G_TYPE_INVALID);
453
454 signals[SIGNAL_ERROR] =
455 g_signal_new ("error", 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, 1, G_TYPE_ERROR);
458
459 signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED] =
460 g_signal_new ("video-dimensions-changed", 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, 2, G_TYPE_INT, G_TYPE_INT);
463
464 signals[SIGNAL_MEDIA_INFO_UPDATED] =
465 g_signal_new ("media-info-updated", 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, 1, GST_TYPE_PLAYER_MEDIA_INFO);
468
469 signals[SIGNAL_VOLUME_CHANGED] =
470 g_signal_new ("volume-changed", 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, 0, G_TYPE_INVALID);
473
474 signals[SIGNAL_MUTE_CHANGED] =
475 g_signal_new ("mute-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_WARNING] =
480 g_signal_new ("warning", 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, 1, G_TYPE_ERROR);
483
484 signals[SIGNAL_SEEK_DONE] =
485 g_signal_new ("seek-done", 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, GST_TYPE_CLOCK_TIME);
488
489 config_quark_initialize ();
490 }
491
492 static void
gst_player_dispose(GObject * object)493 gst_player_dispose (GObject * object)
494 {
495 GstPlayer *self = GST_PLAYER (object);
496
497 GST_TRACE_OBJECT (self, "Stopping main thread");
498
499 if (self->loop) {
500 g_main_loop_quit (self->loop);
501
502 if (self->thread != g_thread_self ())
503 g_thread_join (self->thread);
504 else
505 g_thread_unref (self->thread);
506 self->thread = NULL;
507
508 g_main_loop_unref (self->loop);
509 self->loop = NULL;
510
511 g_main_context_unref (self->context);
512 self->context = NULL;
513 }
514
515 G_OBJECT_CLASS (parent_class)->dispose (object);
516 }
517
518 static void
gst_player_finalize(GObject * object)519 gst_player_finalize (GObject * object)
520 {
521 GstPlayer *self = GST_PLAYER (object);
522
523 GST_TRACE_OBJECT (self, "Finalizing");
524
525 g_free (self->uri);
526 g_free (self->redirect_uri);
527 g_free (self->suburi);
528 g_free (self->video_sid);
529 g_free (self->audio_sid);
530 g_free (self->subtitle_sid);
531 if (self->global_tags)
532 gst_tag_list_unref (self->global_tags);
533 if (self->video_renderer)
534 g_object_unref (self->video_renderer);
535 if (self->signal_dispatcher)
536 g_object_unref (self->signal_dispatcher);
537 if (self->current_vis_element)
538 gst_object_unref (self->current_vis_element);
539 if (self->config)
540 gst_structure_free (self->config);
541 if (self->collection)
542 gst_object_unref (self->collection);
543 g_mutex_clear (&self->lock);
544 g_cond_clear (&self->cond);
545
546 G_OBJECT_CLASS (parent_class)->finalize (object);
547 }
548
549 static void
gst_player_constructed(GObject * object)550 gst_player_constructed (GObject * object)
551 {
552 GstPlayer *self = GST_PLAYER (object);
553
554 GST_TRACE_OBJECT (self, "Constructed");
555
556 g_mutex_lock (&self->lock);
557 self->thread = g_thread_new ("GstPlayer", gst_player_main, self);
558 while (!self->loop || !g_main_loop_is_running (self->loop))
559 g_cond_wait (&self->cond, &self->lock);
560 g_mutex_unlock (&self->lock);
561
562 G_OBJECT_CLASS (parent_class)->constructed (object);
563 }
564
565 typedef struct
566 {
567 GstPlayer *player;
568 gchar *uri;
569 } UriLoadedSignalData;
570
571 static void
uri_loaded_dispatch(gpointer user_data)572 uri_loaded_dispatch (gpointer user_data)
573 {
574 UriLoadedSignalData *data = user_data;
575
576 g_signal_emit (data->player, signals[SIGNAL_URI_LOADED], 0, data->uri);
577 }
578
579 static void
uri_loaded_signal_data_free(UriLoadedSignalData * data)580 uri_loaded_signal_data_free (UriLoadedSignalData * data)
581 {
582 g_object_unref (data->player);
583 g_free (data->uri);
584 g_free (data);
585 }
586
587 static gboolean
gst_player_set_uri_internal(gpointer user_data)588 gst_player_set_uri_internal (gpointer user_data)
589 {
590 GstPlayer *self = user_data;
591
592 gst_player_stop_internal (self, FALSE);
593
594 g_mutex_lock (&self->lock);
595
596 GST_DEBUG_OBJECT (self, "Changing URI to '%s'", GST_STR_NULL (self->uri));
597
598 g_object_set (self->playbin, "uri", self->uri, NULL);
599
600 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
601 signals[SIGNAL_URI_LOADED], 0, NULL, NULL, NULL) != 0) {
602 UriLoadedSignalData *data = g_new (UriLoadedSignalData, 1);
603
604 data->player = g_object_ref (self);
605 data->uri = g_strdup (self->uri);
606 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
607 uri_loaded_dispatch, data,
608 (GDestroyNotify) uri_loaded_signal_data_free);
609 }
610
611 g_object_set (self->playbin, "suburi", NULL, NULL);
612
613 g_mutex_unlock (&self->lock);
614
615 return G_SOURCE_REMOVE;
616 }
617
618 static gboolean
gst_player_set_suburi_internal(gpointer user_data)619 gst_player_set_suburi_internal (gpointer user_data)
620 {
621 GstPlayer *self = user_data;
622 GstClockTime position;
623 GstState target_state;
624
625 /* save the state and position */
626 target_state = self->target_state;
627 position = gst_player_get_position (self);
628
629 gst_player_stop_internal (self, TRUE);
630 g_mutex_lock (&self->lock);
631
632 GST_DEBUG_OBJECT (self, "Changing SUBURI to '%s'",
633 GST_STR_NULL (self->suburi));
634
635 g_object_set (self->playbin, "suburi", self->suburi, NULL);
636
637 g_mutex_unlock (&self->lock);
638
639 /* restore state and position */
640 if (position != GST_CLOCK_TIME_NONE)
641 gst_player_seek (self, position);
642 if (target_state == GST_STATE_PAUSED)
643 gst_player_pause_internal (self);
644 else if (target_state == GST_STATE_PLAYING)
645 gst_player_play_internal (self);
646
647 return G_SOURCE_REMOVE;
648 }
649
650 static void
gst_player_set_rate_internal(GstPlayer * self)651 gst_player_set_rate_internal (GstPlayer * self)
652 {
653 self->seek_position = gst_player_get_position (self);
654
655 /* If there is no seek being dispatch to the main context currently do that,
656 * otherwise we just updated the rate so that it will be taken by
657 * the seek handler from the main context instead of the old one.
658 */
659 if (!self->seek_source) {
660 /* If no seek is pending then create new seek source */
661 if (!self->seek_pending) {
662 self->seek_source = g_idle_source_new ();
663 g_source_set_callback (self->seek_source,
664 (GSourceFunc) gst_player_seek_internal, self, NULL);
665 g_source_attach (self->seek_source, self->context);
666 }
667 }
668 }
669
670 static void
gst_player_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)671 gst_player_set_property (GObject * object, guint prop_id,
672 const GValue * value, GParamSpec * pspec)
673 {
674 GstPlayer *self = GST_PLAYER (object);
675
676 switch (prop_id) {
677 case PROP_VIDEO_RENDERER:
678 self->video_renderer = g_value_dup_object (value);
679 break;
680 case PROP_SIGNAL_DISPATCHER:
681 self->signal_dispatcher = g_value_dup_object (value);
682 break;
683 case PROP_URI:{
684 g_mutex_lock (&self->lock);
685 g_free (self->uri);
686 g_free (self->redirect_uri);
687 self->redirect_uri = NULL;
688
689 g_free (self->suburi);
690 self->suburi = NULL;
691
692 self->uri = g_value_dup_string (value);
693 GST_DEBUG_OBJECT (self, "Set uri=%s", self->uri);
694 g_mutex_unlock (&self->lock);
695
696 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
697 gst_player_set_uri_internal, self, NULL);
698 break;
699 }
700 case PROP_SUBURI:{
701 g_mutex_lock (&self->lock);
702 g_free (self->suburi);
703
704 self->suburi = g_value_dup_string (value);
705 GST_DEBUG_OBJECT (self, "Set suburi=%s", self->suburi);
706 g_mutex_unlock (&self->lock);
707
708 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
709 gst_player_set_suburi_internal, self, NULL);
710 break;
711 }
712 case PROP_VOLUME:
713 GST_DEBUG_OBJECT (self, "Set volume=%lf", g_value_get_double (value));
714 g_object_set_property (G_OBJECT (self->playbin), "volume", value);
715 break;
716 case PROP_RATE:
717 g_mutex_lock (&self->lock);
718 self->rate = g_value_get_double (value);
719 GST_DEBUG_OBJECT (self, "Set rate=%lf", g_value_get_double (value));
720 gst_player_set_rate_internal (self);
721 g_mutex_unlock (&self->lock);
722 break;
723 case PROP_MUTE:
724 GST_DEBUG_OBJECT (self, "Set mute=%d", g_value_get_boolean (value));
725 g_object_set_property (G_OBJECT (self->playbin), "mute", value);
726 break;
727 case PROP_VIDEO_MULTIVIEW_MODE:
728 GST_DEBUG_OBJECT (self, "Set multiview mode=%u",
729 g_value_get_enum (value));
730 g_object_set_property (G_OBJECT (self->playbin), "video-multiview-mode",
731 value);
732 break;
733 case PROP_VIDEO_MULTIVIEW_FLAGS:
734 GST_DEBUG_OBJECT (self, "Set multiview flags=%x",
735 g_value_get_flags (value));
736 g_object_set_property (G_OBJECT (self->playbin), "video-multiview-flags",
737 value);
738 break;
739 case PROP_AUDIO_VIDEO_OFFSET:
740 g_object_set_property (G_OBJECT (self->playbin), "av-offset", value);
741 break;
742 case PROP_SUBTITLE_VIDEO_OFFSET:
743 g_object_set_property (G_OBJECT (self->playbin), "text-offset", value);
744 break;
745 default:
746 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
747 break;
748 }
749 }
750
751 static void
gst_player_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)752 gst_player_get_property (GObject * object, guint prop_id,
753 GValue * value, GParamSpec * pspec)
754 {
755 GstPlayer *self = GST_PLAYER (object);
756
757 switch (prop_id) {
758 case PROP_URI:
759 g_mutex_lock (&self->lock);
760 g_value_set_string (value, self->uri);
761 g_mutex_unlock (&self->lock);
762 break;
763 case PROP_SUBURI:
764 g_mutex_lock (&self->lock);
765 g_value_set_string (value, self->suburi);
766 g_mutex_unlock (&self->lock);
767 GST_DEBUG_OBJECT (self, "Returning suburi=%s",
768 g_value_get_string (value));
769 break;
770 case PROP_POSITION:{
771 gint64 position = GST_CLOCK_TIME_NONE;
772
773 gst_element_query_position (self->playbin, GST_FORMAT_TIME, &position);
774 g_value_set_uint64 (value, position);
775 GST_TRACE_OBJECT (self, "Returning position=%" GST_TIME_FORMAT,
776 GST_TIME_ARGS (g_value_get_uint64 (value)));
777 break;
778 }
779 case PROP_DURATION:{
780 g_value_set_uint64 (value, self->cached_duration);
781 GST_TRACE_OBJECT (self, "Returning duration=%" GST_TIME_FORMAT,
782 GST_TIME_ARGS (g_value_get_uint64 (value)));
783 break;
784 }
785 case PROP_MEDIA_INFO:{
786 GstPlayerMediaInfo *media_info = gst_player_get_media_info (self);
787 g_value_take_object (value, media_info);
788 break;
789 }
790 case PROP_CURRENT_AUDIO_TRACK:{
791 GstPlayerAudioInfo *audio_info =
792 gst_player_get_current_audio_track (self);
793 g_value_take_object (value, audio_info);
794 break;
795 }
796 case PROP_CURRENT_VIDEO_TRACK:{
797 GstPlayerVideoInfo *video_info =
798 gst_player_get_current_video_track (self);
799 g_value_take_object (value, video_info);
800 break;
801 }
802 case PROP_CURRENT_SUBTITLE_TRACK:{
803 GstPlayerSubtitleInfo *subtitle_info =
804 gst_player_get_current_subtitle_track (self);
805 g_value_take_object (value, subtitle_info);
806 break;
807 }
808 case PROP_VOLUME:
809 g_object_get_property (G_OBJECT (self->playbin), "volume", value);
810 GST_TRACE_OBJECT (self, "Returning volume=%lf",
811 g_value_get_double (value));
812 break;
813 case PROP_RATE:
814 g_mutex_lock (&self->lock);
815 g_value_set_double (value, self->rate);
816 g_mutex_unlock (&self->lock);
817 break;
818 case PROP_MUTE:
819 g_object_get_property (G_OBJECT (self->playbin), "mute", value);
820 GST_TRACE_OBJECT (self, "Returning mute=%d", g_value_get_boolean (value));
821 break;
822 case PROP_PIPELINE:
823 g_value_set_object (value, self->playbin);
824 break;
825 case PROP_VIDEO_MULTIVIEW_MODE:{
826 g_object_get_property (G_OBJECT (self->playbin), "video-multiview-mode",
827 value);
828 GST_TRACE_OBJECT (self, "Return multiview mode=%d",
829 g_value_get_enum (value));
830 break;
831 }
832 case PROP_VIDEO_MULTIVIEW_FLAGS:{
833 g_object_get_property (G_OBJECT (self->playbin), "video-multiview-flags",
834 value);
835 GST_TRACE_OBJECT (self, "Return multiview flags=%x",
836 g_value_get_flags (value));
837 break;
838 }
839 case PROP_AUDIO_VIDEO_OFFSET:
840 g_object_get_property (G_OBJECT (self->playbin), "av-offset", value);
841 break;
842 case PROP_SUBTITLE_VIDEO_OFFSET:
843 g_object_get_property (G_OBJECT (self->playbin), "text-offset", value);
844 break;
845 default:
846 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
847 break;
848 }
849 }
850
851 static gboolean
main_loop_running_cb(gpointer user_data)852 main_loop_running_cb (gpointer user_data)
853 {
854 GstPlayer *self = GST_PLAYER (user_data);
855
856 GST_TRACE_OBJECT (self, "Main loop running now");
857
858 g_mutex_lock (&self->lock);
859 g_cond_signal (&self->cond);
860 g_mutex_unlock (&self->lock);
861
862 return G_SOURCE_REMOVE;
863 }
864
865 typedef struct
866 {
867 GstPlayer *player;
868 GstPlayerState state;
869 } StateChangedSignalData;
870
871 static void
state_changed_dispatch(gpointer user_data)872 state_changed_dispatch (gpointer user_data)
873 {
874 StateChangedSignalData *data = user_data;
875
876 if (data->player->inhibit_sigs && data->state != GST_PLAYER_STATE_STOPPED
877 && data->state != GST_PLAYER_STATE_PAUSED)
878 return;
879
880 g_signal_emit (data->player, signals[SIGNAL_STATE_CHANGED], 0, data->state);
881 }
882
883 static void
state_changed_signal_data_free(StateChangedSignalData * data)884 state_changed_signal_data_free (StateChangedSignalData * data)
885 {
886 g_object_unref (data->player);
887 g_free (data);
888 }
889
890 static void
change_state(GstPlayer * self,GstPlayerState state)891 change_state (GstPlayer * self, GstPlayerState state)
892 {
893 if (state == self->app_state)
894 return;
895
896 GST_DEBUG_OBJECT (self, "Changing app state from %s to %s",
897 gst_player_state_get_name (self->app_state),
898 gst_player_state_get_name (state));
899 self->app_state = state;
900
901 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
902 signals[SIGNAL_STATE_CHANGED], 0, NULL, NULL, NULL) != 0) {
903 StateChangedSignalData *data = g_new (StateChangedSignalData, 1);
904
905 data->player = g_object_ref (self);
906 data->state = state;
907 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
908 state_changed_dispatch, data,
909 (GDestroyNotify) state_changed_signal_data_free);
910 }
911 }
912
913 typedef struct
914 {
915 GstPlayer *player;
916 GstClockTime position;
917 } PositionUpdatedSignalData;
918
919 static void
position_updated_dispatch(gpointer user_data)920 position_updated_dispatch (gpointer user_data)
921 {
922 PositionUpdatedSignalData *data = user_data;
923
924 if (data->player->inhibit_sigs)
925 return;
926
927 if (data->player->target_state >= GST_STATE_PAUSED) {
928 g_signal_emit (data->player, signals[SIGNAL_POSITION_UPDATED], 0,
929 data->position);
930 g_object_notify_by_pspec (G_OBJECT (data->player),
931 param_specs[PROP_POSITION]);
932 }
933 }
934
935 static void
position_updated_signal_data_free(PositionUpdatedSignalData * data)936 position_updated_signal_data_free (PositionUpdatedSignalData * data)
937 {
938 g_object_unref (data->player);
939 g_free (data);
940 }
941
942 static gboolean
tick_cb(gpointer user_data)943 tick_cb (gpointer user_data)
944 {
945 GstPlayer *self = GST_PLAYER (user_data);
946 gint64 position;
947
948 if (self->target_state >= GST_STATE_PAUSED
949 && gst_element_query_position (self->playbin, GST_FORMAT_TIME,
950 &position)) {
951 GST_LOG_OBJECT (self, "Position %" GST_TIME_FORMAT,
952 GST_TIME_ARGS (position));
953
954 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
955 signals[SIGNAL_POSITION_UPDATED], 0, NULL, NULL, NULL) != 0) {
956 PositionUpdatedSignalData *data = g_new (PositionUpdatedSignalData, 1);
957
958 data->player = g_object_ref (self);
959 data->position = position;
960 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
961 position_updated_dispatch, data,
962 (GDestroyNotify) position_updated_signal_data_free);
963 }
964 }
965
966 return G_SOURCE_CONTINUE;
967 }
968
969 static void
add_tick_source(GstPlayer * self)970 add_tick_source (GstPlayer * self)
971 {
972 guint position_update_interval_ms;
973
974 if (self->tick_source)
975 return;
976
977 position_update_interval_ms =
978 gst_player_config_get_position_update_interval (self->config);
979 if (!position_update_interval_ms)
980 return;
981
982 self->tick_source = g_timeout_source_new (position_update_interval_ms);
983 g_source_set_callback (self->tick_source, (GSourceFunc) tick_cb, self, NULL);
984 g_source_attach (self->tick_source, self->context);
985 }
986
987 static void
remove_tick_source(GstPlayer * self)988 remove_tick_source (GstPlayer * self)
989 {
990 if (!self->tick_source)
991 return;
992
993 g_source_destroy (self->tick_source);
994 g_source_unref (self->tick_source);
995 self->tick_source = NULL;
996 }
997
998 static gboolean
ready_timeout_cb(gpointer user_data)999 ready_timeout_cb (gpointer user_data)
1000 {
1001 GstPlayer *self = user_data;
1002
1003 if (self->target_state <= GST_STATE_READY) {
1004 GST_DEBUG_OBJECT (self, "Setting pipeline to NULL state");
1005 self->target_state = GST_STATE_NULL;
1006 self->current_state = GST_STATE_NULL;
1007 gst_element_set_state (self->playbin, GST_STATE_NULL);
1008 }
1009
1010 return G_SOURCE_REMOVE;
1011 }
1012
1013 static void
add_ready_timeout_source(GstPlayer * self)1014 add_ready_timeout_source (GstPlayer * self)
1015 {
1016 if (self->ready_timeout_source)
1017 return;
1018
1019 self->ready_timeout_source = g_timeout_source_new_seconds (60);
1020 g_source_set_callback (self->ready_timeout_source,
1021 (GSourceFunc) ready_timeout_cb, self, NULL);
1022 g_source_attach (self->ready_timeout_source, self->context);
1023 }
1024
1025 static void
remove_ready_timeout_source(GstPlayer * self)1026 remove_ready_timeout_source (GstPlayer * self)
1027 {
1028 if (!self->ready_timeout_source)
1029 return;
1030
1031 g_source_destroy (self->ready_timeout_source);
1032 g_source_unref (self->ready_timeout_source);
1033 self->ready_timeout_source = NULL;
1034 }
1035
1036 typedef struct
1037 {
1038 GstPlayer *player;
1039 GError *err;
1040 } ErrorSignalData;
1041
1042 static void
error_dispatch(gpointer user_data)1043 error_dispatch (gpointer user_data)
1044 {
1045 ErrorSignalData *data = user_data;
1046
1047 if (data->player->inhibit_sigs)
1048 return;
1049
1050 g_signal_emit (data->player, signals[SIGNAL_ERROR], 0, data->err);
1051 }
1052
1053 static void
free_error_signal_data(ErrorSignalData * data)1054 free_error_signal_data (ErrorSignalData * data)
1055 {
1056 g_object_unref (data->player);
1057 g_clear_error (&data->err);
1058 g_free (data);
1059 }
1060
1061 static void
emit_error(GstPlayer * self,GError * err)1062 emit_error (GstPlayer * self, GError * err)
1063 {
1064 GST_ERROR_OBJECT (self, "Error: %s (%s, %d)", err->message,
1065 g_quark_to_string (err->domain), err->code);
1066
1067 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1068 signals[SIGNAL_ERROR], 0, NULL, NULL, NULL) != 0) {
1069 ErrorSignalData *data = g_new (ErrorSignalData, 1);
1070
1071 data->player = g_object_ref (self);
1072 data->err = g_error_copy (err);
1073 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1074 error_dispatch, data, (GDestroyNotify) free_error_signal_data);
1075 }
1076
1077 g_error_free (err);
1078
1079 remove_tick_source (self);
1080 remove_ready_timeout_source (self);
1081
1082 self->target_state = GST_STATE_NULL;
1083 self->current_state = GST_STATE_NULL;
1084 self->is_live = FALSE;
1085 self->is_eos = FALSE;
1086 gst_element_set_state (self->playbin, GST_STATE_NULL);
1087 change_state (self, GST_PLAYER_STATE_STOPPED);
1088 self->buffering = 100;
1089
1090 g_mutex_lock (&self->lock);
1091 if (self->media_info) {
1092 g_object_unref (self->media_info);
1093 self->media_info = NULL;
1094 }
1095
1096 if (self->global_tags) {
1097 gst_tag_list_unref (self->global_tags);
1098 self->global_tags = NULL;
1099 }
1100
1101 self->seek_pending = FALSE;
1102 remove_seek_source (self);
1103 self->seek_position = GST_CLOCK_TIME_NONE;
1104 self->last_seek_time = GST_CLOCK_TIME_NONE;
1105 g_mutex_unlock (&self->lock);
1106 }
1107
1108 static void
dump_dot_file(GstPlayer * self,const gchar * name)1109 dump_dot_file (GstPlayer * self, const gchar * name)
1110 {
1111 gchar *full_name;
1112
1113 full_name = g_strdup_printf ("gst-player.%p.%s", self, name);
1114
1115 GST_DEBUG_BIN_TO_DOT_FILE_WITH_TS (GST_BIN (self->playbin),
1116 GST_DEBUG_GRAPH_SHOW_ALL, full_name);
1117
1118 g_free (full_name);
1119 }
1120
1121 typedef struct
1122 {
1123 GstPlayer *player;
1124 GError *err;
1125 } WarningSignalData;
1126
1127 static void
warning_dispatch(gpointer user_data)1128 warning_dispatch (gpointer user_data)
1129 {
1130 WarningSignalData *data = user_data;
1131
1132 if (data->player->inhibit_sigs)
1133 return;
1134
1135 g_signal_emit (data->player, signals[SIGNAL_WARNING], 0, data->err);
1136 }
1137
1138 static void
free_warning_signal_data(WarningSignalData * data)1139 free_warning_signal_data (WarningSignalData * data)
1140 {
1141 g_object_unref (data->player);
1142 g_clear_error (&data->err);
1143 g_free (data);
1144 }
1145
1146 static void
emit_warning(GstPlayer * self,GError * err)1147 emit_warning (GstPlayer * self, GError * err)
1148 {
1149 GST_ERROR_OBJECT (self, "Warning: %s (%s, %d)", err->message,
1150 g_quark_to_string (err->domain), err->code);
1151
1152 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1153 signals[SIGNAL_WARNING], 0, NULL, NULL, NULL) != 0) {
1154 WarningSignalData *data = g_new (WarningSignalData, 1);
1155
1156 data->player = g_object_ref (self);
1157 data->err = g_error_copy (err);
1158 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1159 warning_dispatch, data, (GDestroyNotify) free_warning_signal_data);
1160 }
1161
1162 g_error_free (err);
1163 }
1164
1165 static void
error_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1166 error_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1167 {
1168 GstPlayer *self = GST_PLAYER (user_data);
1169 GError *err, *player_err;
1170 gchar *name, *debug, *message, *full_message;
1171
1172 dump_dot_file (self, "error");
1173
1174 gst_message_parse_error (msg, &err, &debug);
1175
1176 name = gst_object_get_path_string (msg->src);
1177 message = gst_error_get_message (err->domain, err->code);
1178
1179 if (debug)
1180 full_message =
1181 g_strdup_printf ("Error from element %s: %s\n%s\n%s", name, message,
1182 err->message, debug);
1183 else
1184 full_message =
1185 g_strdup_printf ("Error from element %s: %s\n%s", name, message,
1186 err->message);
1187
1188 GST_ERROR_OBJECT (self, "ERROR: from element %s: %s\n", name, err->message);
1189 if (debug != NULL)
1190 GST_ERROR_OBJECT (self, "Additional debug info:\n%s\n", debug);
1191
1192 player_err =
1193 g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1194 full_message);
1195 emit_error (self, player_err);
1196
1197 g_clear_error (&err);
1198 g_free (debug);
1199 g_free (name);
1200 g_free (full_message);
1201 g_free (message);
1202 }
1203
1204 static void
warning_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1205 warning_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1206 {
1207 GstPlayer *self = GST_PLAYER (user_data);
1208 GError *err, *player_err;
1209 gchar *name, *debug, *message, *full_message;
1210
1211 dump_dot_file (self, "warning");
1212
1213 gst_message_parse_warning (msg, &err, &debug);
1214
1215 name = gst_object_get_path_string (msg->src);
1216 message = gst_error_get_message (err->domain, err->code);
1217
1218 if (debug)
1219 full_message =
1220 g_strdup_printf ("Warning from element %s: %s\n%s\n%s", name, message,
1221 err->message, debug);
1222 else
1223 full_message =
1224 g_strdup_printf ("Warning from element %s: %s\n%s", name, message,
1225 err->message);
1226
1227 GST_WARNING_OBJECT (self, "WARNING: from element %s: %s\n", name,
1228 err->message);
1229 if (debug != NULL)
1230 GST_WARNING_OBJECT (self, "Additional debug info:\n%s\n", debug);
1231
1232 player_err =
1233 g_error_new_literal (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1234 full_message);
1235 emit_warning (self, player_err);
1236
1237 g_clear_error (&err);
1238 g_free (debug);
1239 g_free (name);
1240 g_free (full_message);
1241 g_free (message);
1242 }
1243
1244 static void
eos_dispatch(gpointer user_data)1245 eos_dispatch (gpointer user_data)
1246 {
1247 GstPlayer *player = user_data;
1248
1249 if (player->inhibit_sigs)
1250 return;
1251
1252 g_signal_emit (player, signals[SIGNAL_END_OF_STREAM], 0);
1253 }
1254
1255 static void
eos_cb(G_GNUC_UNUSED GstBus * bus,G_GNUC_UNUSED GstMessage * msg,gpointer user_data)1256 eos_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1257 gpointer user_data)
1258 {
1259 GstPlayer *self = GST_PLAYER (user_data);
1260
1261 GST_DEBUG_OBJECT (self, "End of stream");
1262
1263 tick_cb (self);
1264 remove_tick_source (self);
1265
1266 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1267 signals[SIGNAL_END_OF_STREAM], 0, NULL, NULL, NULL) != 0) {
1268 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1269 eos_dispatch, g_object_ref (self), (GDestroyNotify) g_object_unref);
1270 }
1271 change_state (self, GST_PLAYER_STATE_STOPPED);
1272 self->buffering = 100;
1273 self->is_eos = TRUE;
1274 }
1275
1276 typedef struct
1277 {
1278 GstPlayer *player;
1279 gint percent;
1280 } BufferingSignalData;
1281
1282 static void
buffering_dispatch(gpointer user_data)1283 buffering_dispatch (gpointer user_data)
1284 {
1285 BufferingSignalData *data = user_data;
1286
1287 if (data->player->inhibit_sigs)
1288 return;
1289
1290 if (data->player->target_state >= GST_STATE_PAUSED) {
1291 g_signal_emit (data->player, signals[SIGNAL_BUFFERING], 0, data->percent);
1292 }
1293 }
1294
1295 static void
buffering_signal_data_free(BufferingSignalData * data)1296 buffering_signal_data_free (BufferingSignalData * data)
1297 {
1298 g_object_unref (data->player);
1299 g_free (data);
1300 }
1301
1302 static void
buffering_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1303 buffering_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1304 {
1305 GstPlayer *self = GST_PLAYER (user_data);
1306 gint percent;
1307
1308 if (self->target_state < GST_STATE_PAUSED)
1309 return;
1310 if (self->is_live)
1311 return;
1312
1313 gst_message_parse_buffering (msg, &percent);
1314 GST_LOG_OBJECT (self, "Buffering %d%%", percent);
1315
1316 if (percent < 100 && self->target_state >= GST_STATE_PAUSED) {
1317 GstStateChangeReturn state_ret;
1318
1319 GST_DEBUG_OBJECT (self, "Waiting for buffering to finish");
1320 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
1321
1322 if (state_ret == GST_STATE_CHANGE_FAILURE) {
1323 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1324 "Failed to handle buffering"));
1325 return;
1326 }
1327
1328 change_state (self, GST_PLAYER_STATE_BUFFERING);
1329 }
1330
1331 if (self->buffering != percent) {
1332 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1333 signals[SIGNAL_BUFFERING], 0, NULL, NULL, NULL) != 0) {
1334 BufferingSignalData *data = g_new (BufferingSignalData, 1);
1335
1336 data->player = g_object_ref (self);
1337 data->percent = percent;
1338 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1339 buffering_dispatch, data,
1340 (GDestroyNotify) buffering_signal_data_free);
1341 }
1342
1343 self->buffering = percent;
1344 }
1345
1346
1347 g_mutex_lock (&self->lock);
1348 if (percent == 100 && (self->seek_position != GST_CLOCK_TIME_NONE ||
1349 self->seek_pending)) {
1350 g_mutex_unlock (&self->lock);
1351
1352 GST_DEBUG_OBJECT (self, "Buffering finished - seek pending");
1353 } else if (percent == 100 && self->target_state >= GST_STATE_PLAYING
1354 && self->current_state >= GST_STATE_PAUSED) {
1355 GstStateChangeReturn state_ret;
1356
1357 g_mutex_unlock (&self->lock);
1358
1359 GST_DEBUG_OBJECT (self, "Buffering finished - going to PLAYING");
1360 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
1361 /* Application state change is happening when the state change happened */
1362 if (state_ret == GST_STATE_CHANGE_FAILURE)
1363 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1364 "Failed to handle buffering"));
1365 } else if (percent == 100 && self->target_state >= GST_STATE_PAUSED) {
1366 g_mutex_unlock (&self->lock);
1367
1368 GST_DEBUG_OBJECT (self, "Buffering finished - staying PAUSED");
1369 change_state (self, GST_PLAYER_STATE_PAUSED);
1370 } else {
1371 g_mutex_unlock (&self->lock);
1372 }
1373 }
1374
1375 static void
clock_lost_cb(G_GNUC_UNUSED GstBus * bus,G_GNUC_UNUSED GstMessage * msg,gpointer user_data)1376 clock_lost_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1377 gpointer user_data)
1378 {
1379 GstPlayer *self = GST_PLAYER (user_data);
1380 GstStateChangeReturn state_ret;
1381
1382 GST_DEBUG_OBJECT (self, "Clock lost");
1383 if (self->target_state >= GST_STATE_PLAYING) {
1384 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
1385 if (state_ret != GST_STATE_CHANGE_FAILURE)
1386 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
1387
1388 if (state_ret == GST_STATE_CHANGE_FAILURE)
1389 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1390 "Failed to handle clock loss"));
1391 }
1392 }
1393
1394 typedef struct
1395 {
1396 GstPlayer *player;
1397 gint width, height;
1398 } VideoDimensionsChangedSignalData;
1399
1400 static void
video_dimensions_changed_dispatch(gpointer user_data)1401 video_dimensions_changed_dispatch (gpointer user_data)
1402 {
1403 VideoDimensionsChangedSignalData *data = user_data;
1404
1405 if (data->player->inhibit_sigs)
1406 return;
1407
1408 if (data->player->target_state >= GST_STATE_PAUSED) {
1409 g_signal_emit (data->player, signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED], 0,
1410 data->width, data->height);
1411 }
1412 }
1413
1414 static void
video_dimensions_changed_signal_data_free(VideoDimensionsChangedSignalData * data)1415 video_dimensions_changed_signal_data_free (VideoDimensionsChangedSignalData *
1416 data)
1417 {
1418 g_object_unref (data->player);
1419 g_free (data);
1420 }
1421
1422 static void
check_video_dimensions_changed(GstPlayer * self)1423 check_video_dimensions_changed (GstPlayer * self)
1424 {
1425 GstElement *video_sink;
1426 GstPad *video_sink_pad;
1427 GstCaps *caps;
1428 GstVideoInfo info;
1429 gint width = 0, height = 0;
1430
1431 g_object_get (self->playbin, "video-sink", &video_sink, NULL);
1432 if (!video_sink)
1433 goto out;
1434
1435 video_sink_pad = gst_element_get_static_pad (video_sink, "sink");
1436 if (!video_sink_pad) {
1437 gst_object_unref (video_sink);
1438 goto out;
1439 }
1440
1441 caps = gst_pad_get_current_caps (video_sink_pad);
1442
1443 if (caps) {
1444 if (gst_video_info_from_caps (&info, caps)) {
1445 info.width = info.width * info.par_n / info.par_d;
1446
1447 GST_DEBUG_OBJECT (self, "Video dimensions changed: %dx%d", info.width,
1448 info.height);
1449 width = info.width;
1450 height = info.height;
1451 }
1452
1453 gst_caps_unref (caps);
1454 }
1455 gst_object_unref (video_sink_pad);
1456 gst_object_unref (video_sink);
1457
1458 out:
1459 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1460 signals[SIGNAL_VIDEO_DIMENSIONS_CHANGED], 0, NULL, NULL, NULL) != 0) {
1461 VideoDimensionsChangedSignalData *data =
1462 g_new (VideoDimensionsChangedSignalData, 1);
1463
1464 data->player = g_object_ref (self);
1465 data->width = width;
1466 data->height = height;
1467 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1468 video_dimensions_changed_dispatch, data,
1469 (GDestroyNotify) video_dimensions_changed_signal_data_free);
1470 }
1471 }
1472
1473 static void
notify_caps_cb(G_GNUC_UNUSED GObject * object,G_GNUC_UNUSED GParamSpec * pspec,gpointer user_data)1474 notify_caps_cb (G_GNUC_UNUSED GObject * object,
1475 G_GNUC_UNUSED GParamSpec * pspec, gpointer user_data)
1476 {
1477 GstPlayer *self = GST_PLAYER (user_data);
1478
1479 check_video_dimensions_changed (self);
1480 }
1481
1482 typedef struct
1483 {
1484 GstPlayer *player;
1485 GstClockTime duration;
1486 } DurationChangedSignalData;
1487
1488 static void
duration_changed_dispatch(gpointer user_data)1489 duration_changed_dispatch (gpointer user_data)
1490 {
1491 DurationChangedSignalData *data = user_data;
1492
1493 if (data->player->inhibit_sigs)
1494 return;
1495
1496 if (data->player->target_state >= GST_STATE_PAUSED) {
1497 g_signal_emit (data->player, signals[SIGNAL_DURATION_CHANGED], 0,
1498 data->duration);
1499 g_object_notify_by_pspec (G_OBJECT (data->player),
1500 param_specs[PROP_DURATION]);
1501 }
1502 }
1503
1504 static void
duration_changed_signal_data_free(DurationChangedSignalData * data)1505 duration_changed_signal_data_free (DurationChangedSignalData * data)
1506 {
1507 g_object_unref (data->player);
1508 g_free (data);
1509 }
1510
1511 static void
emit_duration_changed(GstPlayer * self,GstClockTime duration)1512 emit_duration_changed (GstPlayer * self, GstClockTime duration)
1513 {
1514 gboolean updated = FALSE;
1515
1516 if (self->cached_duration == duration)
1517 return;
1518
1519 GST_DEBUG_OBJECT (self, "Duration changed %" GST_TIME_FORMAT,
1520 GST_TIME_ARGS (duration));
1521
1522 self->cached_duration = duration;
1523 g_mutex_lock (&self->lock);
1524 if (self->media_info) {
1525 self->media_info->duration = duration;
1526 updated = TRUE;
1527 }
1528 g_mutex_unlock (&self->lock);
1529 if (updated) {
1530 emit_media_info_updated_signal (self);
1531 }
1532
1533 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1534 signals[SIGNAL_DURATION_CHANGED], 0, NULL, NULL, NULL) != 0) {
1535 DurationChangedSignalData *data = g_new (DurationChangedSignalData, 1);
1536
1537 data->player = g_object_ref (self);
1538 data->duration = duration;
1539 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1540 duration_changed_dispatch, data,
1541 (GDestroyNotify) duration_changed_signal_data_free);
1542 }
1543 }
1544
1545 typedef struct
1546 {
1547 GstPlayer *player;
1548 GstClockTime position;
1549 } SeekDoneSignalData;
1550
1551 static void
seek_done_dispatch(gpointer user_data)1552 seek_done_dispatch (gpointer user_data)
1553 {
1554 SeekDoneSignalData *data = user_data;
1555
1556 if (data->player->inhibit_sigs)
1557 return;
1558
1559 g_signal_emit (data->player, signals[SIGNAL_SEEK_DONE], 0, data->position);
1560 }
1561
1562 static void
seek_done_signal_data_free(SeekDoneSignalData * data)1563 seek_done_signal_data_free (SeekDoneSignalData * data)
1564 {
1565 g_object_unref (data->player);
1566 g_free (data);
1567 }
1568
1569 static void
emit_seek_done(GstPlayer * self)1570 emit_seek_done (GstPlayer * self)
1571 {
1572 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
1573 signals[SIGNAL_SEEK_DONE], 0, NULL, NULL, NULL) != 0) {
1574 SeekDoneSignalData *data = g_new (SeekDoneSignalData, 1);
1575
1576 data->player = g_object_ref (self);
1577 data->position = gst_player_get_position (self);
1578 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
1579 seek_done_dispatch, data, (GDestroyNotify) seek_done_signal_data_free);
1580 }
1581 }
1582
1583 static void
state_changed_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1584 state_changed_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1585 gpointer user_data)
1586 {
1587 GstPlayer *self = GST_PLAYER (user_data);
1588 GstState old_state, new_state, pending_state;
1589
1590 gst_message_parse_state_changed (msg, &old_state, &new_state, &pending_state);
1591
1592 if (GST_MESSAGE_SRC (msg) == GST_OBJECT (self->playbin)) {
1593 gchar *transition_name;
1594
1595 GST_DEBUG_OBJECT (self, "Changed state old: %s new: %s pending: %s",
1596 gst_element_state_get_name (old_state),
1597 gst_element_state_get_name (new_state),
1598 gst_element_state_get_name (pending_state));
1599
1600 transition_name = g_strdup_printf ("%s_%s",
1601 gst_element_state_get_name (old_state),
1602 gst_element_state_get_name (new_state));
1603 dump_dot_file (self, transition_name);
1604 g_free (transition_name);
1605
1606 self->current_state = new_state;
1607
1608 if (old_state == GST_STATE_READY && new_state == GST_STATE_PAUSED
1609 && pending_state == GST_STATE_VOID_PENDING) {
1610 GstElement *video_sink;
1611 GstPad *video_sink_pad;
1612 gint64 duration = -1;
1613
1614 GST_DEBUG_OBJECT (self, "Initial PAUSED - pre-rolled");
1615
1616 g_mutex_lock (&self->lock);
1617 if (self->media_info)
1618 g_object_unref (self->media_info);
1619 self->media_info = gst_player_media_info_create (self);
1620 g_mutex_unlock (&self->lock);
1621 emit_media_info_updated_signal (self);
1622
1623 g_object_get (self->playbin, "video-sink", &video_sink, NULL);
1624
1625 if (video_sink) {
1626 video_sink_pad = gst_element_get_static_pad (video_sink, "sink");
1627
1628 if (video_sink_pad) {
1629 g_signal_connect (video_sink_pad, "notify::caps",
1630 (GCallback) notify_caps_cb, self);
1631 gst_object_unref (video_sink_pad);
1632 }
1633 gst_object_unref (video_sink);
1634 }
1635
1636 check_video_dimensions_changed (self);
1637 if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME,
1638 &duration)) {
1639 emit_duration_changed (self, duration);
1640 } else {
1641 self->cached_duration = GST_CLOCK_TIME_NONE;
1642 }
1643 }
1644
1645 if (new_state == GST_STATE_PAUSED
1646 && pending_state == GST_STATE_VOID_PENDING) {
1647 remove_tick_source (self);
1648
1649 g_mutex_lock (&self->lock);
1650 if (self->seek_pending) {
1651 self->seek_pending = FALSE;
1652
1653 if (!self->media_info->seekable) {
1654 GST_DEBUG_OBJECT (self, "Media is not seekable");
1655 remove_seek_source (self);
1656 self->seek_position = GST_CLOCK_TIME_NONE;
1657 self->last_seek_time = GST_CLOCK_TIME_NONE;
1658 } else if (self->seek_source) {
1659 GST_DEBUG_OBJECT (self, "Seek finished but new seek is pending");
1660 gst_player_seek_internal_locked (self);
1661 } else {
1662 GST_DEBUG_OBJECT (self, "Seek finished");
1663 emit_seek_done (self);
1664 }
1665 }
1666
1667 if (self->seek_position != GST_CLOCK_TIME_NONE) {
1668 GST_DEBUG_OBJECT (self, "Seeking now that we reached PAUSED state");
1669 gst_player_seek_internal_locked (self);
1670 g_mutex_unlock (&self->lock);
1671 } else if (!self->seek_pending) {
1672 g_mutex_unlock (&self->lock);
1673
1674 tick_cb (self);
1675
1676 if (self->target_state >= GST_STATE_PLAYING && self->buffering == 100) {
1677 GstStateChangeReturn state_ret;
1678
1679 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
1680 if (state_ret == GST_STATE_CHANGE_FAILURE)
1681 emit_error (self, g_error_new (GST_PLAYER_ERROR,
1682 GST_PLAYER_ERROR_FAILED, "Failed to play"));
1683 } else if (self->buffering == 100) {
1684 change_state (self, GST_PLAYER_STATE_PAUSED);
1685 }
1686 } else {
1687 g_mutex_unlock (&self->lock);
1688 }
1689 } else if (new_state == GST_STATE_PLAYING
1690 && pending_state == GST_STATE_VOID_PENDING) {
1691
1692 /* If no seek is currently pending, add the tick source. This can happen
1693 * if we seeked already but the state-change message was still queued up */
1694 if (!self->seek_pending) {
1695 add_tick_source (self);
1696 change_state (self, GST_PLAYER_STATE_PLAYING);
1697 }
1698 } else if (new_state == GST_STATE_READY && old_state > GST_STATE_READY) {
1699 change_state (self, GST_PLAYER_STATE_STOPPED);
1700 } else {
1701 /* Otherwise we neither reached PLAYING nor PAUSED, so must
1702 * wait for something to happen... i.e. are BUFFERING now */
1703 change_state (self, GST_PLAYER_STATE_BUFFERING);
1704 }
1705 }
1706 }
1707
1708 static void
duration_changed_cb(G_GNUC_UNUSED GstBus * bus,G_GNUC_UNUSED GstMessage * msg,gpointer user_data)1709 duration_changed_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1710 gpointer user_data)
1711 {
1712 GstPlayer *self = GST_PLAYER (user_data);
1713 gint64 duration = GST_CLOCK_TIME_NONE;
1714
1715 if (gst_element_query_duration (self->playbin, GST_FORMAT_TIME, &duration)) {
1716 emit_duration_changed (self, duration);
1717 }
1718 }
1719
1720 static void
latency_cb(G_GNUC_UNUSED GstBus * bus,G_GNUC_UNUSED GstMessage * msg,gpointer user_data)1721 latency_cb (G_GNUC_UNUSED GstBus * bus, G_GNUC_UNUSED GstMessage * msg,
1722 gpointer user_data)
1723 {
1724 GstPlayer *self = GST_PLAYER (user_data);
1725
1726 GST_DEBUG_OBJECT (self, "Latency changed");
1727
1728 gst_bin_recalculate_latency (GST_BIN (self->playbin));
1729 }
1730
1731 static void
request_state_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1732 request_state_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1733 gpointer user_data)
1734 {
1735 GstPlayer *self = GST_PLAYER (user_data);
1736 GstState state;
1737 GstStateChangeReturn state_ret;
1738
1739 gst_message_parse_request_state (msg, &state);
1740
1741 GST_DEBUG_OBJECT (self, "State %s requested",
1742 gst_element_state_get_name (state));
1743
1744 self->target_state = state;
1745 state_ret = gst_element_set_state (self->playbin, state);
1746 if (state_ret == GST_STATE_CHANGE_FAILURE)
1747 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
1748 "Failed to change to requested state %s",
1749 gst_element_state_get_name (state)));
1750 }
1751
1752 static void
media_info_update(GstPlayer * self,GstPlayerMediaInfo * info)1753 media_info_update (GstPlayer * self, GstPlayerMediaInfo * info)
1754 {
1755 g_free (info->title);
1756 info->title = get_from_tags (self, info, get_title);
1757
1758 g_free (info->container);
1759 info->container = get_from_tags (self, info, get_container_format);
1760
1761 if (info->image_sample)
1762 gst_sample_unref (info->image_sample);
1763 info->image_sample = get_from_tags (self, info, get_cover_sample);
1764
1765 GST_DEBUG_OBJECT (self, "title: %s, container: %s "
1766 "image_sample: %p", info->title, info->container, info->image_sample);
1767 }
1768
1769 static void
tags_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1770 tags_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1771 {
1772 GstPlayer *self = GST_PLAYER (user_data);
1773 GstTagList *tags = NULL;
1774
1775 gst_message_parse_tag (msg, &tags);
1776
1777 GST_DEBUG_OBJECT (self, "received %s tags",
1778 gst_tag_list_get_scope (tags) ==
1779 GST_TAG_SCOPE_GLOBAL ? "global" : "stream");
1780
1781 if (gst_tag_list_get_scope (tags) == GST_TAG_SCOPE_GLOBAL) {
1782 g_mutex_lock (&self->lock);
1783 if (self->media_info) {
1784 if (self->media_info->tags)
1785 gst_tag_list_unref (self->media_info->tags);
1786 self->media_info->tags = gst_tag_list_ref (tags);
1787 media_info_update (self, self->media_info);
1788 g_mutex_unlock (&self->lock);
1789 emit_media_info_updated_signal (self);
1790 } else {
1791 if (self->global_tags)
1792 gst_tag_list_unref (self->global_tags);
1793 self->global_tags = gst_tag_list_ref (tags);
1794 g_mutex_unlock (&self->lock);
1795 }
1796 }
1797
1798 gst_tag_list_unref (tags);
1799 }
1800
1801 static void
element_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1802 element_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg, gpointer user_data)
1803 {
1804 GstPlayer *self = GST_PLAYER (user_data);
1805 const GstStructure *s;
1806
1807 s = gst_message_get_structure (msg);
1808 if (gst_structure_has_name (s, "redirect")) {
1809 const gchar *new_location;
1810
1811 new_location = gst_structure_get_string (s, "new-location");
1812 if (!new_location) {
1813 const GValue *locations_list, *location_val;
1814 guint i, size;
1815
1816 locations_list = gst_structure_get_value (s, "locations");
1817 size = gst_value_list_get_size (locations_list);
1818 for (i = 0; i < size; ++i) {
1819 const GstStructure *location_s;
1820
1821 location_val = gst_value_list_get_value (locations_list, i);
1822 if (!GST_VALUE_HOLDS_STRUCTURE (location_val))
1823 continue;
1824
1825 location_s = (const GstStructure *) g_value_get_boxed (location_val);
1826 if (!gst_structure_has_name (location_s, "redirect"))
1827 continue;
1828
1829 new_location = gst_structure_get_string (location_s, "new-location");
1830 if (new_location)
1831 break;
1832 }
1833 }
1834
1835 if (new_location) {
1836 GstState target_state;
1837
1838 GST_DEBUG_OBJECT (self, "Redirect to '%s'", new_location);
1839
1840 /* Remember target state and restore after setting the URI */
1841 target_state = self->target_state;
1842
1843 gst_player_stop_internal (self, TRUE);
1844
1845 g_mutex_lock (&self->lock);
1846 g_free (self->redirect_uri);
1847 self->redirect_uri = g_strdup (new_location);
1848 g_object_set (self->playbin, "uri", self->redirect_uri, NULL);
1849 g_mutex_unlock (&self->lock);
1850
1851 if (target_state == GST_STATE_PAUSED)
1852 gst_player_pause_internal (self);
1853 else if (target_state == GST_STATE_PLAYING)
1854 gst_player_play_internal (self);
1855 }
1856 }
1857 }
1858
1859 /* Must be called with lock */
1860 static gboolean
update_stream_collection(GstPlayer * self,GstStreamCollection * collection)1861 update_stream_collection (GstPlayer * self, GstStreamCollection * collection)
1862 {
1863 if (self->collection && self->collection == collection)
1864 return FALSE;
1865
1866 if (self->collection && self->stream_notify_id)
1867 g_signal_handler_disconnect (self->collection, self->stream_notify_id);
1868
1869 gst_object_replace ((GstObject **) & self->collection,
1870 (GstObject *) collection);
1871 if (self->media_info) {
1872 gst_object_unref (self->media_info);
1873 self->media_info = gst_player_media_info_create (self);
1874 }
1875
1876 self->stream_notify_id =
1877 g_signal_connect (self->collection, "stream-notify",
1878 G_CALLBACK (stream_notify_cb), self);
1879
1880 return TRUE;
1881 }
1882
1883 static void
stream_collection_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1884 stream_collection_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1885 gpointer user_data)
1886 {
1887 GstPlayer *self = GST_PLAYER (user_data);
1888 GstStreamCollection *collection = NULL;
1889 gboolean updated = FALSE;
1890
1891 gst_message_parse_stream_collection (msg, &collection);
1892
1893 if (!collection)
1894 return;
1895
1896 g_mutex_lock (&self->lock);
1897 updated = update_stream_collection (self, collection);
1898 gst_object_unref (collection);
1899 g_mutex_unlock (&self->lock);
1900
1901 if (self->media_info && updated)
1902 emit_media_info_updated_signal (self);
1903 }
1904
1905 static void
streams_selected_cb(G_GNUC_UNUSED GstBus * bus,GstMessage * msg,gpointer user_data)1906 streams_selected_cb (G_GNUC_UNUSED GstBus * bus, GstMessage * msg,
1907 gpointer user_data)
1908 {
1909 GstPlayer *self = GST_PLAYER (user_data);
1910 GstStreamCollection *collection = NULL;
1911 gboolean updated = FALSE;
1912 guint i, len;
1913
1914 gst_message_parse_streams_selected (msg, &collection);
1915
1916 if (!collection)
1917 return;
1918
1919 g_mutex_lock (&self->lock);
1920 updated = update_stream_collection (self, collection);
1921 gst_object_unref (collection);
1922
1923 g_free (self->video_sid);
1924 g_free (self->audio_sid);
1925 g_free (self->subtitle_sid);
1926 self->video_sid = NULL;
1927 self->audio_sid = NULL;
1928 self->subtitle_sid = NULL;
1929
1930 len = gst_message_streams_selected_get_size (msg);
1931 for (i = 0; i < len; i++) {
1932 GstStream *stream;
1933 GstStreamType stream_type;
1934 const gchar *stream_id;
1935 gchar **current_sid;
1936 stream = gst_message_streams_selected_get_stream (msg, i);
1937 stream_type = gst_stream_get_stream_type (stream);
1938 stream_id = gst_stream_get_stream_id (stream);
1939 if (stream_type & GST_STREAM_TYPE_AUDIO)
1940 current_sid = &self->audio_sid;
1941 else if (stream_type & GST_STREAM_TYPE_VIDEO)
1942 current_sid = &self->video_sid;
1943 else if (stream_type & GST_STREAM_TYPE_TEXT)
1944 current_sid = &self->subtitle_sid;
1945 else {
1946 GST_WARNING_OBJECT (self,
1947 "Unknown stream-id %s with type 0x%x", stream_id, stream_type);
1948 continue;
1949 }
1950
1951 if (G_UNLIKELY (*current_sid)) {
1952 GST_FIXME_OBJECT (self,
1953 "Multiple streams are selected for type %s, choose the first one",
1954 gst_stream_type_get_name (stream_type));
1955 continue;
1956 }
1957
1958 *current_sid = g_strdup (stream_id);
1959 }
1960 g_mutex_unlock (&self->lock);
1961
1962 if (self->media_info && updated)
1963 emit_media_info_updated_signal (self);
1964 }
1965
1966 static void
player_set_flag(GstPlayer * self,gint pos)1967 player_set_flag (GstPlayer * self, gint pos)
1968 {
1969 gint flags;
1970
1971 g_object_get (self->playbin, "flags", &flags, NULL);
1972 flags |= pos;
1973 g_object_set (self->playbin, "flags", flags, NULL);
1974
1975 GST_DEBUG_OBJECT (self, "setting flags=%#x", flags);
1976 }
1977
1978 static void
player_clear_flag(GstPlayer * self,gint pos)1979 player_clear_flag (GstPlayer * self, gint pos)
1980 {
1981 gint flags;
1982
1983 g_object_get (self->playbin, "flags", &flags, NULL);
1984 flags &= ~pos;
1985 g_object_set (self->playbin, "flags", flags, NULL);
1986
1987 GST_DEBUG_OBJECT (self, "setting flags=%#x", flags);
1988 }
1989
1990 typedef struct
1991 {
1992 GstPlayer *player;
1993 GstPlayerMediaInfo *info;
1994 } MediaInfoUpdatedSignalData;
1995
1996 static void
media_info_updated_dispatch(gpointer user_data)1997 media_info_updated_dispatch (gpointer user_data)
1998 {
1999 MediaInfoUpdatedSignalData *data = user_data;
2000
2001 if (data->player->inhibit_sigs)
2002 return;
2003
2004 if (data->player->target_state >= GST_STATE_PAUSED) {
2005 g_signal_emit (data->player, signals[SIGNAL_MEDIA_INFO_UPDATED], 0,
2006 data->info);
2007 }
2008 }
2009
2010 static void
free_media_info_updated_signal_data(MediaInfoUpdatedSignalData * data)2011 free_media_info_updated_signal_data (MediaInfoUpdatedSignalData * data)
2012 {
2013 g_object_unref (data->player);
2014 g_object_unref (data->info);
2015 g_free (data);
2016 }
2017
2018 /*
2019 * emit_media_info_updated_signal:
2020 *
2021 * create a new copy of self->media_info object and emits the newly created
2022 * copy to user application. The newly created media_info will be unref'ed
2023 * as part of signal finalize method.
2024 */
2025 static void
emit_media_info_updated_signal(GstPlayer * self)2026 emit_media_info_updated_signal (GstPlayer * self)
2027 {
2028 MediaInfoUpdatedSignalData *data = g_new (MediaInfoUpdatedSignalData, 1);
2029 data->player = g_object_ref (self);
2030 g_mutex_lock (&self->lock);
2031 data->info = gst_player_media_info_copy (self->media_info);
2032 g_mutex_unlock (&self->lock);
2033
2034 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
2035 media_info_updated_dispatch, data,
2036 (GDestroyNotify) free_media_info_updated_signal_data);
2037 }
2038
2039 static GstCaps *
get_caps(GstPlayer * self,gint stream_index,GType type)2040 get_caps (GstPlayer * self, gint stream_index, GType type)
2041 {
2042 GstPad *pad = NULL;
2043 GstCaps *caps = NULL;
2044
2045 if (type == GST_TYPE_PLAYER_VIDEO_INFO)
2046 g_signal_emit_by_name (G_OBJECT (self->playbin),
2047 "get-video-pad", stream_index, &pad);
2048 else if (type == GST_TYPE_PLAYER_AUDIO_INFO)
2049 g_signal_emit_by_name (G_OBJECT (self->playbin),
2050 "get-audio-pad", stream_index, &pad);
2051 else
2052 g_signal_emit_by_name (G_OBJECT (self->playbin),
2053 "get-text-pad", stream_index, &pad);
2054
2055 if (pad) {
2056 caps = gst_pad_get_current_caps (pad);
2057 gst_object_unref (pad);
2058 }
2059
2060 return caps;
2061 }
2062
2063 static void
gst_player_subtitle_info_update(GstPlayer * self,GstPlayerStreamInfo * stream_info)2064 gst_player_subtitle_info_update (GstPlayer * self,
2065 GstPlayerStreamInfo * stream_info)
2066 {
2067 GstPlayerSubtitleInfo *info = (GstPlayerSubtitleInfo *) stream_info;
2068
2069 if (stream_info->tags) {
2070
2071 /* free the old language info */
2072 g_free (info->language);
2073 info->language = NULL;
2074
2075 /* First try to get the language full name from tag, if name is not
2076 * available then try language code. If we find the language code
2077 * then use gstreamer api to translate code to full name.
2078 */
2079 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME,
2080 &info->language);
2081 if (!info->language) {
2082 gchar *lang_code = NULL;
2083
2084 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE,
2085 &lang_code);
2086 if (lang_code) {
2087 info->language = g_strdup (gst_tag_get_language_name (lang_code));
2088 g_free (lang_code);
2089 }
2090 }
2091
2092 /* If we are still failed to find language name then check if external
2093 * subtitle is loaded and compare the stream index between current sub
2094 * stream index with our stream index and if matches then declare it as
2095 * external subtitle and use the filename.
2096 */
2097 if (!info->language) {
2098 gint text_index = -1;
2099 gchar *suburi = NULL;
2100
2101 g_object_get (G_OBJECT (self->playbin), "current-suburi", &suburi, NULL);
2102 if (suburi) {
2103 if (self->use_playbin3) {
2104 if (g_str_equal (self->subtitle_sid, stream_info->stream_id))
2105 info->language = g_path_get_basename (suburi);
2106 } else {
2107 g_object_get (G_OBJECT (self->playbin), "current-text", &text_index,
2108 NULL);
2109 if (text_index == gst_player_stream_info_get_index (stream_info))
2110 info->language = g_path_get_basename (suburi);
2111 }
2112 g_free (suburi);
2113 }
2114 }
2115
2116 } else {
2117 g_free (info->language);
2118 info->language = NULL;
2119 }
2120
2121 GST_DEBUG_OBJECT (self, "language=%s", info->language);
2122 }
2123
2124 static void
gst_player_video_info_update(GstPlayer * self,GstPlayerStreamInfo * stream_info)2125 gst_player_video_info_update (GstPlayer * self,
2126 GstPlayerStreamInfo * stream_info)
2127 {
2128 GstPlayerVideoInfo *info = (GstPlayerVideoInfo *) stream_info;
2129
2130 if (stream_info->caps) {
2131 GstStructure *s;
2132
2133 s = gst_caps_get_structure (stream_info->caps, 0);
2134 if (s) {
2135 gint width, height;
2136 gint fps_n, fps_d;
2137 gint par_n, par_d;
2138
2139 if (gst_structure_get_int (s, "width", &width))
2140 info->width = width;
2141 else
2142 info->width = -1;
2143
2144 if (gst_structure_get_int (s, "height", &height))
2145 info->height = height;
2146 else
2147 info->height = -1;
2148
2149 if (gst_structure_get_fraction (s, "framerate", &fps_n, &fps_d)) {
2150 info->framerate_num = fps_n;
2151 info->framerate_denom = fps_d;
2152 } else {
2153 info->framerate_num = 0;
2154 info->framerate_denom = 1;
2155 }
2156
2157
2158 if (gst_structure_get_fraction (s, "pixel-aspect-ratio", &par_n, &par_d)) {
2159 info->par_num = par_n;
2160 info->par_denom = par_d;
2161 } else {
2162 info->par_num = 1;
2163 info->par_denom = 1;
2164 }
2165 }
2166 } else {
2167 info->width = info->height = -1;
2168 info->par_num = info->par_denom = 1;
2169 info->framerate_num = 0;
2170 info->framerate_denom = 1;
2171 }
2172
2173 if (stream_info->tags) {
2174 guint bitrate, max_bitrate;
2175
2176 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate))
2177 info->bitrate = bitrate;
2178 else
2179 info->bitrate = -1;
2180
2181 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_MAXIMUM_BITRATE,
2182 &max_bitrate) || gst_tag_list_get_uint (stream_info->tags,
2183 GST_TAG_NOMINAL_BITRATE, &max_bitrate))
2184 info->max_bitrate = max_bitrate;
2185 else
2186 info->max_bitrate = -1;
2187 } else {
2188 info->bitrate = info->max_bitrate = -1;
2189 }
2190
2191 GST_DEBUG_OBJECT (self, "width=%d height=%d fps=%.2f par=%d:%d "
2192 "bitrate=%d max_bitrate=%d", info->width, info->height,
2193 (gdouble) info->framerate_num / info->framerate_denom,
2194 info->par_num, info->par_denom, info->bitrate, info->max_bitrate);
2195 }
2196
2197 static void
gst_player_audio_info_update(GstPlayer * self,GstPlayerStreamInfo * stream_info)2198 gst_player_audio_info_update (GstPlayer * self,
2199 GstPlayerStreamInfo * stream_info)
2200 {
2201 GstPlayerAudioInfo *info = (GstPlayerAudioInfo *) stream_info;
2202
2203 if (stream_info->caps) {
2204 GstStructure *s;
2205
2206 s = gst_caps_get_structure (stream_info->caps, 0);
2207 if (s) {
2208 gint rate, channels;
2209
2210 if (gst_structure_get_int (s, "rate", &rate))
2211 info->sample_rate = rate;
2212 else
2213 info->sample_rate = -1;
2214
2215 if (gst_structure_get_int (s, "channels", &channels))
2216 info->channels = channels;
2217 else
2218 info->channels = 0;
2219 }
2220 } else {
2221 info->sample_rate = -1;
2222 info->channels = 0;
2223 }
2224
2225 if (stream_info->tags) {
2226 guint bitrate, max_bitrate;
2227
2228 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_BITRATE, &bitrate))
2229 info->bitrate = bitrate;
2230 else
2231 info->bitrate = -1;
2232
2233 if (gst_tag_list_get_uint (stream_info->tags, GST_TAG_MAXIMUM_BITRATE,
2234 &max_bitrate) || gst_tag_list_get_uint (stream_info->tags,
2235 GST_TAG_NOMINAL_BITRATE, &max_bitrate))
2236 info->max_bitrate = max_bitrate;
2237 else
2238 info->max_bitrate = -1;
2239
2240 /* if we have old language the free it */
2241 g_free (info->language);
2242 info->language = NULL;
2243
2244 /* First try to get the language full name from tag, if name is not
2245 * available then try language code. If we find the language code
2246 * then use gstreamer api to translate code to full name.
2247 */
2248 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_NAME,
2249 &info->language);
2250 if (!info->language) {
2251 gchar *lang_code = NULL;
2252
2253 gst_tag_list_get_string (stream_info->tags, GST_TAG_LANGUAGE_CODE,
2254 &lang_code);
2255 if (lang_code) {
2256 info->language = g_strdup (gst_tag_get_language_name (lang_code));
2257 g_free (lang_code);
2258 }
2259 }
2260 } else {
2261 g_free (info->language);
2262 info->language = NULL;
2263 info->max_bitrate = info->bitrate = -1;
2264 }
2265
2266 GST_DEBUG_OBJECT (self, "language=%s rate=%d channels=%d bitrate=%d "
2267 "max_bitrate=%d", info->language, info->sample_rate, info->channels,
2268 info->bitrate, info->max_bitrate);
2269 }
2270
2271 static GstPlayerStreamInfo *
gst_player_stream_info_find(GstPlayerMediaInfo * media_info,GType type,gint stream_index)2272 gst_player_stream_info_find (GstPlayerMediaInfo * media_info,
2273 GType type, gint stream_index)
2274 {
2275 GList *list, *l;
2276 GstPlayerStreamInfo *info = NULL;
2277
2278 if (!media_info)
2279 return NULL;
2280
2281 list = gst_player_media_info_get_stream_list (media_info);
2282 for (l = list; l != NULL; l = l->next) {
2283 info = (GstPlayerStreamInfo *) l->data;
2284 if ((G_OBJECT_TYPE (info) == type) && (info->stream_index == stream_index)) {
2285 return info;
2286 }
2287 }
2288
2289 return NULL;
2290 }
2291
2292 static GstPlayerStreamInfo *
gst_player_stream_info_find_from_stream_id(GstPlayerMediaInfo * media_info,const gchar * stream_id)2293 gst_player_stream_info_find_from_stream_id (GstPlayerMediaInfo * media_info,
2294 const gchar * stream_id)
2295 {
2296 GList *list, *l;
2297 GstPlayerStreamInfo *info = NULL;
2298
2299 if (!media_info)
2300 return NULL;
2301
2302 list = gst_player_media_info_get_stream_list (media_info);
2303 for (l = list; l != NULL; l = l->next) {
2304 info = (GstPlayerStreamInfo *) l->data;
2305 if (g_str_equal (info->stream_id, stream_id)) {
2306 return info;
2307 }
2308 }
2309
2310 return NULL;
2311 }
2312
2313 static gboolean
is_track_enabled(GstPlayer * self,gint pos)2314 is_track_enabled (GstPlayer * self, gint pos)
2315 {
2316 gint flags;
2317
2318 g_object_get (G_OBJECT (self->playbin), "flags", &flags, NULL);
2319
2320 if ((flags & pos))
2321 return TRUE;
2322
2323 return FALSE;
2324 }
2325
2326 static GstPlayerStreamInfo *
gst_player_stream_info_get_current(GstPlayer * self,const gchar * prop,GType type)2327 gst_player_stream_info_get_current (GstPlayer * self, const gchar * prop,
2328 GType type)
2329 {
2330 gint current;
2331 GstPlayerStreamInfo *info;
2332
2333 if (!self->media_info)
2334 return NULL;
2335
2336 g_object_get (G_OBJECT (self->playbin), prop, ¤t, NULL);
2337 g_mutex_lock (&self->lock);
2338 info = gst_player_stream_info_find (self->media_info, type, current);
2339 if (info)
2340 info = gst_player_stream_info_copy (info);
2341 g_mutex_unlock (&self->lock);
2342
2343 return info;
2344 }
2345
2346 static GstPlayerStreamInfo *
gst_player_stream_info_get_current_from_stream_id(GstPlayer * self,const gchar * stream_id,GType type)2347 gst_player_stream_info_get_current_from_stream_id (GstPlayer * self,
2348 const gchar * stream_id, GType type)
2349 {
2350 GstPlayerStreamInfo *info;
2351
2352 if (!self->media_info || !stream_id)
2353 return NULL;
2354
2355 g_mutex_lock (&self->lock);
2356 info =
2357 gst_player_stream_info_find_from_stream_id (self->media_info, stream_id);
2358 if (info && G_OBJECT_TYPE (info) == type)
2359 info = gst_player_stream_info_copy (info);
2360 else
2361 info = NULL;
2362 g_mutex_unlock (&self->lock);
2363
2364 return info;
2365 }
2366
2367 static void
stream_notify_cb(GstStreamCollection * collection,GstStream * stream,GParamSpec * pspec,GstPlayer * self)2368 stream_notify_cb (GstStreamCollection * collection, GstStream * stream,
2369 GParamSpec * pspec, GstPlayer * self)
2370 {
2371 GstPlayerStreamInfo *info;
2372 const gchar *stream_id;
2373 gboolean emit_signal = FALSE;
2374
2375 if (!self->media_info)
2376 return;
2377
2378 if (G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_CAPS &&
2379 G_PARAM_SPEC_VALUE_TYPE (pspec) != GST_TYPE_TAG_LIST)
2380 return;
2381
2382 stream_id = gst_stream_get_stream_id (stream);
2383 g_mutex_lock (&self->lock);
2384 info =
2385 gst_player_stream_info_find_from_stream_id (self->media_info, stream_id);
2386 if (info) {
2387 gst_player_stream_info_update_from_stream (self, info, stream);
2388 emit_signal = TRUE;
2389 }
2390 g_mutex_unlock (&self->lock);
2391
2392 if (emit_signal)
2393 emit_media_info_updated_signal (self);
2394 }
2395
2396 static void
gst_player_stream_info_update(GstPlayer * self,GstPlayerStreamInfo * s)2397 gst_player_stream_info_update (GstPlayer * self, GstPlayerStreamInfo * s)
2398 {
2399 if (GST_IS_PLAYER_VIDEO_INFO (s))
2400 gst_player_video_info_update (self, s);
2401 else if (GST_IS_PLAYER_AUDIO_INFO (s))
2402 gst_player_audio_info_update (self, s);
2403 else
2404 gst_player_subtitle_info_update (self, s);
2405 }
2406
2407 static gchar *
stream_info_get_codec(GstPlayerStreamInfo * s)2408 stream_info_get_codec (GstPlayerStreamInfo * s)
2409 {
2410 const gchar *type;
2411 GstTagList *tags;
2412 gchar *codec = NULL;
2413
2414 if (GST_IS_PLAYER_VIDEO_INFO (s))
2415 type = GST_TAG_VIDEO_CODEC;
2416 else if (GST_IS_PLAYER_AUDIO_INFO (s))
2417 type = GST_TAG_AUDIO_CODEC;
2418 else
2419 type = GST_TAG_SUBTITLE_CODEC;
2420
2421 tags = gst_player_stream_info_get_tags (s);
2422 if (tags) {
2423 gst_tag_list_get_string (tags, type, &codec);
2424 if (!codec)
2425 gst_tag_list_get_string (tags, GST_TAG_CODEC, &codec);
2426 }
2427
2428 if (!codec) {
2429 GstCaps *caps;
2430 caps = gst_player_stream_info_get_caps (s);
2431 if (caps) {
2432 codec = gst_pb_utils_get_codec_description (caps);
2433 }
2434 }
2435
2436 return codec;
2437 }
2438
2439 static void
gst_player_stream_info_update_tags_and_caps(GstPlayer * self,GstPlayerStreamInfo * s)2440 gst_player_stream_info_update_tags_and_caps (GstPlayer * self,
2441 GstPlayerStreamInfo * s)
2442 {
2443 GstTagList *tags;
2444 gint stream_index;
2445
2446 stream_index = gst_player_stream_info_get_index (s);
2447
2448 if (GST_IS_PLAYER_VIDEO_INFO (s))
2449 g_signal_emit_by_name (self->playbin, "get-video-tags",
2450 stream_index, &tags);
2451 else if (GST_IS_PLAYER_AUDIO_INFO (s))
2452 g_signal_emit_by_name (self->playbin, "get-audio-tags",
2453 stream_index, &tags);
2454 else
2455 g_signal_emit_by_name (self->playbin, "get-text-tags", stream_index, &tags);
2456
2457 if (s->tags)
2458 gst_tag_list_unref (s->tags);
2459 s->tags = tags;
2460
2461 if (s->caps)
2462 gst_caps_unref (s->caps);
2463 s->caps = get_caps (self, stream_index, G_OBJECT_TYPE (s));
2464
2465 g_free (s->codec);
2466 s->codec = stream_info_get_codec (s);
2467
2468 GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p",
2469 gst_player_stream_info_get_stream_type (s), stream_index,
2470 s->tags, s->caps);
2471
2472 gst_player_stream_info_update (self, s);
2473 }
2474
2475 static void
gst_player_streams_info_create(GstPlayer * self,GstPlayerMediaInfo * media_info,const gchar * prop,GType type)2476 gst_player_streams_info_create (GstPlayer * self,
2477 GstPlayerMediaInfo * media_info, const gchar * prop, GType type)
2478 {
2479 gint i;
2480 gint total = -1;
2481 GstPlayerStreamInfo *s;
2482
2483 if (!media_info)
2484 return;
2485
2486 g_object_get (G_OBJECT (self->playbin), prop, &total, NULL);
2487
2488 GST_DEBUG_OBJECT (self, "%s: %d", prop, total);
2489
2490 for (i = 0; i < total; i++) {
2491 /* check if stream already exist in the list */
2492 s = gst_player_stream_info_find (media_info, type, i);
2493
2494 if (!s) {
2495 /* create a new stream info instance */
2496 s = gst_player_stream_info_new (i, type);
2497
2498 /* add the object in stream list */
2499 media_info->stream_list = g_list_append (media_info->stream_list, s);
2500
2501 /* based on type, add the object in its corresponding stream_ list */
2502 if (GST_IS_PLAYER_AUDIO_INFO (s))
2503 media_info->audio_stream_list = g_list_append
2504 (media_info->audio_stream_list, s);
2505 else if (GST_IS_PLAYER_VIDEO_INFO (s))
2506 media_info->video_stream_list = g_list_append
2507 (media_info->video_stream_list, s);
2508 else
2509 media_info->subtitle_stream_list = g_list_append
2510 (media_info->subtitle_stream_list, s);
2511
2512 GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d",
2513 gst_player_stream_info_get_stream_type (s), i);
2514 }
2515
2516 gst_player_stream_info_update_tags_and_caps (self, s);
2517 }
2518 }
2519
2520 static void
gst_player_stream_info_update_from_stream(GstPlayer * self,GstPlayerStreamInfo * s,GstStream * stream)2521 gst_player_stream_info_update_from_stream (GstPlayer * self,
2522 GstPlayerStreamInfo * s, GstStream * stream)
2523 {
2524 if (s->tags)
2525 gst_tag_list_unref (s->tags);
2526 s->tags = gst_stream_get_tags (stream);
2527
2528 if (s->caps)
2529 gst_caps_unref (s->caps);
2530 s->caps = gst_stream_get_caps (stream);
2531
2532 g_free (s->codec);
2533 s->codec = stream_info_get_codec (s);
2534
2535 GST_DEBUG_OBJECT (self, "%s index: %d tags: %p caps: %p",
2536 gst_player_stream_info_get_stream_type (s), s->stream_index,
2537 s->tags, s->caps);
2538
2539 gst_player_stream_info_update (self, s);
2540 }
2541
2542 static void
gst_player_streams_info_create_from_collection(GstPlayer * self,GstPlayerMediaInfo * media_info,GstStreamCollection * collection)2543 gst_player_streams_info_create_from_collection (GstPlayer * self,
2544 GstPlayerMediaInfo * media_info, GstStreamCollection * collection)
2545 {
2546 guint i;
2547 guint total;
2548 GstPlayerStreamInfo *s;
2549 guint n_audio = 0;
2550 guint n_video = 0;
2551 guint n_text = 0;
2552
2553 if (!media_info || !collection)
2554 return;
2555
2556 total = gst_stream_collection_get_size (collection);
2557
2558 for (i = 0; i < total; i++) {
2559 GstStream *stream = gst_stream_collection_get_stream (collection, i);
2560 GstStreamType stream_type = gst_stream_get_stream_type (stream);
2561 const gchar *stream_id = gst_stream_get_stream_id (stream);
2562
2563 if (stream_type & GST_STREAM_TYPE_AUDIO) {
2564 s = gst_player_stream_info_new (n_audio, GST_TYPE_PLAYER_AUDIO_INFO);
2565 n_audio++;
2566 } else if (stream_type & GST_STREAM_TYPE_VIDEO) {
2567 s = gst_player_stream_info_new (n_video, GST_TYPE_PLAYER_VIDEO_INFO);
2568 n_video++;
2569 } else if (stream_type & GST_STREAM_TYPE_TEXT) {
2570 s = gst_player_stream_info_new (n_text, GST_TYPE_PLAYER_SUBTITLE_INFO);
2571 n_text++;
2572 } else {
2573 GST_DEBUG_OBJECT (self, "Unknown type stream %d", i);
2574 continue;
2575 }
2576
2577 s->stream_id = g_strdup (stream_id);
2578
2579 /* add the object in stream list */
2580 media_info->stream_list = g_list_append (media_info->stream_list, s);
2581
2582 /* based on type, add the object in its corresponding stream_ list */
2583 if (GST_IS_PLAYER_AUDIO_INFO (s))
2584 media_info->audio_stream_list = g_list_append
2585 (media_info->audio_stream_list, s);
2586 else if (GST_IS_PLAYER_VIDEO_INFO (s))
2587 media_info->video_stream_list = g_list_append
2588 (media_info->video_stream_list, s);
2589 else
2590 media_info->subtitle_stream_list = g_list_append
2591 (media_info->subtitle_stream_list, s);
2592
2593 GST_DEBUG_OBJECT (self, "create %s stream stream_index: %d",
2594 gst_player_stream_info_get_stream_type (s), s->stream_index);
2595
2596 gst_player_stream_info_update_from_stream (self, s, stream);
2597 }
2598 }
2599
2600 static void
video_changed_cb(G_GNUC_UNUSED GObject * object,gpointer user_data)2601 video_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2602 {
2603 GstPlayer *self = GST_PLAYER (user_data);
2604
2605 g_mutex_lock (&self->lock);
2606 gst_player_streams_info_create (self, self->media_info,
2607 "n-video", GST_TYPE_PLAYER_VIDEO_INFO);
2608 g_mutex_unlock (&self->lock);
2609 }
2610
2611 static void
audio_changed_cb(G_GNUC_UNUSED GObject * object,gpointer user_data)2612 audio_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2613 {
2614 GstPlayer *self = GST_PLAYER (user_data);
2615
2616 g_mutex_lock (&self->lock);
2617 gst_player_streams_info_create (self, self->media_info,
2618 "n-audio", GST_TYPE_PLAYER_AUDIO_INFO);
2619 g_mutex_unlock (&self->lock);
2620 }
2621
2622 static void
subtitle_changed_cb(G_GNUC_UNUSED GObject * object,gpointer user_data)2623 subtitle_changed_cb (G_GNUC_UNUSED GObject * object, gpointer user_data)
2624 {
2625 GstPlayer *self = GST_PLAYER (user_data);
2626
2627 g_mutex_lock (&self->lock);
2628 gst_player_streams_info_create (self, self->media_info,
2629 "n-text", GST_TYPE_PLAYER_SUBTITLE_INFO);
2630 g_mutex_unlock (&self->lock);
2631 }
2632
2633 static void *
get_title(GstTagList * tags)2634 get_title (GstTagList * tags)
2635 {
2636 gchar *title = NULL;
2637
2638 gst_tag_list_get_string (tags, GST_TAG_TITLE, &title);
2639 if (!title)
2640 gst_tag_list_get_string (tags, GST_TAG_TITLE_SORTNAME, &title);
2641
2642 return title;
2643 }
2644
2645 static void *
get_container_format(GstTagList * tags)2646 get_container_format (GstTagList * tags)
2647 {
2648 gchar *container = NULL;
2649
2650 gst_tag_list_get_string (tags, GST_TAG_CONTAINER_FORMAT, &container);
2651
2652 /* TODO: If container is not available then maybe consider
2653 * parsing caps or file extension to guess the container format.
2654 */
2655
2656 return container;
2657 }
2658
2659 static void *
get_from_tags(GstPlayer * self,GstPlayerMediaInfo * media_info,void * (* func)(GstTagList *))2660 get_from_tags (GstPlayer * self, GstPlayerMediaInfo * media_info,
2661 void *(*func) (GstTagList *))
2662 {
2663 GList *l;
2664 void *ret = NULL;
2665
2666 if (media_info->tags) {
2667 ret = func (media_info->tags);
2668 if (ret)
2669 return ret;
2670 }
2671
2672 /* if global tag does not exit then try video and audio streams */
2673 GST_DEBUG_OBJECT (self, "trying video tags");
2674 for (l = gst_player_media_info_get_video_streams (media_info); l != NULL;
2675 l = l->next) {
2676 GstTagList *tags;
2677
2678 tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data);
2679 if (tags)
2680 ret = func (tags);
2681
2682 if (ret)
2683 return ret;
2684 }
2685
2686 GST_DEBUG_OBJECT (self, "trying audio tags");
2687 for (l = gst_player_media_info_get_audio_streams (media_info); l != NULL;
2688 l = l->next) {
2689 GstTagList *tags;
2690
2691 tags = gst_player_stream_info_get_tags ((GstPlayerStreamInfo *) l->data);
2692 if (tags)
2693 ret = func (tags);
2694
2695 if (ret)
2696 return ret;
2697 }
2698
2699 GST_DEBUG_OBJECT (self, "failed to get the information from tags");
2700 return NULL;
2701 }
2702
2703 static void *
get_cover_sample(GstTagList * tags)2704 get_cover_sample (GstTagList * tags)
2705 {
2706 GstSample *cover_sample = NULL;
2707
2708 gst_tag_list_get_sample (tags, GST_TAG_IMAGE, &cover_sample);
2709 if (!cover_sample)
2710 gst_tag_list_get_sample (tags, GST_TAG_PREVIEW_IMAGE, &cover_sample);
2711
2712 return cover_sample;
2713 }
2714
2715 static GstPlayerMediaInfo *
gst_player_media_info_create(GstPlayer * self)2716 gst_player_media_info_create (GstPlayer * self)
2717 {
2718 GstPlayerMediaInfo *media_info;
2719 GstQuery *query;
2720
2721 GST_DEBUG_OBJECT (self, "begin");
2722 media_info = gst_player_media_info_new (self->uri);
2723 media_info->duration = gst_player_get_duration (self);
2724 media_info->tags = self->global_tags;
2725 media_info->is_live = self->is_live;
2726 self->global_tags = NULL;
2727
2728 query = gst_query_new_seeking (GST_FORMAT_TIME);
2729 if (gst_element_query (self->playbin, query))
2730 gst_query_parse_seeking (query, NULL, &media_info->seekable, NULL, NULL);
2731 gst_query_unref (query);
2732
2733 if (self->use_playbin3 && self->collection) {
2734 gst_player_streams_info_create_from_collection (self, media_info,
2735 self->collection);
2736 } else {
2737 /* create audio/video/sub streams */
2738 gst_player_streams_info_create (self, media_info, "n-video",
2739 GST_TYPE_PLAYER_VIDEO_INFO);
2740 gst_player_streams_info_create (self, media_info, "n-audio",
2741 GST_TYPE_PLAYER_AUDIO_INFO);
2742 gst_player_streams_info_create (self, media_info, "n-text",
2743 GST_TYPE_PLAYER_SUBTITLE_INFO);
2744 }
2745
2746 media_info->title = get_from_tags (self, media_info, get_title);
2747 media_info->container =
2748 get_from_tags (self, media_info, get_container_format);
2749 media_info->image_sample = get_from_tags (self, media_info, get_cover_sample);
2750
2751 GST_DEBUG_OBJECT (self, "uri: %s title: %s duration: %" GST_TIME_FORMAT
2752 " seekable: %s live: %s container: %s image_sample %p",
2753 media_info->uri, media_info->title, GST_TIME_ARGS (media_info->duration),
2754 media_info->seekable ? "yes" : "no", media_info->is_live ? "yes" : "no",
2755 media_info->container, media_info->image_sample);
2756
2757 GST_DEBUG_OBJECT (self, "end");
2758 return media_info;
2759 }
2760
2761 static void
tags_changed_cb(GstPlayer * self,gint stream_index,GType type)2762 tags_changed_cb (GstPlayer * self, gint stream_index, GType type)
2763 {
2764 GstPlayerStreamInfo *s;
2765
2766 if (!self->media_info)
2767 return;
2768
2769 /* update the stream information */
2770 g_mutex_lock (&self->lock);
2771 s = gst_player_stream_info_find (self->media_info, type, stream_index);
2772 gst_player_stream_info_update_tags_and_caps (self, s);
2773 g_mutex_unlock (&self->lock);
2774
2775 emit_media_info_updated_signal (self);
2776 }
2777
2778 static void
video_tags_changed_cb(G_GNUC_UNUSED GstElement * playbin,gint stream_index,gpointer user_data)2779 video_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2780 gpointer user_data)
2781 {
2782 tags_changed_cb (GST_PLAYER (user_data), stream_index,
2783 GST_TYPE_PLAYER_VIDEO_INFO);
2784 }
2785
2786 static void
audio_tags_changed_cb(G_GNUC_UNUSED GstElement * playbin,gint stream_index,gpointer user_data)2787 audio_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2788 gpointer user_data)
2789 {
2790 tags_changed_cb (GST_PLAYER (user_data), stream_index,
2791 GST_TYPE_PLAYER_AUDIO_INFO);
2792 }
2793
2794 static void
subtitle_tags_changed_cb(G_GNUC_UNUSED GstElement * playbin,gint stream_index,gpointer user_data)2795 subtitle_tags_changed_cb (G_GNUC_UNUSED GstElement * playbin, gint stream_index,
2796 gpointer user_data)
2797 {
2798 tags_changed_cb (GST_PLAYER (user_data), stream_index,
2799 GST_TYPE_PLAYER_SUBTITLE_INFO);
2800 }
2801
2802 static void
volume_changed_dispatch(gpointer user_data)2803 volume_changed_dispatch (gpointer user_data)
2804 {
2805 GstPlayer *player = user_data;
2806
2807 if (player->inhibit_sigs)
2808 return;
2809
2810 g_signal_emit (player, signals[SIGNAL_VOLUME_CHANGED], 0);
2811 g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_VOLUME]);
2812 }
2813
2814 static void
volume_notify_cb(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GParamSpec * pspec,GstPlayer * self)2815 volume_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
2816 GstPlayer * self)
2817 {
2818 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
2819 signals[SIGNAL_VOLUME_CHANGED], 0, NULL, NULL, NULL) != 0) {
2820 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
2821 volume_changed_dispatch, g_object_ref (self),
2822 (GDestroyNotify) g_object_unref);
2823 }
2824 }
2825
2826 static void
mute_changed_dispatch(gpointer user_data)2827 mute_changed_dispatch (gpointer user_data)
2828 {
2829 GstPlayer *player = user_data;
2830
2831 if (player->inhibit_sigs)
2832 return;
2833
2834 g_signal_emit (player, signals[SIGNAL_MUTE_CHANGED], 0);
2835 g_object_notify_by_pspec (G_OBJECT (player), param_specs[PROP_MUTE]);
2836 }
2837
2838 static void
mute_notify_cb(G_GNUC_UNUSED GObject * obj,G_GNUC_UNUSED GParamSpec * pspec,GstPlayer * self)2839 mute_notify_cb (G_GNUC_UNUSED GObject * obj, G_GNUC_UNUSED GParamSpec * pspec,
2840 GstPlayer * self)
2841 {
2842 if (g_signal_handler_find (self, G_SIGNAL_MATCH_ID,
2843 signals[SIGNAL_MUTE_CHANGED], 0, NULL, NULL, NULL) != 0) {
2844 gst_player_signal_dispatcher_dispatch (self->signal_dispatcher, self,
2845 mute_changed_dispatch, g_object_ref (self),
2846 (GDestroyNotify) g_object_unref);
2847 }
2848 }
2849
2850 static void
source_setup_cb(GstElement * playbin,GstElement * source,GstPlayer * self)2851 source_setup_cb (GstElement * playbin, GstElement * source, GstPlayer * self)
2852 {
2853 gchar *user_agent;
2854
2855 user_agent = gst_player_config_get_user_agent (self->config);
2856 if (user_agent) {
2857 GParamSpec *prop;
2858
2859 prop = g_object_class_find_property (G_OBJECT_GET_CLASS (source),
2860 "user-agent");
2861 if (prop && prop->value_type == G_TYPE_STRING) {
2862 GST_INFO_OBJECT (self, "Setting source user-agent: %s", user_agent);
2863 g_object_set (source, "user-agent", user_agent, NULL);
2864 }
2865
2866 g_free (user_agent);
2867 }
2868 }
2869
2870 static gpointer
gst_player_main(gpointer data)2871 gst_player_main (gpointer data)
2872 {
2873 GstPlayer *self = GST_PLAYER (data);
2874 GstBus *bus;
2875 GSource *source;
2876 GSource *bus_source;
2877 GstElement *scaletempo;
2878 const gchar *env;
2879
2880 GST_TRACE_OBJECT (self, "Starting main thread");
2881
2882 g_main_context_push_thread_default (self->context);
2883
2884 source = g_idle_source_new ();
2885 g_source_set_callback (source, (GSourceFunc) main_loop_running_cb, self,
2886 NULL);
2887 g_source_attach (source, self->context);
2888 g_source_unref (source);
2889
2890 env = g_getenv ("GST_PLAYER_USE_PLAYBIN3");
2891 if (env && g_str_has_prefix (env, "1"))
2892 self->use_playbin3 = TRUE;
2893
2894 if (self->use_playbin3) {
2895 GST_DEBUG_OBJECT (self, "playbin3 enabled");
2896 self->playbin = gst_element_factory_make ("playbin3", "playbin3");
2897 } else {
2898 self->playbin = gst_element_factory_make ("playbin", "playbin");
2899 }
2900
2901 if (!self->playbin) {
2902 g_error ("GstPlayer: 'playbin' element not found, please check your setup");
2903 g_assert_not_reached ();
2904 }
2905
2906 if (self->video_renderer) {
2907 GstElement *video_sink =
2908 gst_player_video_renderer_create_video_sink (self->video_renderer,
2909 self);
2910
2911 if (video_sink)
2912 g_object_set (self->playbin, "video-sink", video_sink, NULL);
2913 }
2914
2915 scaletempo = gst_element_factory_make ("scaletempo", NULL);
2916 if (scaletempo) {
2917 g_object_set (self->playbin, "audio-filter", scaletempo, NULL);
2918 } else {
2919 g_warning ("GstPlayer: scaletempo element not available. Audio pitch "
2920 "will not be preserved during trick modes");
2921 }
2922
2923 self->bus = bus = gst_element_get_bus (self->playbin);
2924 bus_source = gst_bus_create_watch (bus);
2925 g_source_set_callback (bus_source, (GSourceFunc) gst_bus_async_signal_func,
2926 NULL, NULL);
2927 g_source_attach (bus_source, self->context);
2928
2929 g_signal_connect (G_OBJECT (bus), "message::error", G_CALLBACK (error_cb),
2930 self);
2931 g_signal_connect (G_OBJECT (bus), "message::warning", G_CALLBACK (warning_cb),
2932 self);
2933 g_signal_connect (G_OBJECT (bus), "message::eos", G_CALLBACK (eos_cb), self);
2934 g_signal_connect (G_OBJECT (bus), "message::state-changed",
2935 G_CALLBACK (state_changed_cb), self);
2936 g_signal_connect (G_OBJECT (bus), "message::buffering",
2937 G_CALLBACK (buffering_cb), self);
2938 g_signal_connect (G_OBJECT (bus), "message::clock-lost",
2939 G_CALLBACK (clock_lost_cb), self);
2940 g_signal_connect (G_OBJECT (bus), "message::duration-changed",
2941 G_CALLBACK (duration_changed_cb), self);
2942 g_signal_connect (G_OBJECT (bus), "message::latency",
2943 G_CALLBACK (latency_cb), self);
2944 g_signal_connect (G_OBJECT (bus), "message::request-state",
2945 G_CALLBACK (request_state_cb), self);
2946 g_signal_connect (G_OBJECT (bus), "message::element",
2947 G_CALLBACK (element_cb), self);
2948 g_signal_connect (G_OBJECT (bus), "message::tag", G_CALLBACK (tags_cb), self);
2949
2950 if (self->use_playbin3) {
2951 g_signal_connect (G_OBJECT (bus), "message::stream-collection",
2952 G_CALLBACK (stream_collection_cb), self);
2953 g_signal_connect (G_OBJECT (bus), "message::streams-selected",
2954 G_CALLBACK (streams_selected_cb), self);
2955 } else {
2956 g_signal_connect (self->playbin, "video-changed",
2957 G_CALLBACK (video_changed_cb), self);
2958 g_signal_connect (self->playbin, "audio-changed",
2959 G_CALLBACK (audio_changed_cb), self);
2960 g_signal_connect (self->playbin, "text-changed",
2961 G_CALLBACK (subtitle_changed_cb), self);
2962
2963 g_signal_connect (self->playbin, "video-tags-changed",
2964 G_CALLBACK (video_tags_changed_cb), self);
2965 g_signal_connect (self->playbin, "audio-tags-changed",
2966 G_CALLBACK (audio_tags_changed_cb), self);
2967 g_signal_connect (self->playbin, "text-tags-changed",
2968 G_CALLBACK (subtitle_tags_changed_cb), self);
2969 }
2970
2971 g_signal_connect (self->playbin, "notify::volume",
2972 G_CALLBACK (volume_notify_cb), self);
2973 g_signal_connect (self->playbin, "notify::mute",
2974 G_CALLBACK (mute_notify_cb), self);
2975 g_signal_connect (self->playbin, "source-setup",
2976 G_CALLBACK (source_setup_cb), self);
2977
2978 self->target_state = GST_STATE_NULL;
2979 self->current_state = GST_STATE_NULL;
2980 change_state (self, GST_PLAYER_STATE_STOPPED);
2981 self->buffering = 100;
2982 self->is_eos = FALSE;
2983 self->is_live = FALSE;
2984 self->rate = 1.0;
2985
2986 GST_TRACE_OBJECT (self, "Starting main loop");
2987 g_main_loop_run (self->loop);
2988 GST_TRACE_OBJECT (self, "Stopped main loop");
2989
2990 g_source_destroy (bus_source);
2991 g_source_unref (bus_source);
2992 gst_object_unref (bus);
2993
2994 remove_tick_source (self);
2995 remove_ready_timeout_source (self);
2996
2997 g_mutex_lock (&self->lock);
2998 if (self->media_info) {
2999 g_object_unref (self->media_info);
3000 self->media_info = NULL;
3001 }
3002
3003 remove_seek_source (self);
3004 g_mutex_unlock (&self->lock);
3005
3006 g_main_context_pop_thread_default (self->context);
3007
3008 self->target_state = GST_STATE_NULL;
3009 self->current_state = GST_STATE_NULL;
3010 if (self->playbin) {
3011 gst_element_set_state (self->playbin, GST_STATE_NULL);
3012 gst_object_unref (self->playbin);
3013 self->playbin = NULL;
3014 }
3015
3016 GST_TRACE_OBJECT (self, "Stopped main thread");
3017
3018 return NULL;
3019 }
3020
3021 static gpointer
gst_player_init_once(G_GNUC_UNUSED gpointer user_data)3022 gst_player_init_once (G_GNUC_UNUSED gpointer user_data)
3023 {
3024 gst_init (NULL, NULL);
3025
3026 GST_DEBUG_CATEGORY_INIT (gst_player_debug, "gst-player", 0, "GstPlayer");
3027 gst_player_error_quark ();
3028
3029 return NULL;
3030 }
3031
3032 /**
3033 * gst_player_new:
3034 * @video_renderer: (transfer full) (allow-none): GstPlayerVideoRenderer to use
3035 * @signal_dispatcher: (transfer full) (allow-none): GstPlayerSignalDispatcher to use
3036 *
3037 * Creates a new #GstPlayer instance that uses @signal_dispatcher to dispatch
3038 * signals to some event loop system, or emits signals directly if NULL is
3039 * passed. See gst_player_g_main_context_signal_dispatcher_new().
3040 *
3041 * Video is going to be rendered by @video_renderer, or if %NULL is provided
3042 * no special video set up will be done and some default handling will be
3043 * performed.
3044 *
3045 * Returns: (transfer full): a new #GstPlayer instance
3046 */
3047 GstPlayer *
gst_player_new(GstPlayerVideoRenderer * video_renderer,GstPlayerSignalDispatcher * signal_dispatcher)3048 gst_player_new (GstPlayerVideoRenderer * video_renderer,
3049 GstPlayerSignalDispatcher * signal_dispatcher)
3050 {
3051 static GOnce once = G_ONCE_INIT;
3052 GstPlayer *self;
3053
3054 g_once (&once, gst_player_init_once, NULL);
3055
3056 self =
3057 g_object_new (GST_TYPE_PLAYER, "video-renderer", video_renderer,
3058 "signal-dispatcher", signal_dispatcher, NULL);
3059 gst_object_ref_sink (self);
3060
3061 if (video_renderer)
3062 g_object_unref (video_renderer);
3063 if (signal_dispatcher)
3064 g_object_unref (signal_dispatcher);
3065
3066 return self;
3067 }
3068
3069 static gboolean
gst_player_play_internal(gpointer user_data)3070 gst_player_play_internal (gpointer user_data)
3071 {
3072 GstPlayer *self = GST_PLAYER (user_data);
3073 GstStateChangeReturn state_ret;
3074
3075 GST_DEBUG_OBJECT (self, "Play");
3076
3077 g_mutex_lock (&self->lock);
3078 if (!self->uri) {
3079 g_mutex_unlock (&self->lock);
3080 return G_SOURCE_REMOVE;
3081 }
3082 g_mutex_unlock (&self->lock);
3083
3084 remove_ready_timeout_source (self);
3085 self->target_state = GST_STATE_PLAYING;
3086
3087 if (self->current_state < GST_STATE_PAUSED)
3088 change_state (self, GST_PLAYER_STATE_BUFFERING);
3089
3090 if (self->current_state >= GST_STATE_PAUSED && !self->is_eos
3091 && self->buffering >= 100 && !(self->seek_position != GST_CLOCK_TIME_NONE
3092 || self->seek_pending)) {
3093 state_ret = gst_element_set_state (self->playbin, GST_STATE_PLAYING);
3094 } else {
3095 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
3096 }
3097
3098 if (state_ret == GST_STATE_CHANGE_FAILURE) {
3099 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3100 "Failed to play"));
3101 return G_SOURCE_REMOVE;
3102 } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) {
3103 self->is_live = TRUE;
3104 GST_DEBUG_OBJECT (self, "Pipeline is live");
3105 }
3106
3107 if (self->is_eos) {
3108 gboolean ret;
3109
3110 GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning");
3111 self->is_eos = FALSE;
3112 ret =
3113 gst_element_seek_simple (self->playbin, GST_FORMAT_TIME,
3114 GST_SEEK_FLAG_FLUSH, 0);
3115 if (!ret) {
3116 GST_ERROR_OBJECT (self, "Seek to beginning failed");
3117 gst_player_stop_internal (self, TRUE);
3118 gst_player_play_internal (self);
3119 }
3120 }
3121
3122 return G_SOURCE_REMOVE;
3123 }
3124
3125 /**
3126 * gst_player_play:
3127 * @player: #GstPlayer instance
3128 *
3129 * Request to play the loaded stream.
3130 */
3131 void
gst_player_play(GstPlayer * self)3132 gst_player_play (GstPlayer * self)
3133 {
3134 g_return_if_fail (GST_IS_PLAYER (self));
3135
3136 g_mutex_lock (&self->lock);
3137 self->inhibit_sigs = FALSE;
3138 g_mutex_unlock (&self->lock);
3139
3140 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3141 gst_player_play_internal, self, NULL);
3142 }
3143
3144 static gboolean
gst_player_pause_internal(gpointer user_data)3145 gst_player_pause_internal (gpointer user_data)
3146 {
3147 GstPlayer *self = GST_PLAYER (user_data);
3148 GstStateChangeReturn state_ret;
3149
3150 GST_DEBUG_OBJECT (self, "Pause");
3151
3152 g_mutex_lock (&self->lock);
3153 if (!self->uri) {
3154 g_mutex_unlock (&self->lock);
3155 return G_SOURCE_REMOVE;
3156 }
3157 g_mutex_unlock (&self->lock);
3158
3159 tick_cb (self);
3160 remove_tick_source (self);
3161 remove_ready_timeout_source (self);
3162
3163 self->target_state = GST_STATE_PAUSED;
3164
3165 if (self->current_state < GST_STATE_PAUSED)
3166 change_state (self, GST_PLAYER_STATE_BUFFERING);
3167
3168 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
3169 if (state_ret == GST_STATE_CHANGE_FAILURE) {
3170 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3171 "Failed to pause"));
3172 return G_SOURCE_REMOVE;
3173 } else if (state_ret == GST_STATE_CHANGE_NO_PREROLL) {
3174 self->is_live = TRUE;
3175 GST_DEBUG_OBJECT (self, "Pipeline is live");
3176 }
3177
3178 if (self->is_eos) {
3179 gboolean ret;
3180
3181 GST_DEBUG_OBJECT (self, "Was EOS, seeking to beginning");
3182 self->is_eos = FALSE;
3183 ret =
3184 gst_element_seek_simple (self->playbin, GST_FORMAT_TIME,
3185 GST_SEEK_FLAG_FLUSH, 0);
3186 if (!ret) {
3187 GST_ERROR_OBJECT (self, "Seek to beginning failed");
3188 gst_player_stop_internal (self, TRUE);
3189 gst_player_pause_internal (self);
3190 }
3191 }
3192
3193 return G_SOURCE_REMOVE;
3194 }
3195
3196 /**
3197 * gst_player_pause:
3198 * @player: #GstPlayer instance
3199 *
3200 * Pauses the current stream.
3201 */
3202 void
gst_player_pause(GstPlayer * self)3203 gst_player_pause (GstPlayer * self)
3204 {
3205 g_return_if_fail (GST_IS_PLAYER (self));
3206
3207 g_mutex_lock (&self->lock);
3208 self->inhibit_sigs = FALSE;
3209 g_mutex_unlock (&self->lock);
3210
3211 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3212 gst_player_pause_internal, self, NULL);
3213 }
3214
3215 static void
gst_player_stop_internal(GstPlayer * self,gboolean transient)3216 gst_player_stop_internal (GstPlayer * self, gboolean transient)
3217 {
3218 /* directly return if we're already stopped */
3219 if (self->current_state <= GST_STATE_READY &&
3220 self->target_state <= GST_STATE_READY)
3221 return;
3222
3223 GST_DEBUG_OBJECT (self, "Stop (transient %d)", transient);
3224
3225 tick_cb (self);
3226 remove_tick_source (self);
3227
3228 add_ready_timeout_source (self);
3229
3230 self->target_state = GST_STATE_NULL;
3231 self->current_state = GST_STATE_READY;
3232 self->is_live = FALSE;
3233 self->is_eos = FALSE;
3234 gst_bus_set_flushing (self->bus, TRUE);
3235 gst_element_set_state (self->playbin, GST_STATE_READY);
3236 gst_bus_set_flushing (self->bus, FALSE);
3237 change_state (self, transient
3238 && self->app_state !=
3239 GST_PLAYER_STATE_STOPPED ? GST_PLAYER_STATE_BUFFERING :
3240 GST_PLAYER_STATE_STOPPED);
3241 self->buffering = 100;
3242 self->cached_duration = GST_CLOCK_TIME_NONE;
3243 g_mutex_lock (&self->lock);
3244 if (self->media_info) {
3245 g_object_unref (self->media_info);
3246 self->media_info = NULL;
3247 }
3248 if (self->global_tags) {
3249 gst_tag_list_unref (self->global_tags);
3250 self->global_tags = NULL;
3251 }
3252 self->seek_pending = FALSE;
3253 remove_seek_source (self);
3254 self->seek_position = GST_CLOCK_TIME_NONE;
3255 self->last_seek_time = GST_CLOCK_TIME_NONE;
3256 self->rate = 1.0;
3257 if (self->collection) {
3258 if (self->stream_notify_id)
3259 g_signal_handler_disconnect (self->collection, self->stream_notify_id);
3260 self->stream_notify_id = 0;
3261 gst_object_unref (self->collection);
3262 self->collection = NULL;
3263 }
3264 g_free (self->video_sid);
3265 g_free (self->audio_sid);
3266 g_free (self->subtitle_sid);
3267 self->video_sid = NULL;
3268 self->audio_sid = NULL;
3269 self->subtitle_sid = NULL;
3270 g_mutex_unlock (&self->lock);
3271 }
3272
3273 static gboolean
gst_player_stop_internal_dispatch(gpointer user_data)3274 gst_player_stop_internal_dispatch (gpointer user_data)
3275 {
3276 GstPlayer *self = GST_PLAYER (user_data);
3277
3278 gst_player_stop_internal (self, FALSE);
3279
3280 return G_SOURCE_REMOVE;
3281 }
3282
3283
3284 /**
3285 * gst_player_stop:
3286 * @player: #GstPlayer instance
3287 *
3288 * Stops playing the current stream and resets to the first position
3289 * in the stream.
3290 */
3291 void
gst_player_stop(GstPlayer * self)3292 gst_player_stop (GstPlayer * self)
3293 {
3294 g_return_if_fail (GST_IS_PLAYER (self));
3295
3296 g_mutex_lock (&self->lock);
3297 self->inhibit_sigs = TRUE;
3298 g_mutex_unlock (&self->lock);
3299
3300 g_main_context_invoke_full (self->context, G_PRIORITY_DEFAULT,
3301 gst_player_stop_internal_dispatch, self, NULL);
3302 }
3303
3304 /* Must be called with lock from main context, releases lock! */
3305 static void
gst_player_seek_internal_locked(GstPlayer * self)3306 gst_player_seek_internal_locked (GstPlayer * self)
3307 {
3308 gboolean ret;
3309 GstClockTime position;
3310 gdouble rate;
3311 GstStateChangeReturn state_ret;
3312 GstEvent *s_event;
3313 GstSeekFlags flags = 0;
3314 gboolean accurate = FALSE;
3315
3316 remove_seek_source (self);
3317
3318 /* Only seek in PAUSED */
3319 if (self->current_state < GST_STATE_PAUSED) {
3320 return;
3321 } else if (self->current_state != GST_STATE_PAUSED) {
3322 g_mutex_unlock (&self->lock);
3323 state_ret = gst_element_set_state (self->playbin, GST_STATE_PAUSED);
3324 if (state_ret == GST_STATE_CHANGE_FAILURE) {
3325 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3326 "Failed to seek"));
3327 g_mutex_lock (&self->lock);
3328 return;
3329 }
3330 g_mutex_lock (&self->lock);
3331 return;
3332 }
3333
3334 self->last_seek_time = gst_util_get_timestamp ();
3335 position = self->seek_position;
3336 self->seek_position = GST_CLOCK_TIME_NONE;
3337 self->seek_pending = TRUE;
3338 rate = self->rate;
3339 g_mutex_unlock (&self->lock);
3340
3341 remove_tick_source (self);
3342 self->is_eos = FALSE;
3343
3344 flags |= GST_SEEK_FLAG_FLUSH;
3345
3346 accurate = gst_player_config_get_seek_accurate (self->config);
3347
3348 if (accurate) {
3349 flags |= GST_SEEK_FLAG_ACCURATE;
3350 } else {
3351 flags &= ~GST_SEEK_FLAG_ACCURATE;
3352 }
3353
3354 if (rate != 1.0) {
3355 flags |= GST_SEEK_FLAG_TRICKMODE;
3356 }
3357
3358 if (rate >= 0.0) {
3359 s_event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
3360 GST_SEEK_TYPE_SET, position, GST_SEEK_TYPE_SET, GST_CLOCK_TIME_NONE);
3361 } else {
3362 s_event = gst_event_new_seek (rate, GST_FORMAT_TIME, flags,
3363 GST_SEEK_TYPE_SET, G_GINT64_CONSTANT (0), GST_SEEK_TYPE_SET, position);
3364 }
3365
3366 GST_DEBUG_OBJECT (self, "Seek with rate %.2lf to %" GST_TIME_FORMAT,
3367 rate, GST_TIME_ARGS (position));
3368
3369 ret = gst_element_send_event (self->playbin, s_event);
3370 if (!ret)
3371 emit_error (self, g_error_new (GST_PLAYER_ERROR, GST_PLAYER_ERROR_FAILED,
3372 "Failed to seek to %" GST_TIME_FORMAT, GST_TIME_ARGS (position)));
3373
3374 g_mutex_lock (&self->lock);
3375 }
3376
3377 static gboolean
gst_player_seek_internal(gpointer user_data)3378 gst_player_seek_internal (gpointer user_data)
3379 {
3380 GstPlayer *self = GST_PLAYER (user_data);
3381
3382 g_mutex_lock (&self->lock);
3383 gst_player_seek_internal_locked (self);
3384 g_mutex_unlock (&self->lock);
3385
3386 return G_SOURCE_REMOVE;
3387 }
3388
3389 /**
3390 * gst_player_set_rate:
3391 * @player: #GstPlayer instance
3392 * @rate: playback rate
3393 *
3394 * Playback at specified rate
3395 */
3396 void
gst_player_set_rate(GstPlayer * self,gdouble rate)3397 gst_player_set_rate (GstPlayer * self, gdouble rate)
3398 {
3399 g_return_if_fail (GST_IS_PLAYER (self));
3400 g_return_if_fail (rate != 0.0);
3401
3402 g_object_set (self, "rate", rate, NULL);
3403 }
3404
3405 /**
3406 * gst_player_get_rate:
3407 * @player: #GstPlayer instance
3408 *
3409 * Returns: current playback rate
3410 */
3411 gdouble
gst_player_get_rate(GstPlayer * self)3412 gst_player_get_rate (GstPlayer * self)
3413 {
3414 gdouble val;
3415
3416 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_RATE);
3417
3418 g_object_get (self, "rate", &val, NULL);
3419
3420 return val;
3421 }
3422
3423 /**
3424 * gst_player_seek:
3425 * @player: #GstPlayer instance
3426 * @position: position to seek in nanoseconds
3427 *
3428 * Seeks the currently-playing stream to the absolute @position time
3429 * in nanoseconds.
3430 */
3431 void
gst_player_seek(GstPlayer * self,GstClockTime position)3432 gst_player_seek (GstPlayer * self, GstClockTime position)
3433 {
3434 g_return_if_fail (GST_IS_PLAYER (self));
3435 g_return_if_fail (GST_CLOCK_TIME_IS_VALID (position));
3436
3437 g_mutex_lock (&self->lock);
3438 if (self->media_info && !self->media_info->seekable) {
3439 GST_DEBUG_OBJECT (self, "Media is not seekable");
3440 g_mutex_unlock (&self->lock);
3441 return;
3442 }
3443
3444 self->seek_position = position;
3445
3446 /* If there is no seek being dispatch to the main context currently do that,
3447 * otherwise we just updated the seek position so that it will be taken by
3448 * the seek handler from the main context instead of the old one.
3449 */
3450 if (!self->seek_source) {
3451 GstClockTime now = gst_util_get_timestamp ();
3452
3453 /* If no seek is pending or it was started more than 250 mseconds ago seek
3454 * immediately, otherwise wait until the 250 mseconds have passed */
3455 if (!self->seek_pending || (now - self->last_seek_time > 250 * GST_MSECOND)) {
3456 self->seek_source = g_idle_source_new ();
3457 g_source_set_callback (self->seek_source,
3458 (GSourceFunc) gst_player_seek_internal, self, NULL);
3459 GST_TRACE_OBJECT (self, "Dispatching seek to position %" GST_TIME_FORMAT,
3460 GST_TIME_ARGS (position));
3461 g_source_attach (self->seek_source, self->context);
3462 } else {
3463 guint delay = 250000 - (now - self->last_seek_time) / 1000;
3464
3465 /* Note that last_seek_time must be set to something at this point and
3466 * it must be smaller than 250 mseconds */
3467 self->seek_source = g_timeout_source_new (delay);
3468 g_source_set_callback (self->seek_source,
3469 (GSourceFunc) gst_player_seek_internal, self, NULL);
3470
3471 GST_TRACE_OBJECT (self,
3472 "Delaying seek to position %" GST_TIME_FORMAT " by %u us",
3473 GST_TIME_ARGS (position), delay);
3474 g_source_attach (self->seek_source, self->context);
3475 }
3476 }
3477 g_mutex_unlock (&self->lock);
3478 }
3479
3480 static void
remove_seek_source(GstPlayer * self)3481 remove_seek_source (GstPlayer * self)
3482 {
3483 if (!self->seek_source)
3484 return;
3485
3486 g_source_destroy (self->seek_source);
3487 g_source_unref (self->seek_source);
3488 self->seek_source = NULL;
3489 }
3490
3491 /**
3492 * gst_player_get_uri:
3493 * @player: #GstPlayer instance
3494 *
3495 * Gets the URI of the currently-playing stream.
3496 *
3497 * Returns: (transfer full): a string containing the URI of the
3498 * currently-playing stream. g_free() after usage.
3499 */
3500 gchar *
gst_player_get_uri(GstPlayer * self)3501 gst_player_get_uri (GstPlayer * self)
3502 {
3503 gchar *val;
3504
3505 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_URI);
3506
3507 g_object_get (self, "uri", &val, NULL);
3508
3509 return val;
3510 }
3511
3512 /**
3513 * gst_player_set_uri:
3514 * @player: #GstPlayer instance
3515 * @uri: next URI to play.
3516 *
3517 * Sets the next URI to play.
3518 */
3519 void
gst_player_set_uri(GstPlayer * self,const gchar * val)3520 gst_player_set_uri (GstPlayer * self, const gchar * val)
3521 {
3522 g_return_if_fail (GST_IS_PLAYER (self));
3523
3524 g_object_set (self, "uri", val, NULL);
3525 }
3526
3527 /**
3528 * gst_player_set_subtitle_uri:
3529 * @player: #GstPlayer instance
3530 * @uri: subtitle URI
3531 *
3532 * Sets the external subtitle URI. This should be combined with a call to
3533 * gst_player_set_subtitle_track_enabled(@player, TRUE) so the subtitles are actually
3534 * rendered.
3535 */
3536 void
gst_player_set_subtitle_uri(GstPlayer * self,const gchar * suburi)3537 gst_player_set_subtitle_uri (GstPlayer * self, const gchar * suburi)
3538 {
3539 g_return_if_fail (GST_IS_PLAYER (self));
3540
3541 g_object_set (self, "suburi", suburi, NULL);
3542 }
3543
3544 /**
3545 * gst_player_get_subtitle_uri:
3546 * @player: #GstPlayer instance
3547 *
3548 * current subtitle URI
3549 *
3550 * Returns: (transfer full): URI of the current external subtitle.
3551 * g_free() after usage.
3552 */
3553 gchar *
gst_player_get_subtitle_uri(GstPlayer * self)3554 gst_player_get_subtitle_uri (GstPlayer * self)
3555 {
3556 gchar *val = NULL;
3557
3558 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3559
3560 g_object_get (self, "suburi", &val, NULL);
3561
3562 return val;
3563 }
3564
3565 /**
3566 * gst_player_get_position:
3567 * @player: #GstPlayer instance
3568 *
3569 * Returns: the absolute position time, in nanoseconds, of the
3570 * currently-playing stream.
3571 */
3572 GstClockTime
gst_player_get_position(GstPlayer * self)3573 gst_player_get_position (GstPlayer * self)
3574 {
3575 GstClockTime val;
3576
3577 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_POSITION);
3578
3579 g_object_get (self, "position", &val, NULL);
3580
3581 return val;
3582 }
3583
3584 /**
3585 * gst_player_get_duration:
3586 * @player: #GstPlayer instance
3587 *
3588 * Retrieves the duration of the media stream that self represents.
3589 *
3590 * Returns: the duration of the currently-playing media stream, in
3591 * nanoseconds.
3592 */
3593 GstClockTime
gst_player_get_duration(GstPlayer * self)3594 gst_player_get_duration (GstPlayer * self)
3595 {
3596 GstClockTime val;
3597
3598 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_DURATION);
3599
3600 g_object_get (self, "duration", &val, NULL);
3601
3602 return val;
3603 }
3604
3605 /**
3606 * gst_player_get_volume:
3607 * @player: #GstPlayer instance
3608 *
3609 * Returns the current volume level, as a percentage between 0 and 1.
3610 *
3611 * Returns: the volume as percentage between 0 and 1.
3612 */
3613 gdouble
gst_player_get_volume(GstPlayer * self)3614 gst_player_get_volume (GstPlayer * self)
3615 {
3616 gdouble val;
3617
3618 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_VOLUME);
3619
3620 g_object_get (self, "volume", &val, NULL);
3621
3622 return val;
3623 }
3624
3625 /**
3626 * gst_player_set_volume:
3627 * @player: #GstPlayer instance
3628 * @val: the new volume level, as a percentage between 0 and 1
3629 *
3630 * Sets the volume level of the stream as a percentage between 0 and 1.
3631 */
3632 void
gst_player_set_volume(GstPlayer * self,gdouble val)3633 gst_player_set_volume (GstPlayer * self, gdouble val)
3634 {
3635 g_return_if_fail (GST_IS_PLAYER (self));
3636
3637 g_object_set (self, "volume", val, NULL);
3638 }
3639
3640 /**
3641 * gst_player_get_mute:
3642 * @player: #GstPlayer instance
3643 *
3644 * Returns: %TRUE if the currently-playing stream is muted.
3645 */
3646 gboolean
gst_player_get_mute(GstPlayer * self)3647 gst_player_get_mute (GstPlayer * self)
3648 {
3649 gboolean val;
3650
3651 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_MUTE);
3652
3653 g_object_get (self, "mute", &val, NULL);
3654
3655 return val;
3656 }
3657
3658 /**
3659 * gst_player_set_mute:
3660 * @player: #GstPlayer instance
3661 * @val: Mute state the should be set
3662 *
3663 * %TRUE if the currently-playing stream should be muted.
3664 */
3665 void
gst_player_set_mute(GstPlayer * self,gboolean val)3666 gst_player_set_mute (GstPlayer * self, gboolean val)
3667 {
3668 g_return_if_fail (GST_IS_PLAYER (self));
3669
3670 g_object_set (self, "mute", val, NULL);
3671 }
3672
3673 /**
3674 * gst_player_get_pipeline:
3675 * @player: #GstPlayer instance
3676 *
3677 * Returns: (transfer full): The internal playbin instance
3678 */
3679 GstElement *
gst_player_get_pipeline(GstPlayer * self)3680 gst_player_get_pipeline (GstPlayer * self)
3681 {
3682 GstElement *val;
3683
3684 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3685
3686 g_object_get (self, "pipeline", &val, NULL);
3687
3688 return val;
3689 }
3690
3691 /**
3692 * gst_player_get_media_info:
3693 * @player: #GstPlayer instance
3694 *
3695 * A Function to get the current media info #GstPlayerMediaInfo instance.
3696 *
3697 * Returns: (transfer full): media info instance.
3698 *
3699 * The caller should free it with g_object_unref()
3700 */
3701 GstPlayerMediaInfo *
gst_player_get_media_info(GstPlayer * self)3702 gst_player_get_media_info (GstPlayer * self)
3703 {
3704 GstPlayerMediaInfo *info;
3705
3706 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3707
3708 if (!self->media_info)
3709 return NULL;
3710
3711 g_mutex_lock (&self->lock);
3712 info = gst_player_media_info_copy (self->media_info);
3713 g_mutex_unlock (&self->lock);
3714
3715 return info;
3716 }
3717
3718 /**
3719 * gst_player_get_current_audio_track:
3720 * @player: #GstPlayer instance
3721 *
3722 * A Function to get current audio #GstPlayerAudioInfo instance.
3723 *
3724 * Returns: (transfer full): current audio track.
3725 *
3726 * The caller should free it with g_object_unref()
3727 */
3728 GstPlayerAudioInfo *
gst_player_get_current_audio_track(GstPlayer * self)3729 gst_player_get_current_audio_track (GstPlayer * self)
3730 {
3731 GstPlayerAudioInfo *info;
3732
3733 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3734
3735 if (!is_track_enabled (self, GST_PLAY_FLAG_AUDIO))
3736 return NULL;
3737
3738 if (self->use_playbin3) {
3739 info = (GstPlayerAudioInfo *)
3740 gst_player_stream_info_get_current_from_stream_id (self,
3741 self->audio_sid, GST_TYPE_PLAYER_AUDIO_INFO);
3742 } else {
3743 info = (GstPlayerAudioInfo *) gst_player_stream_info_get_current (self,
3744 "current-audio", GST_TYPE_PLAYER_AUDIO_INFO);
3745 }
3746
3747 return info;
3748 }
3749
3750 /**
3751 * gst_player_get_current_video_track:
3752 * @player: #GstPlayer instance
3753 *
3754 * A Function to get current video #GstPlayerVideoInfo instance.
3755 *
3756 * Returns: (transfer full): current video track.
3757 *
3758 * The caller should free it with g_object_unref()
3759 */
3760 GstPlayerVideoInfo *
gst_player_get_current_video_track(GstPlayer * self)3761 gst_player_get_current_video_track (GstPlayer * self)
3762 {
3763 GstPlayerVideoInfo *info;
3764
3765 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3766
3767 if (!is_track_enabled (self, GST_PLAY_FLAG_VIDEO))
3768 return NULL;
3769
3770 if (self->use_playbin3) {
3771 info = (GstPlayerVideoInfo *)
3772 gst_player_stream_info_get_current_from_stream_id (self,
3773 self->video_sid, GST_TYPE_PLAYER_VIDEO_INFO);
3774 } else {
3775 info = (GstPlayerVideoInfo *) gst_player_stream_info_get_current (self,
3776 "current-video", GST_TYPE_PLAYER_VIDEO_INFO);
3777 }
3778
3779 return info;
3780 }
3781
3782 /**
3783 * gst_player_get_current_subtitle_track:
3784 * @player: #GstPlayer instance
3785 *
3786 * A Function to get current subtitle #GstPlayerSubtitleInfo instance.
3787 *
3788 * Returns: (transfer none): current subtitle track.
3789 *
3790 * The caller should free it with g_object_unref()
3791 */
3792 GstPlayerSubtitleInfo *
gst_player_get_current_subtitle_track(GstPlayer * self)3793 gst_player_get_current_subtitle_track (GstPlayer * self)
3794 {
3795 GstPlayerSubtitleInfo *info;
3796
3797 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
3798
3799 if (!is_track_enabled (self, GST_PLAY_FLAG_SUBTITLE))
3800 return NULL;
3801
3802 if (self->use_playbin3) {
3803 info = (GstPlayerSubtitleInfo *)
3804 gst_player_stream_info_get_current_from_stream_id (self,
3805 self->subtitle_sid, GST_TYPE_PLAYER_SUBTITLE_INFO);
3806 } else {
3807 info = (GstPlayerSubtitleInfo *) gst_player_stream_info_get_current (self,
3808 "current-text", GST_TYPE_PLAYER_SUBTITLE_INFO);
3809 }
3810
3811 return info;
3812 }
3813
3814 /* Must be called with lock */
3815 static gboolean
gst_player_select_streams(GstPlayer * self)3816 gst_player_select_streams (GstPlayer * self)
3817 {
3818 GList *stream_list = NULL;
3819 gboolean ret = FALSE;
3820
3821 if (self->audio_sid)
3822 stream_list = g_list_append (stream_list, g_strdup (self->audio_sid));
3823 if (self->video_sid)
3824 stream_list = g_list_append (stream_list, g_strdup (self->video_sid));
3825 if (self->subtitle_sid)
3826 stream_list = g_list_append (stream_list, g_strdup (self->subtitle_sid));
3827
3828 g_mutex_unlock (&self->lock);
3829 if (stream_list) {
3830 ret = gst_element_send_event (self->playbin,
3831 gst_event_new_select_streams (stream_list));
3832 g_list_free_full (stream_list, g_free);
3833 } else {
3834 GST_ERROR_OBJECT (self, "No available streams for select-streams");
3835 }
3836 g_mutex_lock (&self->lock);
3837
3838 return ret;
3839 }
3840
3841 /**
3842 * gst_player_set_audio_track:
3843 * @player: #GstPlayer instance
3844 * @stream_index: stream index
3845 *
3846 * Returns: %TRUE or %FALSE
3847 *
3848 * Sets the audio track @stream_idex.
3849 */
3850 gboolean
gst_player_set_audio_track(GstPlayer * self,gint stream_index)3851 gst_player_set_audio_track (GstPlayer * self, gint stream_index)
3852 {
3853 GstPlayerStreamInfo *info;
3854 gboolean ret = TRUE;
3855
3856 g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3857
3858 g_mutex_lock (&self->lock);
3859 info = gst_player_stream_info_find (self->media_info,
3860 GST_TYPE_PLAYER_AUDIO_INFO, stream_index);
3861 g_mutex_unlock (&self->lock);
3862 if (!info) {
3863 GST_ERROR_OBJECT (self, "invalid audio stream index %d", stream_index);
3864 return FALSE;
3865 }
3866
3867 if (self->use_playbin3) {
3868 g_mutex_lock (&self->lock);
3869 g_free (self->audio_sid);
3870 self->audio_sid = g_strdup (info->stream_id);
3871 ret = gst_player_select_streams (self);
3872 g_mutex_unlock (&self->lock);
3873 } else {
3874 g_object_set (G_OBJECT (self->playbin), "current-audio", stream_index,
3875 NULL);
3876 }
3877
3878 GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3879 return ret;
3880 }
3881
3882 /**
3883 * gst_player_set_video_track:
3884 * @player: #GstPlayer instance
3885 * @stream_index: stream index
3886 *
3887 * Returns: %TRUE or %FALSE
3888 *
3889 * Sets the video track @stream_index.
3890 */
3891 gboolean
gst_player_set_video_track(GstPlayer * self,gint stream_index)3892 gst_player_set_video_track (GstPlayer * self, gint stream_index)
3893 {
3894 GstPlayerStreamInfo *info;
3895 gboolean ret = TRUE;
3896
3897 g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3898
3899 /* check if stream_index exist in our internal media_info list */
3900 g_mutex_lock (&self->lock);
3901 info = gst_player_stream_info_find (self->media_info,
3902 GST_TYPE_PLAYER_VIDEO_INFO, stream_index);
3903 g_mutex_unlock (&self->lock);
3904 if (!info) {
3905 GST_ERROR_OBJECT (self, "invalid video stream index %d", stream_index);
3906 return FALSE;
3907 }
3908
3909 if (self->use_playbin3) {
3910 g_mutex_lock (&self->lock);
3911 g_free (self->video_sid);
3912 self->video_sid = g_strdup (info->stream_id);
3913 ret = gst_player_select_streams (self);
3914 g_mutex_unlock (&self->lock);
3915 } else {
3916 g_object_set (G_OBJECT (self->playbin), "current-video", stream_index,
3917 NULL);
3918 }
3919
3920 GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3921 return ret;
3922 }
3923
3924 /**
3925 * gst_player_set_subtitle_track:
3926 * @player: #GstPlayer instance
3927 * @stream_index: stream index
3928 *
3929 * Returns: %TRUE or %FALSE
3930 *
3931 * Sets the subtitle strack @stream_index.
3932 */
3933 gboolean
gst_player_set_subtitle_track(GstPlayer * self,gint stream_index)3934 gst_player_set_subtitle_track (GstPlayer * self, gint stream_index)
3935 {
3936 GstPlayerStreamInfo *info;
3937 gboolean ret = TRUE;
3938
3939 g_return_val_if_fail (GST_IS_PLAYER (self), 0);
3940
3941 g_mutex_lock (&self->lock);
3942 info = gst_player_stream_info_find (self->media_info,
3943 GST_TYPE_PLAYER_SUBTITLE_INFO, stream_index);
3944 g_mutex_unlock (&self->lock);
3945 if (!info) {
3946 GST_ERROR_OBJECT (self, "invalid subtitle stream index %d", stream_index);
3947 return FALSE;
3948 }
3949
3950 if (self->use_playbin3) {
3951 g_mutex_lock (&self->lock);
3952 g_free (self->subtitle_sid);
3953 self->subtitle_sid = g_strdup (info->stream_id);
3954 ret = gst_player_select_streams (self);
3955 g_mutex_unlock (&self->lock);
3956 } else {
3957 g_object_set (G_OBJECT (self->playbin), "current-text", stream_index, NULL);
3958 }
3959
3960 GST_DEBUG_OBJECT (self, "set stream index '%d'", stream_index);
3961 return ret;
3962 }
3963
3964 /**
3965 * gst_player_set_audio_track_enabled:
3966 * @player: #GstPlayer instance
3967 * @enabled: TRUE or FALSE
3968 *
3969 * Enable or disable the current audio track.
3970 */
3971 void
gst_player_set_audio_track_enabled(GstPlayer * self,gboolean enabled)3972 gst_player_set_audio_track_enabled (GstPlayer * self, gboolean enabled)
3973 {
3974 g_return_if_fail (GST_IS_PLAYER (self));
3975
3976 if (enabled)
3977 player_set_flag (self, GST_PLAY_FLAG_AUDIO);
3978 else
3979 player_clear_flag (self, GST_PLAY_FLAG_AUDIO);
3980
3981 GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
3982 }
3983
3984 /**
3985 * gst_player_set_video_track_enabled:
3986 * @player: #GstPlayer instance
3987 * @enabled: TRUE or FALSE
3988 *
3989 * Enable or disable the current video track.
3990 */
3991 void
gst_player_set_video_track_enabled(GstPlayer * self,gboolean enabled)3992 gst_player_set_video_track_enabled (GstPlayer * self, gboolean enabled)
3993 {
3994 g_return_if_fail (GST_IS_PLAYER (self));
3995
3996 if (enabled)
3997 player_set_flag (self, GST_PLAY_FLAG_VIDEO);
3998 else
3999 player_clear_flag (self, GST_PLAY_FLAG_VIDEO);
4000
4001 GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
4002 }
4003
4004 /**
4005 * gst_player_set_subtitle_track_enabled:
4006 * @player: #GstPlayer instance
4007 * @enabled: TRUE or FALSE
4008 *
4009 * Enable or disable the current subtitle track.
4010 */
4011 void
gst_player_set_subtitle_track_enabled(GstPlayer * self,gboolean enabled)4012 gst_player_set_subtitle_track_enabled (GstPlayer * self, gboolean enabled)
4013 {
4014 g_return_if_fail (GST_IS_PLAYER (self));
4015
4016 if (enabled)
4017 player_set_flag (self, GST_PLAY_FLAG_SUBTITLE);
4018 else
4019 player_clear_flag (self, GST_PLAY_FLAG_SUBTITLE);
4020
4021 GST_DEBUG_OBJECT (self, "track is '%s'", enabled ? "Enabled" : "Disabled");
4022 }
4023
4024 /**
4025 * gst_player_set_visualization:
4026 * @player: #GstPlayer instance
4027 * @name: visualization element obtained from
4028 * #gst_player_visualizations_get()
4029 *
4030 * Returns: %TRUE if the visualizations was set correctly. Otherwise,
4031 * %FALSE.
4032 */
4033 gboolean
gst_player_set_visualization(GstPlayer * self,const gchar * name)4034 gst_player_set_visualization (GstPlayer * self, const gchar * name)
4035 {
4036 g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4037
4038 g_mutex_lock (&self->lock);
4039 if (self->current_vis_element) {
4040 gst_object_unref (self->current_vis_element);
4041 self->current_vis_element = NULL;
4042 }
4043
4044 if (name) {
4045 self->current_vis_element = gst_element_factory_make (name, NULL);
4046 if (!self->current_vis_element)
4047 goto error_no_element;
4048 gst_object_ref_sink (self->current_vis_element);
4049 }
4050 g_object_set (self->playbin, "vis-plugin", self->current_vis_element, NULL);
4051
4052 g_mutex_unlock (&self->lock);
4053 GST_DEBUG_OBJECT (self, "set vis-plugin to '%s'", name);
4054
4055 return TRUE;
4056
4057 error_no_element:
4058 g_mutex_unlock (&self->lock);
4059 GST_WARNING_OBJECT (self, "could not find visualization '%s'", name);
4060 return FALSE;
4061 }
4062
4063 /**
4064 * gst_player_get_current_visualization:
4065 * @player: #GstPlayer instance
4066 *
4067 * Returns: (transfer full): Name of the currently enabled visualization.
4068 * g_free() after usage.
4069 */
4070 gchar *
gst_player_get_current_visualization(GstPlayer * self)4071 gst_player_get_current_visualization (GstPlayer * self)
4072 {
4073 gchar *name = NULL;
4074 GstElement *vis_plugin = NULL;
4075
4076 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4077
4078 if (!is_track_enabled (self, GST_PLAY_FLAG_VIS))
4079 return NULL;
4080
4081 g_object_get (self->playbin, "vis-plugin", &vis_plugin, NULL);
4082
4083 if (vis_plugin) {
4084 GstElementFactory *factory = gst_element_get_factory (vis_plugin);
4085 if (factory)
4086 name = g_strdup (gst_plugin_feature_get_name (factory));
4087 gst_object_unref (vis_plugin);
4088 }
4089
4090 GST_DEBUG_OBJECT (self, "vis-plugin '%s' %p", name, vis_plugin);
4091
4092 return name;
4093 }
4094
4095 /**
4096 * gst_player_set_visualization_enabled:
4097 * @player: #GstPlayer instance
4098 * @enabled: TRUE or FALSE
4099 *
4100 * Enable or disable the visualization.
4101 */
4102 void
gst_player_set_visualization_enabled(GstPlayer * self,gboolean enabled)4103 gst_player_set_visualization_enabled (GstPlayer * self, gboolean enabled)
4104 {
4105 g_return_if_fail (GST_IS_PLAYER (self));
4106
4107 if (enabled)
4108 player_set_flag (self, GST_PLAY_FLAG_VIS);
4109 else
4110 player_clear_flag (self, GST_PLAY_FLAG_VIS);
4111
4112 GST_DEBUG_OBJECT (self, "visualization is '%s'",
4113 enabled ? "Enabled" : "Disabled");
4114 }
4115
4116 struct CBChannelMap
4117 {
4118 const gchar *label; /* channel label name */
4119 const gchar *name; /* get_name () */
4120 };
4121
4122 static const struct CBChannelMap cb_channel_map[] = {
4123 /* GST_PLAYER_COLOR_BALANCE_BRIGHTNESS */ {"BRIGHTNESS", "brightness"},
4124 /* GST_PLAYER_COLOR_BALANCE_CONTRAST */ {"CONTRAST", "contrast"},
4125 /* GST_PLAYER_COLOR_BALANCE_SATURATION */ {"SATURATION", "saturation"},
4126 /* GST_PLAYER_COLOR_BALANCE_HUE */ {"HUE", "hue"},
4127 };
4128
4129 static GstColorBalanceChannel *
gst_player_color_balance_find_channel(GstPlayer * self,GstPlayerColorBalanceType type)4130 gst_player_color_balance_find_channel (GstPlayer * self,
4131 GstPlayerColorBalanceType type)
4132 {
4133 GstColorBalanceChannel *channel;
4134 const GList *l, *channels;
4135
4136 if (type < GST_PLAYER_COLOR_BALANCE_BRIGHTNESS ||
4137 type > GST_PLAYER_COLOR_BALANCE_HUE)
4138 return NULL;
4139
4140 channels =
4141 gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin));
4142 for (l = channels; l; l = l->next) {
4143 channel = l->data;
4144 if (g_strrstr (channel->label, cb_channel_map[type].label))
4145 return channel;
4146 }
4147
4148 return NULL;
4149 }
4150
4151 /**
4152 * gst_player_has_color_balance:
4153 * @player:#GstPlayer instance
4154 *
4155 * Checks whether the @player has color balance support available.
4156 *
4157 * Returns: %TRUE if @player has color balance support. Otherwise,
4158 * %FALSE.
4159 */
4160 gboolean
gst_player_has_color_balance(GstPlayer * self)4161 gst_player_has_color_balance (GstPlayer * self)
4162 {
4163 const GList *channels;
4164
4165 g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4166
4167 if (!GST_IS_COLOR_BALANCE (self->playbin))
4168 return FALSE;
4169
4170 channels =
4171 gst_color_balance_list_channels (GST_COLOR_BALANCE (self->playbin));
4172 return (channels != NULL);
4173 }
4174
4175 /**
4176 * gst_player_set_color_balance:
4177 * @player: #GstPlayer instance
4178 * @type: #GstPlayerColorBalanceType
4179 * @value: The new value for the @type, ranged [0,1]
4180 *
4181 * Sets the current value of the indicated channel @type to the passed
4182 * value.
4183 */
4184 void
gst_player_set_color_balance(GstPlayer * self,GstPlayerColorBalanceType type,gdouble value)4185 gst_player_set_color_balance (GstPlayer * self, GstPlayerColorBalanceType type,
4186 gdouble value)
4187 {
4188 GstColorBalanceChannel *channel;
4189 gdouble new_val;
4190
4191 g_return_if_fail (GST_IS_PLAYER (self));
4192 g_return_if_fail (value >= 0.0 && value <= 1.0);
4193
4194 if (!GST_IS_COLOR_BALANCE (self->playbin))
4195 return;
4196
4197 channel = gst_player_color_balance_find_channel (self, type);
4198 if (!channel)
4199 return;
4200
4201 value = CLAMP (value, 0.0, 1.0);
4202
4203 /* Convert to channel range */
4204 new_val = channel->min_value + value * ((gdouble) channel->max_value -
4205 (gdouble) channel->min_value);
4206
4207 gst_color_balance_set_value (GST_COLOR_BALANCE (self->playbin), channel,
4208 new_val);
4209 }
4210
4211 /**
4212 * gst_player_get_color_balance:
4213 * @player: #GstPlayer instance
4214 * @type: #GstPlayerColorBalanceType
4215 *
4216 * Retrieve the current value of the indicated @type.
4217 *
4218 * Returns: The current value of @type, between [0,1]. In case of
4219 * error -1 is returned.
4220 */
4221 gdouble
gst_player_get_color_balance(GstPlayer * self,GstPlayerColorBalanceType type)4222 gst_player_get_color_balance (GstPlayer * self, GstPlayerColorBalanceType type)
4223 {
4224 GstColorBalanceChannel *channel;
4225 gint value;
4226
4227 g_return_val_if_fail (GST_IS_PLAYER (self), -1);
4228
4229 if (!GST_IS_COLOR_BALANCE (self->playbin))
4230 return -1;
4231
4232 channel = gst_player_color_balance_find_channel (self, type);
4233 if (!channel)
4234 return -1;
4235
4236 value = gst_color_balance_get_value (GST_COLOR_BALANCE (self->playbin),
4237 channel);
4238
4239 return ((gdouble) value -
4240 (gdouble) channel->min_value) / ((gdouble) channel->max_value -
4241 (gdouble) channel->min_value);
4242 }
4243
4244 /**
4245 * gst_player_get_multiview_mode:
4246 * @player: #GstPlayer instance
4247 *
4248 * Retrieve the current value of the indicated @type.
4249 *
4250 * Returns: The current value of @type, Default: -1 "none"
4251 *
4252 * Since: 1.10
4253 */
4254 GstVideoMultiviewFramePacking
gst_player_get_multiview_mode(GstPlayer * self)4255 gst_player_get_multiview_mode (GstPlayer * self)
4256 {
4257 GstVideoMultiviewFramePacking val = GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE;
4258
4259 g_return_val_if_fail (GST_IS_PLAYER (self),
4260 GST_VIDEO_MULTIVIEW_FRAME_PACKING_NONE);
4261
4262 g_object_get (self, "video-multiview-mode", &val, NULL);
4263
4264 return val;
4265 }
4266
4267 /**
4268 * gst_player_set_multiview_mode:
4269 * @player: #GstPlayer instance
4270 * @mode: The new value for the @type
4271 *
4272 * Sets the current value of the indicated mode @type to the passed
4273 * value.
4274 *
4275 * Since: 1.10
4276 */
4277 void
gst_player_set_multiview_mode(GstPlayer * self,GstVideoMultiviewFramePacking mode)4278 gst_player_set_multiview_mode (GstPlayer * self,
4279 GstVideoMultiviewFramePacking mode)
4280 {
4281 g_return_if_fail (GST_IS_PLAYER (self));
4282
4283 g_object_set (self, "video-multiview-mode", mode, NULL);
4284 }
4285
4286 /**
4287 * gst_player_get_multiview_flags:
4288 * @player: #GstPlayer instance
4289 *
4290 * Retrieve the current value of the indicated @type.
4291 *
4292 * Returns: The current value of @type, Default: 0x00000000 "none
4293 *
4294 * Since: 1.10
4295 */
4296 GstVideoMultiviewFlags
gst_player_get_multiview_flags(GstPlayer * self)4297 gst_player_get_multiview_flags (GstPlayer * self)
4298 {
4299 GstVideoMultiviewFlags val = GST_VIDEO_MULTIVIEW_FLAGS_NONE;
4300
4301 g_return_val_if_fail (GST_IS_PLAYER (self), val);
4302
4303 g_object_get (self, "video-multiview-flags", &val, NULL);
4304
4305 return val;
4306 }
4307
4308 /**
4309 * gst_player_set_multiview_flags:
4310 * @player: #GstPlayer instance
4311 * @flags: The new value for the @type
4312 *
4313 * Sets the current value of the indicated mode @type to the passed
4314 * value.
4315 *
4316 * Since: 1.10
4317 */
4318 void
gst_player_set_multiview_flags(GstPlayer * self,GstVideoMultiviewFlags flags)4319 gst_player_set_multiview_flags (GstPlayer * self, GstVideoMultiviewFlags flags)
4320 {
4321 g_return_if_fail (GST_IS_PLAYER (self));
4322
4323 g_object_set (self, "video-multiview-flags", flags, NULL);
4324 }
4325
4326 /**
4327 * gst_player_get_audio_video_offset:
4328 * @player: #GstPlayer instance
4329 *
4330 * Retrieve the current value of audio-video-offset property
4331 *
4332 * Returns: The current value of audio-video-offset in nanoseconds
4333 *
4334 * Since: 1.10
4335 */
4336 gint64
gst_player_get_audio_video_offset(GstPlayer * self)4337 gst_player_get_audio_video_offset (GstPlayer * self)
4338 {
4339 gint64 val = 0;
4340
4341 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_AUDIO_VIDEO_OFFSET);
4342
4343 g_object_get (self, "audio-video-offset", &val, NULL);
4344
4345 return val;
4346 }
4347
4348 /**
4349 * gst_player_set_audio_video_offset:
4350 * @player: #GstPlayer instance
4351 * @offset: #gint64 in nanoseconds
4352 *
4353 * Sets audio-video-offset property by value of @offset
4354 *
4355 * Since: 1.10
4356 */
4357 void
gst_player_set_audio_video_offset(GstPlayer * self,gint64 offset)4358 gst_player_set_audio_video_offset (GstPlayer * self, gint64 offset)
4359 {
4360 g_return_if_fail (GST_IS_PLAYER (self));
4361
4362 g_object_set (self, "audio-video-offset", offset, NULL);
4363 }
4364
4365 /**
4366 * gst_player_get_subtitle_video_offset:
4367 * @player: #GstPlayer instance
4368 *
4369 * Retrieve the current value of subtitle-video-offset property
4370 *
4371 * Returns: The current value of subtitle-video-offset in nanoseconds
4372 *
4373 * Since: 1.16
4374 */
4375 gint64
gst_player_get_subtitle_video_offset(GstPlayer * self)4376 gst_player_get_subtitle_video_offset (GstPlayer * self)
4377 {
4378 gint64 val = 0;
4379
4380 g_return_val_if_fail (GST_IS_PLAYER (self), DEFAULT_SUBTITLE_VIDEO_OFFSET);
4381
4382 g_object_get (self, "subtitle-video-offset", &val, NULL);
4383
4384 return val;
4385 }
4386
4387 /**
4388 * gst_player_set_subtitle_video_offset:
4389 * @player: #GstPlayer instance
4390 * @offset: #gint64 in nanoseconds
4391 *
4392 * Sets subtitle-video-offset property by value of @offset
4393 *
4394 * Since: 1.16
4395 */
4396 void
gst_player_set_subtitle_video_offset(GstPlayer * self,gint64 offset)4397 gst_player_set_subtitle_video_offset (GstPlayer * self, gint64 offset)
4398 {
4399 g_return_if_fail (GST_IS_PLAYER (self));
4400
4401 g_object_set (self, "subtitle-video-offset", offset, NULL);
4402 }
4403
4404
4405 #define C_ENUM(v) ((gint) v)
4406 #define C_FLAGS(v) ((guint) v)
4407
4408 GType
gst_player_color_balance_type_get_type(void)4409 gst_player_color_balance_type_get_type (void)
4410 {
4411 static gsize id = 0;
4412 static const GEnumValue values[] = {
4413 {C_ENUM (GST_PLAYER_COLOR_BALANCE_HUE), "GST_PLAYER_COLOR_BALANCE_HUE",
4414 "hue"},
4415 {C_ENUM (GST_PLAYER_COLOR_BALANCE_BRIGHTNESS),
4416 "GST_PLAYER_COLOR_BALANCE_BRIGHTNESS", "brightness"},
4417 {C_ENUM (GST_PLAYER_COLOR_BALANCE_SATURATION),
4418 "GST_PLAYER_COLOR_BALANCE_SATURATION", "saturation"},
4419 {C_ENUM (GST_PLAYER_COLOR_BALANCE_CONTRAST),
4420 "GST_PLAYER_COLOR_BALANCE_CONTRAST", "contrast"},
4421 {0, NULL, NULL}
4422 };
4423
4424 if (g_once_init_enter (&id)) {
4425 GType tmp = g_enum_register_static ("GstPlayerColorBalanceType", values);
4426 g_once_init_leave (&id, tmp);
4427 }
4428
4429 return (GType) id;
4430 }
4431
4432 /**
4433 * gst_player_color_balance_type_get_name:
4434 * @type: a #GstPlayerColorBalanceType
4435 *
4436 * Gets a string representing the given color balance type.
4437 *
4438 * Returns: (transfer none): a string with the name of the color
4439 * balance type.
4440 */
4441 const gchar *
gst_player_color_balance_type_get_name(GstPlayerColorBalanceType type)4442 gst_player_color_balance_type_get_name (GstPlayerColorBalanceType type)
4443 {
4444 g_return_val_if_fail (type >= GST_PLAYER_COLOR_BALANCE_BRIGHTNESS &&
4445 type <= GST_PLAYER_COLOR_BALANCE_HUE, NULL);
4446
4447 return cb_channel_map[type].name;
4448 }
4449
4450 GType
gst_player_state_get_type(void)4451 gst_player_state_get_type (void)
4452 {
4453 static gsize id = 0;
4454 static const GEnumValue values[] = {
4455 {C_ENUM (GST_PLAYER_STATE_STOPPED), "GST_PLAYER_STATE_STOPPED", "stopped"},
4456 {C_ENUM (GST_PLAYER_STATE_BUFFERING), "GST_PLAYER_STATE_BUFFERING",
4457 "buffering"},
4458 {C_ENUM (GST_PLAYER_STATE_PAUSED), "GST_PLAYER_STATE_PAUSED", "paused"},
4459 {C_ENUM (GST_PLAYER_STATE_PLAYING), "GST_PLAYER_STATE_PLAYING", "playing"},
4460 {0, NULL, NULL}
4461 };
4462
4463 if (g_once_init_enter (&id)) {
4464 GType tmp = g_enum_register_static ("GstPlayerState", values);
4465 g_once_init_leave (&id, tmp);
4466 }
4467
4468 return (GType) id;
4469 }
4470
4471 /**
4472 * gst_player_state_get_name:
4473 * @state: a #GstPlayerState
4474 *
4475 * Gets a string representing the given state.
4476 *
4477 * Returns: (transfer none): a string with the name of the state.
4478 */
4479 const gchar *
gst_player_state_get_name(GstPlayerState state)4480 gst_player_state_get_name (GstPlayerState state)
4481 {
4482 switch (state) {
4483 case GST_PLAYER_STATE_STOPPED:
4484 return "stopped";
4485 case GST_PLAYER_STATE_BUFFERING:
4486 return "buffering";
4487 case GST_PLAYER_STATE_PAUSED:
4488 return "paused";
4489 case GST_PLAYER_STATE_PLAYING:
4490 return "playing";
4491 }
4492
4493 g_assert_not_reached ();
4494 return NULL;
4495 }
4496
4497 GType
gst_player_error_get_type(void)4498 gst_player_error_get_type (void)
4499 {
4500 static gsize id = 0;
4501 static const GEnumValue values[] = {
4502 {C_ENUM (GST_PLAYER_ERROR_FAILED), "GST_PLAYER_ERROR_FAILED", "failed"},
4503 {0, NULL, NULL}
4504 };
4505
4506 if (g_once_init_enter (&id)) {
4507 GType tmp = g_enum_register_static ("GstPlayerError", values);
4508 g_once_init_leave (&id, tmp);
4509 }
4510
4511 return (GType) id;
4512 }
4513
4514 /**
4515 * gst_player_error_get_name:
4516 * @error: a #GstPlayerError
4517 *
4518 * Gets a string representing the given error.
4519 *
4520 * Returns: (transfer none): a string with the given error.
4521 */
4522 const gchar *
gst_player_error_get_name(GstPlayerError error)4523 gst_player_error_get_name (GstPlayerError error)
4524 {
4525 switch (error) {
4526 case GST_PLAYER_ERROR_FAILED:
4527 return "failed";
4528 }
4529
4530 g_assert_not_reached ();
4531 return NULL;
4532 }
4533
4534 /**
4535 * gst_player_set_config:
4536 * @player: #GstPlayer instance
4537 * @config: (transfer full): a #GstStructure
4538 *
4539 * Set the configuration of the player. If the player is already configured, and
4540 * the configuration haven't change, this function will return %TRUE. If the
4541 * player is not in the GST_PLAYER_STATE_STOPPED, this method will return %FALSE
4542 * and active configuration will remain.
4543 *
4544 * @config is a #GstStructure that contains the configuration parameters for
4545 * the player.
4546 *
4547 * This function takes ownership of @config.
4548 *
4549 * Returns: %TRUE when the configuration could be set.
4550 * Since: 1.10
4551 */
4552 gboolean
gst_player_set_config(GstPlayer * self,GstStructure * config)4553 gst_player_set_config (GstPlayer * self, GstStructure * config)
4554 {
4555 g_return_val_if_fail (GST_IS_PLAYER (self), FALSE);
4556 g_return_val_if_fail (config != NULL, FALSE);
4557
4558 g_mutex_lock (&self->lock);
4559
4560 if (self->app_state != GST_PLAYER_STATE_STOPPED) {
4561 GST_INFO_OBJECT (self, "can't change config while player is %s",
4562 gst_player_state_get_name (self->app_state));
4563 g_mutex_unlock (&self->lock);
4564 return FALSE;
4565 }
4566
4567 if (self->config)
4568 gst_structure_free (self->config);
4569 self->config = config;
4570 g_mutex_unlock (&self->lock);
4571
4572 return TRUE;
4573 }
4574
4575 /**
4576 * gst_player_get_config:
4577 * @player: #GstPlayer instance
4578 *
4579 * Get a copy of the current configuration of the player. This configuration
4580 * can either be modified and used for the gst_player_set_config() call
4581 * or it must be freed after usage.
4582 *
4583 * Returns: (transfer full): a copy of the current configuration of @player. Use
4584 * gst_structure_free() after usage or gst_player_set_config().
4585 *
4586 * Since: 1.10
4587 */
4588 GstStructure *
gst_player_get_config(GstPlayer * self)4589 gst_player_get_config (GstPlayer * self)
4590 {
4591 GstStructure *ret;
4592
4593 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4594
4595 g_mutex_lock (&self->lock);
4596 ret = gst_structure_copy (self->config);
4597 g_mutex_unlock (&self->lock);
4598
4599 return ret;
4600 }
4601
4602 /**
4603 * gst_player_config_set_user_agent:
4604 * @config: a #GstPlayer configuration
4605 * @agent: the string to use as user agent
4606 *
4607 * Set the user agent to pass to the server if @player needs to connect
4608 * to a server during playback. This is typically used when playing HTTP
4609 * or RTSP streams.
4610 *
4611 * Since: 1.10
4612 */
4613 void
gst_player_config_set_user_agent(GstStructure * config,const gchar * agent)4614 gst_player_config_set_user_agent (GstStructure * config, const gchar * agent)
4615 {
4616 g_return_if_fail (config != NULL);
4617 g_return_if_fail (agent != NULL);
4618
4619 gst_structure_id_set (config,
4620 CONFIG_QUARK (USER_AGENT), G_TYPE_STRING, agent, NULL);
4621 }
4622
4623 /**
4624 * gst_player_config_get_user_agent:
4625 * @config: a #GstPlayer configuration
4626 *
4627 * Return the user agent which has been configured using
4628 * gst_player_config_set_user_agent() if any.
4629 *
4630 * Returns: (transfer full): the configured agent, or %NULL
4631 * Since: 1.10
4632 */
4633 gchar *
gst_player_config_get_user_agent(const GstStructure * config)4634 gst_player_config_get_user_agent (const GstStructure * config)
4635 {
4636 gchar *agent = NULL;
4637
4638 g_return_val_if_fail (config != NULL, NULL);
4639
4640 gst_structure_id_get (config,
4641 CONFIG_QUARK (USER_AGENT), G_TYPE_STRING, &agent, NULL);
4642
4643 return agent;
4644 }
4645
4646 /**
4647 * gst_player_config_set_position_update_interval:
4648 * @config: a #GstPlayer configuration
4649 * @interval: interval in ms
4650 *
4651 * set interval in milliseconds between two position-updated signals.
4652 * pass 0 to stop updating the position.
4653 * Since: 1.10
4654 */
4655 void
gst_player_config_set_position_update_interval(GstStructure * config,guint interval)4656 gst_player_config_set_position_update_interval (GstStructure * config,
4657 guint interval)
4658 {
4659 g_return_if_fail (config != NULL);
4660 g_return_if_fail (interval <= 10000);
4661
4662 gst_structure_id_set (config,
4663 CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, interval, NULL);
4664 }
4665
4666 /**
4667 * gst_player_config_get_position_update_interval:
4668 * @config: a #GstPlayer configuration
4669 *
4670 * Returns: current position update interval in milliseconds
4671 *
4672 * Since: 1.10
4673 */
4674 guint
gst_player_config_get_position_update_interval(const GstStructure * config)4675 gst_player_config_get_position_update_interval (const GstStructure * config)
4676 {
4677 guint interval = DEFAULT_POSITION_UPDATE_INTERVAL_MS;
4678
4679 g_return_val_if_fail (config != NULL, DEFAULT_POSITION_UPDATE_INTERVAL_MS);
4680
4681 gst_structure_id_get (config,
4682 CONFIG_QUARK (POSITION_INTERVAL_UPDATE), G_TYPE_UINT, &interval, NULL);
4683
4684 return interval;
4685 }
4686
4687 /**
4688 * gst_player_config_set_seek_accurate:
4689 * @config: a #GstPlayer configuration
4690 * @accurate: accurate seek or not
4691 *
4692 * Enable or disable accurate seeking. When enabled, elements will try harder
4693 * to seek as accurately as possible to the requested seek position. Generally
4694 * it will be slower especially for formats that don't have any indexes or
4695 * timestamp markers in the stream.
4696 *
4697 * If accurate seeking is disabled, elements will seek as close as the request
4698 * position without slowing down seeking too much.
4699 *
4700 * Accurate seeking is disabled by default.
4701 *
4702 * Since: 1.12
4703 */
4704 void
gst_player_config_set_seek_accurate(GstStructure * config,gboolean accurate)4705 gst_player_config_set_seek_accurate (GstStructure * config, gboolean accurate)
4706 {
4707 g_return_if_fail (config != NULL);
4708
4709 gst_structure_id_set (config,
4710 CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, accurate, NULL);
4711 }
4712
4713 /**
4714 * gst_player_config_get_seek_accurate:
4715 * @config: a #GstPlayer configuration
4716 *
4717 * Returns: %TRUE if accurate seeking is enabled
4718 *
4719 * Since: 1.12
4720 */
4721 gboolean
gst_player_config_get_seek_accurate(const GstStructure * config)4722 gst_player_config_get_seek_accurate (const GstStructure * config)
4723 {
4724 gboolean accurate = FALSE;
4725
4726 g_return_val_if_fail (config != NULL, FALSE);
4727
4728 gst_structure_id_get (config,
4729 CONFIG_QUARK (ACCURATE_SEEK), G_TYPE_BOOLEAN, &accurate, NULL);
4730
4731 return accurate;
4732 }
4733
4734 /**
4735 * gst_player_get_video_snapshot:
4736 * @player: #GstPlayer instance
4737 * @format: output format of the video snapshot
4738 * @config: (allow-none): Additional configuration
4739 *
4740 * Get a snapshot of the currently selected video stream, if any. The format can be
4741 * selected with @format and optional configuration is possible with @config
4742 * Currently supported settings are:
4743 * - width, height of type G_TYPE_INT
4744 * - pixel-aspect-ratio of type GST_TYPE_FRACTION
4745 * Except for GST_PLAYER_THUMBNAIL_RAW_NATIVE format, if no config is set, pixel-aspect-ratio would be 1/1
4746 *
4747 * Returns: (transfer full): Current video snapshot sample or %NULL on failure
4748 *
4749 * Since: 1.12
4750 */
4751 GstSample *
gst_player_get_video_snapshot(GstPlayer * self,GstPlayerSnapshotFormat format,const GstStructure * config)4752 gst_player_get_video_snapshot (GstPlayer * self,
4753 GstPlayerSnapshotFormat format, const GstStructure * config)
4754 {
4755 gint video_tracks = 0;
4756 GstSample *sample = NULL;
4757 GstCaps *caps = NULL;
4758 gint width = -1;
4759 gint height = -1;
4760 gint par_n = 1;
4761 gint par_d = 1;
4762 g_return_val_if_fail (GST_IS_PLAYER (self), NULL);
4763
4764 g_object_get (self->playbin, "n-video", &video_tracks, NULL);
4765 if (video_tracks == 0) {
4766 GST_DEBUG_OBJECT (self, "total video track num is 0");
4767 return NULL;
4768 }
4769
4770 switch (format) {
4771 case GST_PLAYER_THUMBNAIL_RAW_xRGB:
4772 caps = gst_caps_new_simple ("video/x-raw",
4773 "format", G_TYPE_STRING, "xRGB", NULL);
4774 break;
4775 case GST_PLAYER_THUMBNAIL_RAW_BGRx:
4776 caps = gst_caps_new_simple ("video/x-raw",
4777 "format", G_TYPE_STRING, "BGRx", NULL);
4778 break;
4779 case GST_PLAYER_THUMBNAIL_JPG:
4780 caps = gst_caps_new_empty_simple ("image/jpeg");
4781 break;
4782 case GST_PLAYER_THUMBNAIL_PNG:
4783 caps = gst_caps_new_empty_simple ("image/png");
4784 break;
4785 case GST_PLAYER_THUMBNAIL_RAW_NATIVE:
4786 default:
4787 caps = gst_caps_new_empty_simple ("video/x-raw");
4788 break;
4789 }
4790
4791 if (NULL != config) {
4792 if (!gst_structure_get_int (config, "width", &width))
4793 width = -1;
4794 if (!gst_structure_get_int (config, "height", &height))
4795 height = -1;
4796 if (!gst_structure_get_fraction (config, "pixel-aspect-ratio", &par_n,
4797 &par_d)) {
4798 if (format != GST_PLAYER_THUMBNAIL_RAW_NATIVE) {
4799 par_n = 1;
4800 par_d = 1;
4801 } else {
4802 par_n = 0;
4803 par_d = 0;
4804 }
4805 }
4806 }
4807
4808 if (width > 0 && height > 0) {
4809 gst_caps_set_simple (caps, "width", G_TYPE_INT, width,
4810 "height", G_TYPE_INT, height, NULL);
4811 }
4812
4813 if (format != GST_PLAYER_THUMBNAIL_RAW_NATIVE) {
4814 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
4815 par_n, par_d, NULL);
4816 } else if (NULL != config && par_n != 0 && par_d != 0) {
4817 gst_caps_set_simple (caps, "pixel-aspect-ratio", GST_TYPE_FRACTION,
4818 par_n, par_d, NULL);
4819 }
4820
4821 g_signal_emit_by_name (self->playbin, "convert-sample", caps, &sample);
4822 gst_caps_unref (caps);
4823 if (!sample) {
4824 GST_WARNING_OBJECT (self, "Failed to retrieve or convert video frame");
4825 return NULL;
4826 }
4827
4828 return sample;
4829 }
4830