~ubuntu-branches/ubuntu/wily/clutter-gst-3.0/wily

« back to all changes in this revision

Viewing changes to clutter-gst/clutter-gst-playback.c

  • Committer: Package Import Robot
  • Author(s): Emilio Pozuelo Monfort
  • Date: 2015-06-13 13:51:21 UTC
  • Revision ID: package-import@ubuntu.com-20150613135121-w3b13f9ogdulgrxv
Tags: upstream-3.0.4
Import upstream version 3.0.4

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Clutter-GStreamer.
 
3
 *
 
4
 * GStreamer integration library for Clutter.
 
5
 *
 
6
 * clutter-gst-player.c - Wrap some convenience functions around playbin
 
7
 *
 
8
 * Authored By Damien Lespiau    <damien.lespiau@intel.com>
 
9
 *             Lionel Landwerlin <lionel.g.landwerlin@linux.intel.com>
 
10
 *             Matthew Allum     <mallum@openedhand.com>
 
11
 *             Emmanuele Bassi   <ebassi@linux.intel.com>
 
12
 *             Andre Moreira Magalhaes <andre.magalhaes@collabora.co.uk>
 
13
 *
 
14
 * Copyright (C) 2006 OpenedHand
 
15
 * Copyright (C) 2009-2013 Intel Corporation
 
16
 * Copyright (C) 2012 Collabora Ltd. <http://www.collabora.co.uk/>
 
17
 *
 
18
 * This library is free software; you can redistribute it and/or
 
19
 * modify it under the terms of the GNU Lesser General Public
 
20
 * License as published by the Free Software Foundation; either
 
21
 * version 2 of the License, or (at your option) any later version.
 
22
 *
 
23
 * This library is distributed in the hope that it will be useful,
 
24
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 
25
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
26
 * Lesser General Public License for more details.
 
27
 *
 
28
 * You should have received a copy of the GNU Lesser General Public
 
29
 * License along with this library; if not, write to the
 
30
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 
31
 * Boston, MA 02111-1307, USA.
 
32
 */
 
33
 
 
34
/**
 
35
 * SECTION:clutter-gst-playback
 
36
 * @short_description: A #ClutterGstPlayback to play media streams
 
37
 *
 
38
 * #ClutterGstPlayback implements #ClutterGstPlayer.
 
39
 */
 
40
 
 
41
#ifdef HAVE_CONFIG_H
 
42
#include "config.h"
 
43
#endif
 
44
 
 
45
#include "clutter-gst-debug.h"
 
46
#include "clutter-gst-enum-types.h"
 
47
#include "clutter-gst-marshal.h"
 
48
#include "clutter-gst-playback.h"
 
49
#include "clutter-gst-player.h"
 
50
#include "clutter-gst-private.h"
 
51
 
 
52
#include <string.h>
 
53
 
 
54
#include <gio/gio.h>
 
55
#include <gst/video/video.h>
 
56
#include <gst/tag/tag.h>
 
57
#include <gst/audio/streamvolume.h>
 
58
 
 
59
#if defined (CLUTTER_WINDOWING_X11) && defined (HAVE_HW_DECODER_SUPPORT)
 
60
#define GST_USE_UNSTABLE_API 1
 
61
#include <gst/video/videocontext.h>
 
62
#include <clutter/x11/clutter-x11.h>
 
63
#endif
 
64
 
 
65
static void player_iface_init (ClutterGstPlayerIface *iface);
 
66
 
 
67
G_DEFINE_TYPE_WITH_CODE (ClutterGstPlayback, clutter_gst_playback, G_TYPE_OBJECT,
 
68
                         G_IMPLEMENT_INTERFACE (CLUTTER_GST_TYPE_PLAYER, player_iface_init))
 
69
 
 
70
#define GST_PLAYBACK_PRIVATE(o) \
 
71
  (G_TYPE_INSTANCE_GET_PRIVATE ((o), CLUTTER_GST_TYPE_PLAYBACK, ClutterGstPlaybackPrivate))
 
72
 
 
73
/* idle timeouts (in ms) */
 
74
#define TICK_TIMEOUT        500
 
75
#define BUFFERING_TIMEOUT   250
 
76
 
 
77
enum
 
78
{
 
79
  PROP_0,
 
80
 
 
81
  PROP_URI,
 
82
  PROP_PLAYING,
 
83
  PROP_PROGRESS,
 
84
  PROP_SUBTITLE_URI,
 
85
  PROP_SUBTITLE_FONT_NAME,
 
86
  PROP_AUDIO_VOLUME,
 
87
  PROP_CAN_SEEK,
 
88
  PROP_BUFFER_FILL,
 
89
  PROP_DURATION,
 
90
  PROP_IDLE,
 
91
  PROP_USER_AGENT,
 
92
  PROP_SEEK_FLAGS,
 
93
  PROP_AUDIO_STREAMS,
 
94
  PROP_AUDIO_STREAM,
 
95
  PROP_SUBTITLE_TRACKS,
 
96
  PROP_SUBTITLE_TRACK,
 
97
  PROP_IN_SEEK
 
98
};
 
99
 
 
100
enum
 
101
{
 
102
  SHOULD_BUFFER = 1,
 
103
 
 
104
  LAST_SIGNAL
 
105
};
 
106
 
 
107
/* Elements don't expose header files */
 
108
typedef enum {
 
109
  GST_PLAY_FLAG_VIDEO         = (1 << 0),
 
110
  GST_PLAY_FLAG_AUDIO         = (1 << 1),
 
111
  GST_PLAY_FLAG_TEXT          = (1 << 2),
 
112
  GST_PLAY_FLAG_VIS           = (1 << 3),
 
113
  GST_PLAY_FLAG_SOFT_VOLUME   = (1 << 4),
 
114
  GST_PLAY_FLAG_NATIVE_AUDIO  = (1 << 5),
 
115
  GST_PLAY_FLAG_NATIVE_VIDEO  = (1 << 6),
 
116
  GST_PLAY_FLAG_DOWNLOAD      = (1 << 7),
 
117
  GST_PLAY_FLAG_BUFFERING     = (1 << 8),
 
118
  GST_PLAY_FLAG_DEINTERLACE   = (1 << 9)
 
119
} GstPlayFlags;
 
120
 
 
121
struct _ClutterGstPlaybackPrivate
 
122
{
 
123
  GstElement *pipeline;
 
124
  GstBus *bus;
 
125
  ClutterGstVideoSink *video_sink;
 
126
  GArray *gst_pipe_sigs;
 
127
  GArray *gst_bus_sigs;
 
128
 
 
129
  ClutterGstFrame *current_frame;
 
130
 
 
131
  gchar *uri;
 
132
 
 
133
  guint is_idle : 1;
 
134
  guint is_live : 1;
 
135
  guint can_seek : 1;
 
136
  guint in_seek : 1;
 
137
  guint is_changing_uri : 1;
 
138
  guint in_error : 1;
 
139
  guint in_eos : 1;
 
140
  guint in_download_buffering : 1;
 
141
 
 
142
  gdouble stacked_progress;
 
143
 
 
144
  gdouble target_progress;
 
145
  GstState target_state;
 
146
  GstState force_state;
 
147
 
 
148
  guint tick_timeout_id;
 
149
  guint buffering_timeout_id;
 
150
 
 
151
  /* This is a cubic volume, suitable for use in a UI cf. StreamVolume doc */
 
152
  gdouble volume;
 
153
 
 
154
  gdouble buffer_fill;
 
155
  gdouble duration;
 
156
  gchar *font_name;
 
157
  gchar *user_agent;
 
158
 
 
159
  GstSeekFlags seek_flags;    /* flags for the seek in set_progress(); */
 
160
 
 
161
  GList *audio_streams;
 
162
  GList *subtitle_tracks;
 
163
};
 
164
 
 
165
 
 
166
static guint signals[LAST_SIGNAL] = { 0, };
 
167
 
 
168
static gboolean player_buffering_timeout (gpointer data);
 
169
 
 
170
/* Logic */
 
171
 
 
172
#ifdef CLUTTER_GST_ENABLE_DEBUG
 
173
static gchar *
 
174
get_stream_description (GstTagList *tags,
 
175
                        gint        track_num)
 
176
{
 
177
  gchar *description = NULL;
 
178
 
 
179
  if (tags)
 
180
    {
 
181
 
 
182
      gst_tag_list_get_string (tags, GST_TAG_LANGUAGE_CODE, &description);
 
183
 
 
184
      if (description)
 
185
        {
 
186
          const gchar *language = gst_tag_get_language_name (description);
 
187
 
 
188
          if (language)
 
189
            {
 
190
              g_free (description);
 
191
              description = g_strdup (language);
 
192
            }
 
193
        }
 
194
 
 
195
      if (!description)
 
196
        gst_tag_list_get_string (tags, GST_TAG_CODEC, &description);
 
197
    }
 
198
 
 
199
  if (!description)
 
200
    description = g_strdup_printf ("Track %d", track_num);
 
201
 
 
202
  return description;
 
203
}
 
204
 
 
205
gchar *
 
206
list_to_string (GList *list)
 
207
{
 
208
  GstTagList *tags;
 
209
  gchar *description;
 
210
  GString *string;
 
211
  GList *l;
 
212
  gint n, i;
 
213
 
 
214
  if (!list)
 
215
    return g_strdup ("<empty list>");
 
216
 
 
217
  string = g_string_new (NULL);
 
218
  n = g_list_length (list);
 
219
  for (i = 0, l = list; i < n - 1; i++, l = g_list_next (l))
 
220
    {
 
221
      tags = l->data;
 
222
      description = get_stream_description (tags, i);
 
223
      g_string_append_printf (string, "%s, ", description);
 
224
      g_free (description);
 
225
    }
 
226
 
 
227
  tags = l->data;
 
228
  description = get_stream_description (tags, i);
 
229
  g_string_append_printf (string, "%s", (gchar *) description);
 
230
  g_free (description);
 
231
 
 
232
  return g_string_free (string, FALSE);
 
233
}
 
234
#endif
 
235
 
 
236
static const gchar *
 
237
gst_state_to_string (GstState state)
 
238
{
 
239
  switch (state)
 
240
    {
 
241
    case GST_STATE_VOID_PENDING:
 
242
      return "pending";
 
243
    case GST_STATE_NULL:
 
244
      return "null";
 
245
    case GST_STATE_READY:
 
246
      return "ready";
 
247
    case GST_STATE_PAUSED:
 
248
      return "paused";
 
249
    case GST_STATE_PLAYING:
 
250
      return "playing";
 
251
    }
 
252
 
 
253
  return "Unknown state";
 
254
}
 
255
 
 
256
static void
 
257
free_tags_list (GList **listp)
 
258
{
 
259
  GList *l;
 
260
 
 
261
  l = *listp;
 
262
  while (l)
 
263
    {
 
264
      if (l->data)
 
265
        gst_tag_list_unref (l->data);
 
266
      l = g_list_delete_link (l, l);
 
267
    }
 
268
 
 
269
  *listp = NULL;
 
270
}
 
271
 
 
272
static GstStateChangeReturn
 
273
set_pipeline_target_state (ClutterGstPlayback *self, GstState state)
 
274
{
 
275
  ClutterGstPlaybackPrivate *priv = self->priv;
 
276
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
 
277
 
 
278
  priv->target_state = state;
 
279
  if (!priv->pipeline || !priv->uri)
 
280
    goto out;
 
281
 
 
282
  if (priv->force_state == GST_STATE_VOID_PENDING)
 
283
    ret = gst_element_set_state (priv->pipeline, state);
 
284
 
 
285
out:
 
286
  return ret;
 
287
}
 
288
 
 
289
static GstStateChangeReturn
 
290
force_pipeline_state (ClutterGstPlayback *self, GstState state)
 
291
{
 
292
  ClutterGstPlaybackPrivate *priv = self->priv;
 
293
  GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
 
294
 
 
295
  priv->force_state = state;
 
296
  if (!priv->pipeline)
 
297
    goto out;
 
298
 
 
299
  if (state == GST_STATE_VOID_PENDING)
 
300
    state = priv->target_state;
 
301
 
 
302
  ret = gst_element_set_state (priv->pipeline, state);
 
303
 
 
304
out:
 
305
  return ret;
 
306
}
 
307
 
 
308
 
 
309
static gboolean
 
310
tick_timeout (gpointer data)
 
311
{
 
312
  GObject *player = data;
 
313
 
 
314
  g_object_notify (player, "progress");
 
315
 
 
316
  return TRUE;
 
317
}
 
318
 
 
319
static void
 
320
player_set_user_agent (ClutterGstPlayback *self,
 
321
                       const gchar        *user_agent)
 
322
{
 
323
  ClutterGstPlaybackPrivate *priv = self->priv;
 
324
  GstElement *source;
 
325
  GParamSpec *pspec;
 
326
 
 
327
  if (user_agent == NULL)
 
328
    return;
 
329
 
 
330
  g_object_get (priv->pipeline, "source", &source, NULL);
 
331
  if (source == NULL)
 
332
    return;
 
333
 
 
334
  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source),
 
335
                                        "user-agent");
 
336
  if (pspec == NULL)
 
337
    return;
 
338
 
 
339
  CLUTTER_GST_NOTE (MEDIA, "setting user agent: %s", user_agent);
 
340
 
 
341
  g_object_set (source, "user-agent", user_agent, NULL);
 
342
}
 
343
 
 
344
static void
 
345
autoload_subtitle (ClutterGstPlayback *self,
 
346
                   const gchar        *uri)
 
347
{
 
348
  ClutterGstPlaybackPrivate *priv = self->priv;
 
349
  gchar *path, *dot, *subtitle_path;
 
350
  GFile *video;
 
351
  guint i;
 
352
 
 
353
  static const char subtitles_extensions[][4] =
 
354
    {
 
355
      "sub", "SUB",
 
356
      "srt", "SRT",
 
357
      "smi", "SMI",
 
358
      "ssa", "SSA",
 
359
      "ass", "ASS",
 
360
      "asc", "ASC"
 
361
    };
 
362
 
 
363
  /* do not try to look for subtitle files if the video file is not mounted
 
364
   * locally */
 
365
  if (!g_str_has_prefix (uri, "file://"))
 
366
    return;
 
367
 
 
368
  /* Retrieve the absolute path of the video file */
 
369
  video = g_file_new_for_uri (uri);
 
370
  path = g_file_get_path (video);
 
371
  g_object_unref (video);
 
372
  if (path == NULL)
 
373
    return;
 
374
 
 
375
  /* Put a '\0' after the dot of the extension */
 
376
  dot = strrchr (path, '.');
 
377
  if (dot == NULL) {
 
378
    g_free (path);
 
379
    return;
 
380
  }
 
381
  *++dot = '\0';
 
382
 
 
383
  /* we can't use path as the temporary buffer for the paths of the potential
 
384
   * subtitle files as we may not have enough room there */
 
385
  subtitle_path = g_malloc (strlen (path) + 1 + 4);
 
386
  strcpy (subtitle_path, path);
 
387
 
 
388
  /* reuse dot to point to the first byte of the extension of subtitle_path */
 
389
  dot = subtitle_path + (dot - path);
 
390
 
 
391
  for (i = 0; i < G_N_ELEMENTS (subtitles_extensions); i++)
 
392
    {
 
393
      GFile *candidate;
 
394
 
 
395
      memcpy (dot, subtitles_extensions[i], 4);
 
396
      candidate = g_file_new_for_path (subtitle_path);
 
397
      if (g_file_query_exists (candidate, NULL))
 
398
        {
 
399
          gchar *suburi;
 
400
 
 
401
          suburi = g_file_get_uri (candidate);
 
402
 
 
403
          CLUTTER_GST_NOTE (MEDIA, "found subtitle: %s", suburi);
 
404
 
 
405
          g_object_set (priv->pipeline, "suburi", suburi, NULL);
 
406
          g_free (suburi);
 
407
 
 
408
          g_object_unref (candidate);
 
409
          break;
 
410
        }
 
411
 
 
412
      g_object_unref (candidate);
 
413
    }
 
414
 
 
415
  g_free (path);
 
416
  g_free (subtitle_path);
 
417
}
 
418
 
 
419
static void
 
420
set_subtitle_uri (ClutterGstPlayback *self,
 
421
                  const gchar        *uri)
 
422
{
 
423
  ClutterGstPlaybackPrivate *priv = self->priv;
 
424
  GstPlayFlags flags;
 
425
 
 
426
  if (!priv->pipeline)
 
427
    return;
 
428
 
 
429
  CLUTTER_GST_NOTE (MEDIA, "setting subtitle URI: %s", uri);
 
430
 
 
431
  g_object_get (priv->pipeline, "flags", &flags, NULL);
 
432
 
 
433
  g_object_set (priv->pipeline, "suburi", uri, NULL);
 
434
 
 
435
  g_object_set (priv->pipeline, "flags", flags, NULL);
 
436
}
 
437
 
 
438
static void
 
439
player_configure_buffering_timeout (ClutterGstPlayback *self,
 
440
                                    guint               ms)
 
441
{
 
442
  ClutterGstPlaybackPrivate *priv = self->priv;
 
443
 
 
444
  if (priv->buffering_timeout_id)
 
445
    {
 
446
      g_source_remove (priv->buffering_timeout_id);
 
447
      priv->buffering_timeout_id = 0;
 
448
    }
 
449
 
 
450
  if (ms)
 
451
    {
 
452
      priv->buffering_timeout_id =
 
453
        g_timeout_add (ms, player_buffering_timeout, self);
 
454
      player_buffering_timeout (self);
 
455
 
 
456
    }
 
457
}
 
458
 
 
459
static void
 
460
player_clear_download_buffering (ClutterGstPlayback *self)
 
461
{
 
462
  ClutterGstPlaybackPrivate *priv = self->priv;
 
463
 
 
464
  player_configure_buffering_timeout (self, 0);
 
465
 
 
466
  priv->in_download_buffering = FALSE;
 
467
}
 
468
 
 
469
static gboolean
 
470
is_live_pipeline (GstElement *pipeline)
 
471
{
 
472
  GstState state, pending;
 
473
  GstStateChangeReturn state_change_res;
 
474
  gboolean is_live = FALSE;
 
475
 
 
476
  /* get pipeline current state, we need to change the pipeline state to PAUSED to
 
477
   * see if we are dealing with a live source and we want to restore the pipeline
 
478
   * state afterwards */
 
479
  gst_element_get_state (pipeline, &state, &pending, 0);
 
480
 
 
481
  /* a pipeline with live source should return NO_PREROLL in PAUSE */
 
482
  state_change_res = gst_element_set_state (pipeline, GST_STATE_PAUSED);
 
483
  is_live = (state_change_res == GST_STATE_CHANGE_NO_PREROLL);
 
484
 
 
485
  /* restore pipeline previous state */
 
486
  if (pending == GST_STATE_VOID_PENDING)
 
487
    gst_element_set_state (pipeline, state);
 
488
  else
 
489
    gst_element_set_state (pipeline, pending);
 
490
 
 
491
  return is_live;
 
492
}
 
493
 
 
494
static void
 
495
set_uri (ClutterGstPlayback *self,
 
496
         const gchar        *uri)
 
497
{
 
498
  ClutterGstPlaybackPrivate *priv = self->priv;
 
499
 
 
500
  CLUTTER_GST_NOTE (MEDIA, "setting uri %s", uri);
 
501
 
 
502
  if (!priv->pipeline)
 
503
    return;
 
504
 
 
505
  g_free (priv->uri);
 
506
 
 
507
  priv->in_eos = FALSE;
 
508
  priv->in_error = FALSE;
 
509
 
 
510
  if (uri)
 
511
    {
 
512
      priv->uri = g_strdup (uri);
 
513
 
 
514
      /* Ensure the tick timeout is installed.
 
515
       *
 
516
       * We also have it installed in PAUSED state, because
 
517
       * seeks etc may have a delayed effect on the position.
 
518
       */
 
519
      if (priv->tick_timeout_id == 0)
 
520
        {
 
521
          priv->tick_timeout_id =
 
522
            g_timeout_add (TICK_TIMEOUT, tick_timeout, self);
 
523
        }
 
524
 
 
525
      /* try to load subtitles based on the uri of the file */
 
526
      set_subtitle_uri (self, NULL);
 
527
 
 
528
      /* reset the states of download buffering */
 
529
      player_clear_download_buffering (self);
 
530
    }
 
531
  else
 
532
    {
 
533
      priv->uri = NULL;
 
534
 
 
535
      if (priv->tick_timeout_id)
 
536
        {
 
537
          g_source_remove (priv->tick_timeout_id);
 
538
          priv->tick_timeout_id = 0;
 
539
        }
 
540
 
 
541
      if (priv->buffering_timeout_id)
 
542
        {
 
543
          g_source_remove (priv->buffering_timeout_id);
 
544
          priv->buffering_timeout_id = 0;
 
545
        }
 
546
    }
 
547
 
 
548
  priv->can_seek = FALSE;
 
549
  priv->duration = 0.0;
 
550
  priv->stacked_progress = -1.0;
 
551
  priv->target_progress = 0.0;
 
552
 
 
553
  CLUTTER_GST_NOTE (MEDIA, "setting URI: %s", uri);
 
554
 
 
555
  if (uri)
 
556
    {
 
557
      /* Change uri, force the pipeline to NULL so the uri can be changed, then
 
558
       * set the pipeline to "unforced" mode so it will return to its current
 
559
       * target mode */
 
560
      force_pipeline_state (self, GST_STATE_NULL);
 
561
 
 
562
      g_object_set (priv->pipeline, "uri", uri, NULL);
 
563
 
 
564
      priv->is_live = is_live_pipeline (priv->pipeline);
 
565
 
 
566
      set_subtitle_uri (self, NULL);
 
567
      autoload_subtitle (self, uri);
 
568
 
 
569
      force_pipeline_state (self, GST_STATE_VOID_PENDING);
 
570
 
 
571
      priv->is_changing_uri = TRUE;
 
572
    }
 
573
  else
 
574
    {
 
575
      priv->is_idle = TRUE;
 
576
      priv->is_live = FALSE;
 
577
      set_subtitle_uri (self, NULL);
 
578
      gst_element_set_state (priv->pipeline, GST_STATE_NULL);
 
579
      g_object_notify (G_OBJECT (self), "idle");
 
580
    }
 
581
 
 
582
  /*
 
583
   * Emit notifications for all these to make sure UI is not showing
 
584
   * any properties of the old URI.
 
585
   */
 
586
  g_object_notify (G_OBJECT (self), "uri");
 
587
  g_object_notify (G_OBJECT (self), "can-seek");
 
588
  g_object_notify (G_OBJECT (self), "duration");
 
589
  g_object_notify (G_OBJECT (self), "progress");
 
590
 
 
591
  free_tags_list (&priv->audio_streams);
 
592
  CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed");
 
593
  g_object_notify (G_OBJECT (self), "audio-streams");
 
594
 
 
595
  free_tags_list (&priv->subtitle_tracks);
 
596
  CLUTTER_GST_NOTE (SUBTITLES, "subtitle-tracks changed");
 
597
  g_object_notify (G_OBJECT (self), "subtitle-tracks");
 
598
}
 
599
 
 
600
static gdouble
 
601
get_audio_volume (ClutterGstPlayback *self)
 
602
{
 
603
  ClutterGstPlaybackPrivate *priv = self->priv;
 
604
 
 
605
  if (!priv->pipeline)
 
606
    return 0.0;
 
607
 
 
608
  CLUTTER_GST_NOTE (MEDIA, "get volume: %.02f", priv->volume);
 
609
 
 
610
  return priv->volume;
 
611
}
 
612
 
 
613
static void
 
614
set_audio_volume (ClutterGstPlayback *self,
 
615
                  gdouble             volume)
 
616
{
 
617
  ClutterGstPlaybackPrivate *priv = self->priv;
 
618
 
 
619
    if (!priv->pipeline)
 
620
      return;
 
621
 
 
622
  CLUTTER_GST_NOTE (MEDIA, "set volume: %.02f", volume);
 
623
 
 
624
  volume = CLAMP (volume, 0.0, 1.0);
 
625
  gst_stream_volume_set_volume (GST_STREAM_VOLUME (priv->pipeline),
 
626
                                GST_STREAM_VOLUME_FORMAT_CUBIC,
 
627
                                volume);
 
628
  g_object_notify (G_OBJECT (self), "audio-volume");
 
629
}
 
630
 
 
631
static void
 
632
set_in_seek (ClutterGstPlayback *self,
 
633
             gboolean            seeking)
 
634
{
 
635
  ClutterGstPlaybackPrivate *priv = self->priv;
 
636
 
 
637
  priv->in_seek = seeking;
 
638
  g_object_notify (G_OBJECT (self), "in-seek");
 
639
}
 
640
 
 
641
 
 
642
static void
 
643
set_playing (ClutterGstPlayback *self,
 
644
             gboolean            playing)
 
645
{
 
646
  ClutterGstPlaybackPrivate *priv = self->priv;
 
647
 
 
648
  if (!priv->pipeline)
 
649
    return;
 
650
 
 
651
  CLUTTER_GST_NOTE (MEDIA, "set playing: %d", playing);
 
652
 
 
653
  priv->in_error = FALSE;
 
654
  priv->in_eos = FALSE;
 
655
 
 
656
  if (!priv->uri && playing)
 
657
    {
 
658
      g_warning ("Unable to start playing: no URI is set");
 
659
      return;
 
660
    }
 
661
 
 
662
  set_pipeline_target_state (self,
 
663
    playing ? GST_STATE_PLAYING : GST_STATE_PAUSED);
 
664
 
 
665
  g_object_notify (G_OBJECT (self), "playing");
 
666
  g_object_notify (G_OBJECT (self), "progress");
 
667
}
 
668
 
 
669
static gboolean
 
670
get_playing (ClutterGstPlayback *player)
 
671
{
 
672
  ClutterGstPlaybackPrivate *priv = player->priv;
 
673
  gboolean playing;
 
674
 
 
675
  if (!priv->pipeline || !priv->uri)
 
676
    return FALSE;
 
677
 
 
678
  playing = priv->target_state == GST_STATE_PLAYING;
 
679
 
 
680
  CLUTTER_GST_NOTE (MEDIA, "get playing: %d", playing);
 
681
 
 
682
  return playing;
 
683
}
 
684
 
 
685
static void
 
686
set_progress (ClutterGstPlayback *self,
 
687
              gdouble             progress)
 
688
{
 
689
  ClutterGstPlaybackPrivate *priv = self->priv;
 
690
  GstQuery *duration_q;
 
691
  gint64 position;
 
692
 
 
693
  if (!priv->pipeline)
 
694
    return;
 
695
 
 
696
  CLUTTER_GST_NOTE (MEDIA, "set progress: %.02f", progress);
 
697
 
 
698
  priv->in_eos = FALSE;
 
699
  priv->target_progress = progress;
 
700
 
 
701
  if (priv->is_changing_uri || priv->in_seek)
 
702
    {
 
703
      /* We can't seek right now, let's save the position where we
 
704
         want to seek and do that later. */
 
705
      CLUTTER_GST_NOTE (MEDIA,
 
706
                        "already seeking. stacking progress point.");
 
707
      priv->stacked_progress = progress;
 
708
      return;
 
709
    }
 
710
 
 
711
  duration_q = gst_query_new_duration (GST_FORMAT_TIME);
 
712
 
 
713
  position = 0;
 
714
  if (gst_element_query (priv->pipeline, duration_q))
 
715
    {
 
716
      gint64 duration = 0;
 
717
 
 
718
      gst_query_parse_duration (duration_q, NULL, &duration);
 
719
 
 
720
      position = progress * duration;
 
721
    }
 
722
  else if (progress != 0.0)
 
723
    {
 
724
      /* Can't seek into the file if the duration is unknown */
 
725
      goto out;
 
726
    }
 
727
 
 
728
  gst_element_seek (priv->pipeline,
 
729
                    1.0,
 
730
                    GST_FORMAT_TIME,
 
731
                    GST_SEEK_FLAG_FLUSH | priv->seek_flags,
 
732
                    GST_SEEK_TYPE_SET,
 
733
                    position,
 
734
                    GST_SEEK_TYPE_NONE, GST_CLOCK_TIME_NONE);
 
735
 
 
736
  set_in_seek (self, TRUE);
 
737
  CLUTTER_GST_NOTE (MEDIA, "set progress (seeked): %.02f", progress);
 
738
  /* If we seek we want to go and babysit the buffering again in case of
 
739
   * download buffering */
 
740
  if (!priv->is_live && clutter_gst_playback_get_buffering_mode (self) ==
 
741
      CLUTTER_GST_BUFFERING_MODE_DOWNLOAD)
 
742
    {
 
743
      force_pipeline_state (self, GST_STATE_PAUSED);
 
744
    }
 
745
  priv->stacked_progress = -1.0;
 
746
 
 
747
out:
 
748
  gst_query_unref (duration_q);
 
749
}
 
750
 
 
751
static gdouble
 
752
get_progress (ClutterGstPlayback *self)
 
753
{
 
754
  ClutterGstPlaybackPrivate *priv = self->priv;
 
755
  GstQuery *position_q, *duration_q;
 
756
  gdouble progress;
 
757
 
 
758
  if (!priv->pipeline)
 
759
    return 0.0;
 
760
 
 
761
  /* when hitting an error or after an EOS, playbin has some weird values when
 
762
   * querying the duration and progress. We default to 0.0 on error and 1.0 on
 
763
   * EOS */
 
764
  if (priv->in_error)
 
765
    {
 
766
      CLUTTER_GST_NOTE (MEDIA, "get progress (error): 0.0");
 
767
      return 0.0;
 
768
    }
 
769
 
 
770
  if (priv->in_eos)
 
771
    {
 
772
      CLUTTER_GST_NOTE (MEDIA, "get progress (eos): 1.0");
 
773
      return 1.0;
 
774
    }
 
775
 
 
776
  /* When seeking, the progress returned by playbin is 0.0. We want that to be
 
777
   * the last known position instead as returning 0.0 will have some ugly
 
778
   * effects, say on a progress bar getting updated from the progress tick. */
 
779
  if (priv->in_seek || priv->is_changing_uri)
 
780
    {
 
781
      CLUTTER_GST_NOTE (MEDIA, "get progress (target): %.02f",
 
782
                        priv->target_progress);
 
783
      return priv->target_progress;
 
784
    }
 
785
 
 
786
  position_q = gst_query_new_position (GST_FORMAT_TIME);
 
787
  duration_q = gst_query_new_duration (GST_FORMAT_TIME);
 
788
 
 
789
  if (gst_element_query (priv->pipeline, position_q) &&
 
790
      gst_element_query (priv->pipeline, duration_q))
 
791
    {
 
792
      gint64 position, duration;
 
793
 
 
794
      position = duration = 0;
 
795
 
 
796
      gst_query_parse_position (position_q, NULL, &position);
 
797
      gst_query_parse_duration (duration_q, NULL, &duration);
 
798
 
 
799
      progress = CLAMP ((gdouble) position / (gdouble) duration, 0.0, 1.0);
 
800
    }
 
801
  else
 
802
    progress = 0.0;
 
803
 
 
804
  gst_query_unref (position_q);
 
805
  gst_query_unref (duration_q);
 
806
 
 
807
  CLUTTER_GST_NOTE (MEDIA, "get progress (pipeline): %.02f", progress);
 
808
 
 
809
  return progress;
 
810
}
 
811
 
 
812
static void
 
813
set_subtitle_font_name (ClutterGstPlayback *self,
 
814
                        const gchar        *font_name)
 
815
{
 
816
  ClutterGstPlaybackPrivate *priv = self->priv;
 
817
 
 
818
  if (!priv->pipeline)
 
819
    return;
 
820
 
 
821
  CLUTTER_GST_NOTE (MEDIA, "setting subtitle font to %s", font_name);
 
822
 
 
823
  g_free (priv->font_name);
 
824
  priv->font_name = g_strdup (font_name);
 
825
  g_object_set (priv->pipeline, "subtitle-font-desc", font_name, NULL);
 
826
}
 
827
 
 
828
static gdouble
 
829
get_position (ClutterGstPlayback *self)
 
830
{
 
831
  ClutterGstPlaybackPrivate *priv = self->priv;
 
832
  gboolean success;
 
833
  GstFormat format = GST_FORMAT_TIME;
 
834
  gint64 position;
 
835
 
 
836
  success = gst_element_query_position (priv->pipeline, format, &position);
 
837
  if (G_UNLIKELY (success != TRUE))
 
838
    return 0.0;
 
839
 
 
840
  return (gdouble) position / GST_SECOND;
 
841
}
 
842
 
 
843
static gboolean
 
844
player_should_buffer (ClutterGstPlayback *self, GstQuery *query)
 
845
{
 
846
  ClutterGstPlaybackPrivate *priv = self->priv;
 
847
  gdouble position;
 
848
  gboolean ret = FALSE;
 
849
  gint64 left;
 
850
  gdouble time_left;
 
851
  gboolean busy;
 
852
 
 
853
  /* Use the estimated total duration left as estimated by queue2
 
854
   * based on the average incoming bitrate, we can stop buffering once
 
855
   * the remaining download takes less time then the remaining play
 
856
   * time (with a 10% safety margin). However regardless of that keep
 
857
   * buffering as long as queue2 indicates that buffering should
 
858
   * happen (based on its high water marks) */
 
859
  gst_query_parse_buffering_range (query, NULL, NULL, NULL, &left);
 
860
  gst_query_parse_buffering_percent (query, &busy, NULL);
 
861
 
 
862
  position = get_position (self);
 
863
  if (priv->duration)
 
864
    time_left = priv->duration - position;
 
865
  else
 
866
    time_left = 0;
 
867
 
 
868
  if (left == -1 || (!busy && (((gdouble)left * 1.1) / 1000) < time_left))
 
869
    {
 
870
      ret = FALSE;
 
871
    }
 
872
  else
 
873
    {
 
874
      ret = TRUE;
 
875
    }
 
876
 
 
877
  return ret;
 
878
}
 
879
 
 
880
static gboolean
 
881
player_buffering_timeout (gpointer data)
 
882
{
 
883
  ClutterGstPlayback *self = (ClutterGstPlayback *) data;
 
884
  ClutterGstPlaybackPrivate *priv = self->priv;
 
885
  GstQuery *query;
 
886
  gboolean res;
 
887
  gboolean busy;
 
888
  gboolean ret = TRUE;
 
889
  GstBufferingMode mode;
 
890
  gboolean should_buffer;
 
891
 
 
892
 
 
893
  /* currently seeking, wait until it's done to get consistent results */
 
894
  if (priv->in_seek)
 
895
    return TRUE;
 
896
 
 
897
  /* queue2 only knows about _PERCENT and _BYTES */
 
898
  query = gst_query_new_buffering (GST_FORMAT_BYTES);
 
899
  res = gst_element_query (priv->pipeline, query);
 
900
 
 
901
  if (res == FALSE)
 
902
    {
 
903
      CLUTTER_GST_NOTE (BUFFERING, "Buffer query failed");
 
904
      goto out;
 
905
    }
 
906
 
 
907
  gst_query_parse_buffering_stats (query, &mode, NULL, NULL, NULL);
 
908
 
 
909
  if (mode != GST_BUFFERING_DOWNLOAD)
 
910
    {
 
911
      CLUTTER_GST_NOTE (BUFFERING,
 
912
        "restoring the pipeline as we're not download buffering");
 
913
      if (!busy)
 
914
        force_pipeline_state (self, GST_STATE_VOID_PENDING);
 
915
      ret = FALSE;
 
916
      player_clear_download_buffering (self);
 
917
      goto out;
 
918
    }
 
919
 
 
920
  g_signal_emit (self, signals[SHOULD_BUFFER], 0, query, &should_buffer);
 
921
  if (should_buffer)
 
922
    {
 
923
      if (priv->buffer_fill != 0.0)
 
924
        {
 
925
          priv->buffer_fill = 0.0;
 
926
          g_object_notify (G_OBJECT (self), "buffer-fill");
 
927
        }
 
928
      /* Force to paused if we haven't yet */
 
929
      if (priv->force_state == GST_STATE_VOID_PENDING)
 
930
        {
 
931
          /* Starting buffering again */
 
932
          CLUTTER_GST_NOTE (BUFFERING,
 
933
            "pausing the pipeline for buffering: %d", busy);
 
934
          force_pipeline_state (self, GST_STATE_PAUSED);
 
935
        }
 
936
    }
 
937
  else
 
938
    {
 
939
      /* Done buffering signal and stop the timeouts */
 
940
      player_clear_download_buffering (self);
 
941
      force_pipeline_state (self, GST_STATE_VOID_PENDING);
 
942
      if (priv->buffer_fill != 1.0)
 
943
        {
 
944
          priv->buffer_fill = 1.0;
 
945
          g_object_notify (G_OBJECT (self), "buffer-fill");
 
946
        }
 
947
      ret = FALSE;
 
948
    }
 
949
out:
 
950
  gst_query_unref (query);
 
951
  return ret;
 
952
}
 
953
 
 
954
static void
 
955
bus_message_error_cb (GstBus             *bus,
 
956
                      GstMessage         *message,
 
957
                      ClutterGstPlayback *self)
 
958
{
 
959
  ClutterGstPlaybackPrivate *priv = self->priv;
 
960
  GError *error = NULL;
 
961
 
 
962
  gst_element_set_state (priv->pipeline, GST_STATE_NULL);
 
963
 
 
964
  gst_message_parse_error (message, &error, NULL);
 
965
  g_signal_emit_by_name (self, "error", error);
 
966
  g_error_free (error);
 
967
 
 
968
  priv->is_idle = TRUE;
 
969
  g_object_notify (G_OBJECT (self), "idle");
 
970
}
 
971
 
 
972
/*
 
973
 * This is what's intented in the EOS callback:
 
974
 *   - receive EOS from playbin
 
975
 *   - fire the EOS signal, the user can install a signal handler to loop the
 
976
 *     video for instance.
 
977
 *   - after having emitted the signal, check the state of the pipeline
 
978
 *   - if the pipeline has been set back to playing or pause, don't touch the
 
979
 *     idle state. This will avoid drawing a frame (or more) with the idle
 
980
 *     material when looping
 
981
 */
 
982
static void
 
983
bus_message_eos_cb (GstBus             *bus,
 
984
                    GstMessage         *message,
 
985
                    ClutterGstPlayback *self)
 
986
{
 
987
  ClutterGstPlaybackPrivate *priv = self->priv;
 
988
  GstState state, pending;
 
989
 
 
990
  priv->in_eos = TRUE;
 
991
 
 
992
  gst_element_set_state (priv->pipeline, GST_STATE_READY);
 
993
 
 
994
  g_signal_emit_by_name (self, "eos");
 
995
  g_object_notify (G_OBJECT (self), "progress");
 
996
 
 
997
  gst_element_get_state (priv->pipeline, &state, &pending, 0);
 
998
  if (pending)
 
999
    state = pending;
 
1000
 
 
1001
  if (!(state == GST_STATE_PLAYING || state == GST_STATE_PAUSED))
 
1002
    {
 
1003
      priv->is_idle = TRUE;
 
1004
      g_object_notify (G_OBJECT (self), "idle");
 
1005
    }
 
1006
}
 
1007
 
 
1008
static void
 
1009
bus_message_buffering_cb (GstBus             *bus,
 
1010
                          GstMessage         *message,
 
1011
                          ClutterGstPlayback *self)
 
1012
{
 
1013
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1014
  GstBufferingMode mode;
 
1015
  GstState current_state;
 
1016
  gint buffer_percent;
 
1017
 
 
1018
  gst_message_parse_buffering_stats (message, &mode, NULL, NULL, NULL);
 
1019
 
 
1020
  if (mode != GST_BUFFERING_DOWNLOAD)
 
1021
    priv->in_download_buffering = FALSE;
 
1022
 
 
1023
  switch (mode)
 
1024
    {
 
1025
    case GST_BUFFERING_LIVE:
 
1026
    case GST_BUFFERING_STREAM:
 
1027
      gst_message_parse_buffering (message, &buffer_percent);
 
1028
      priv->buffer_fill = CLAMP ((gdouble) buffer_percent / 100.0, 0.0, 1.0);
 
1029
 
 
1030
      CLUTTER_GST_NOTE (BUFFERING, "buffer-fill: %.02f", priv->buffer_fill);
 
1031
 
 
1032
      /* no state management needed for live pipelines */
 
1033
      if (!priv->is_live)
 
1034
        {
 
1035
          /* The playbin documentation says that we need to pause the pipeline
 
1036
           * when there's not enough data yet. We try to limit the calls to
 
1037
           * gst_element_set_state() */
 
1038
          gst_element_get_state (priv->pipeline, &current_state, NULL, 0);
 
1039
 
 
1040
          if (priv->buffer_fill < 1.0)
 
1041
            {
 
1042
              if (priv->force_state != GST_STATE_PAUSED)
 
1043
                {
 
1044
                  CLUTTER_GST_NOTE (BUFFERING, "pausing the pipeline");
 
1045
                  force_pipeline_state (self, GST_STATE_PAUSED);
 
1046
                }
 
1047
            }
 
1048
          else
 
1049
            {
 
1050
              if (priv->force_state != GST_STATE_VOID_PENDING)
 
1051
                {
 
1052
                  CLUTTER_GST_NOTE (BUFFERING, "restoring the pipeline");
 
1053
                  force_pipeline_state (self, GST_STATE_VOID_PENDING);
 
1054
                }
 
1055
            }
 
1056
        }
 
1057
 
 
1058
      g_object_notify (G_OBJECT (self), "buffer-fill");
 
1059
      break;
 
1060
 
 
1061
    case GST_BUFFERING_DOWNLOAD:
 
1062
      if (priv->in_download_buffering)
 
1063
        break;
 
1064
 
 
1065
      priv->buffer_fill = 0.0;
 
1066
      g_object_notify (G_OBJECT (self), "buffer-fill");
 
1067
      /* install the querying idle handler the first time we receive a download
 
1068
       * buffering message */
 
1069
      player_configure_buffering_timeout (self, BUFFERING_TIMEOUT);
 
1070
 
 
1071
      priv->in_download_buffering = TRUE;
 
1072
      break;
 
1073
 
 
1074
    case GST_BUFFERING_TIMESHIFT:
 
1075
    default:
 
1076
      g_warning ("Buffering mode %d not handled", mode);
 
1077
      break;
 
1078
    }
 
1079
}
 
1080
 
 
1081
static void
 
1082
on_source_changed (GstElement         *pipeline,
 
1083
                   GParamSpec         *pspec,
 
1084
                   ClutterGstPlayback *self)
 
1085
{
 
1086
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1087
 
 
1088
  player_set_user_agent (self, priv->user_agent);
 
1089
}
 
1090
 
 
1091
static void
 
1092
query_duration (ClutterGstPlayback *self)
 
1093
{
 
1094
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1095
  gboolean success;
 
1096
  gint64 duration;
 
1097
  gdouble new_duration, difference;
 
1098
 
 
1099
  success = gst_element_query_duration (priv->pipeline,
 
1100
                                        GST_FORMAT_TIME,
 
1101
                                        &duration);
 
1102
  if (G_UNLIKELY (success != TRUE))
 
1103
    return;
 
1104
 
 
1105
  new_duration = (gdouble) duration / GST_SECOND;
 
1106
 
 
1107
  /* while we store the new duration if it sligthly changes, the duration
 
1108
   * signal is sent only if the new duration is at least one second different
 
1109
   * from the old one (as the duration signal is mainly used to update the
 
1110
   * time displayed in a UI */
 
1111
  difference = ABS (priv->duration - new_duration);
 
1112
  if (difference > 1e-3)
 
1113
    {
 
1114
      CLUTTER_GST_NOTE (MEDIA, "duration: %.02f", new_duration);
 
1115
      priv->duration = new_duration;
 
1116
 
 
1117
      if (difference > 1.0)
 
1118
        g_object_notify (G_OBJECT (self), "duration");
 
1119
    }
 
1120
}
 
1121
 
 
1122
static void
 
1123
bus_message_duration_changed_cb (GstBus             *bus,
 
1124
                                 GstMessage         *message,
 
1125
                                 ClutterGstPlayback *self)
 
1126
{
 
1127
  /* GstElements send a duration-changed message on the bus to signal
 
1128
   * that the duration has changed and should be re-queried */
 
1129
  query_duration (self);
 
1130
}
 
1131
 
 
1132
static void
 
1133
bus_message_state_change_cb (GstBus             *bus,
 
1134
                             GstMessage         *message,
 
1135
                             ClutterGstPlayback *self)
 
1136
{
 
1137
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1138
  GstState old_state, new_state;
 
1139
  gpointer src;
 
1140
 
 
1141
  src = GST_MESSAGE_SRC (message);
 
1142
  if (src != priv->pipeline)
 
1143
    return;
 
1144
 
 
1145
  gst_message_parse_state_changed (message, &old_state, &new_state, NULL);
 
1146
 
 
1147
  CLUTTER_GST_NOTE (MEDIA, "state change:  %s -> %s",
 
1148
                    gst_state_to_string (old_state),
 
1149
                    gst_state_to_string (new_state));
 
1150
 
 
1151
  if (old_state == new_state)
 
1152
    return;
 
1153
 
 
1154
  if (old_state == GST_STATE_READY &&
 
1155
      new_state == GST_STATE_PAUSED)
 
1156
    {
 
1157
      GstQuery *query;
 
1158
 
 
1159
      /* Determine whether we can seek */
 
1160
      query = gst_query_new_seeking (GST_FORMAT_TIME);
 
1161
 
 
1162
      if (gst_element_query (priv->pipeline, query))
 
1163
        {
 
1164
          gboolean can_seek = FALSE;
 
1165
 
 
1166
          gst_query_parse_seeking (query, NULL, &can_seek,
 
1167
                                   NULL,
 
1168
                                   NULL);
 
1169
 
 
1170
          priv->can_seek = (can_seek == TRUE) ? TRUE : FALSE;
 
1171
        }
 
1172
      else
 
1173
        {
 
1174
          /* could not query for ability to seek by querying the
 
1175
           * pipeline; let's crudely try by using the URI
 
1176
           */
 
1177
          if (priv->uri && g_str_has_prefix (priv->uri, "http://"))
 
1178
            priv->can_seek = FALSE;
 
1179
          else
 
1180
            priv->can_seek = TRUE;
 
1181
        }
 
1182
 
 
1183
      gst_query_unref (query);
 
1184
 
 
1185
      CLUTTER_GST_NOTE (MEDIA, "can-seek: %d", priv->can_seek);
 
1186
 
 
1187
      g_object_notify (G_OBJECT (self), "can-seek");
 
1188
 
 
1189
      query_duration (self);
 
1190
 
 
1191
      priv->is_changing_uri = FALSE;
 
1192
      if (priv->stacked_progress != -1.0 && priv->can_seek)
 
1193
        {
 
1194
          set_progress (self, priv->stacked_progress);
 
1195
        }
 
1196
    }
 
1197
 
 
1198
  /* is_idle controls the drawing with the idle material */
 
1199
  if (old_state > GST_STATE_READY && new_state == GST_STATE_READY)
 
1200
    {
 
1201
      priv->is_idle = TRUE;
 
1202
      g_object_notify (G_OBJECT (self), "idle");
 
1203
    }
 
1204
  else if (new_state == GST_STATE_PLAYING)
 
1205
    {
 
1206
      priv->is_idle = FALSE;
 
1207
      g_object_notify (G_OBJECT (self), "idle");
 
1208
    }
 
1209
}
 
1210
 
 
1211
static void
 
1212
bus_message_async_done_cb (GstBus             *bus,
 
1213
                           GstMessage         *message,
 
1214
                           ClutterGstPlayback *self)
 
1215
{
 
1216
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1217
 
 
1218
  if (priv->in_seek)
 
1219
    {
 
1220
      g_object_notify (G_OBJECT (self), "progress");
 
1221
 
 
1222
      set_in_seek (self, FALSE);
 
1223
      player_configure_buffering_timeout (self, BUFFERING_TIMEOUT);
 
1224
 
 
1225
      if (priv->stacked_progress != -1.0)
 
1226
        {
 
1227
          set_progress (self, priv->stacked_progress);
 
1228
        }
 
1229
    }
 
1230
}
 
1231
 
 
1232
static gboolean
 
1233
on_volume_changed_main_context (gpointer data)
 
1234
{
 
1235
  ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data);
 
1236
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1237
  gdouble volume;
 
1238
 
 
1239
  volume =
 
1240
    gst_stream_volume_get_volume (GST_STREAM_VOLUME (priv->pipeline),
 
1241
                                  GST_STREAM_VOLUME_FORMAT_CUBIC);
 
1242
  priv->volume = volume;
 
1243
 
 
1244
  g_object_notify (G_OBJECT (self), "audio-volume");
 
1245
 
 
1246
  g_object_unref (self);
 
1247
 
 
1248
  return FALSE;
 
1249
}
 
1250
 
 
1251
/* playbin proxies the volume property change notification directly from
 
1252
 * the element having the "volume" property. This means this callback is
 
1253
 * called from the thread that runs the element, potentially different from
 
1254
 * the main thread */
 
1255
static void
 
1256
on_volume_changed (GstElement         *pipeline,
 
1257
                   GParamSpec         *pspec,
 
1258
                   ClutterGstPlayback *self)
 
1259
{
 
1260
  g_idle_add (on_volume_changed_main_context, g_object_ref (self));
 
1261
}
 
1262
 
 
1263
static GList *
 
1264
get_tags (GstElement  *pipeline,
 
1265
          const gchar *property_name,
 
1266
          const gchar *action_signal)
 
1267
{
 
1268
  GList *ret = NULL;
 
1269
  gint i, n;
 
1270
 
 
1271
  g_object_get (G_OBJECT (pipeline), property_name, &n, NULL);
 
1272
  if (n == 0)
 
1273
    return NULL;
 
1274
 
 
1275
  for (i = 0; i < n; i++)
 
1276
    {
 
1277
      GstTagList *tags = NULL;
 
1278
 
 
1279
      g_signal_emit_by_name (G_OBJECT (pipeline), action_signal, i, &tags);
 
1280
 
 
1281
      ret = g_list_prepend (ret, tags);
 
1282
    }
 
1283
 
 
1284
  return g_list_reverse (ret);
 
1285
}
 
1286
 
 
1287
static gboolean
 
1288
on_audio_changed_main_context (gpointer data)
 
1289
{
 
1290
  ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data);
 
1291
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1292
 
 
1293
  free_tags_list (&priv->audio_streams);
 
1294
  priv->audio_streams = get_tags (priv->pipeline, "n-audio", "get-audio-tags");
 
1295
 
 
1296
  CLUTTER_GST_NOTE (AUDIO_STREAM, "audio-streams changed");
 
1297
 
 
1298
  g_object_notify (G_OBJECT (self), "audio-streams");
 
1299
 
 
1300
  g_object_unref (self);
 
1301
 
 
1302
  return FALSE;
 
1303
}
 
1304
 
 
1305
/* same explanation as for notify::volume's usage of g_idle_add() */
 
1306
static void
 
1307
on_audio_changed (GstElement         *pipeline,
 
1308
                  ClutterGstPlayback *self)
 
1309
{
 
1310
  g_idle_add (on_audio_changed_main_context, g_object_ref (self));
 
1311
}
 
1312
 
 
1313
static void
 
1314
on_audio_tags_changed (GstElement         *pipeline,
 
1315
                       gint                stream,
 
1316
                       ClutterGstPlayback *self)
 
1317
{
 
1318
  gint current_stream;
 
1319
 
 
1320
  g_object_get (G_OBJECT (pipeline), "current-audio", &current_stream, NULL);
 
1321
 
 
1322
  if (current_stream != stream)
 
1323
    return;
 
1324
 
 
1325
  g_idle_add (on_audio_changed_main_context, g_object_ref (self));
 
1326
}
 
1327
 
 
1328
static gboolean
 
1329
on_current_audio_changed_main_context (gpointer data)
 
1330
{
 
1331
  ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data);
 
1332
 
 
1333
  CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream changed");
 
1334
  g_object_notify (G_OBJECT (self), "audio-stream");
 
1335
 
 
1336
  g_object_unref (self);
 
1337
 
 
1338
  return FALSE;
 
1339
}
 
1340
 
 
1341
static void
 
1342
on_current_audio_changed (GstElement         *pipeline,
 
1343
                          GParamSpec         *pspec,
 
1344
                          ClutterGstPlayback *self)
 
1345
{
 
1346
  g_idle_add (on_current_audio_changed_main_context, g_object_ref (self));
 
1347
}
 
1348
 
 
1349
static gboolean
 
1350
on_text_changed_main_context (gpointer data)
 
1351
{
 
1352
  ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data);
 
1353
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1354
 
 
1355
  free_tags_list (&priv->subtitle_tracks);
 
1356
  priv->subtitle_tracks = get_tags (priv->pipeline, "n-text", "get-text-tags");
 
1357
 
 
1358
  CLUTTER_GST_NOTE (AUDIO_STREAM, "subtitle-tracks changed");
 
1359
 
 
1360
  g_object_notify (G_OBJECT (self), "subtitle-tracks");
 
1361
 
 
1362
  g_object_unref (self);
 
1363
 
 
1364
  return FALSE;
 
1365
}
 
1366
 
 
1367
/* same explanation as for notify::volume's usage of g_idle_add() */
 
1368
static void
 
1369
on_text_changed (GstElement         *pipeline,
 
1370
                 ClutterGstPlayback *self)
 
1371
{
 
1372
  g_idle_add (on_text_changed_main_context, g_object_ref (self));
 
1373
}
 
1374
 
 
1375
static void
 
1376
on_text_tags_changed (GstElement         *pipeline,
 
1377
                      gint                stream,
 
1378
                      ClutterGstPlayback *self)
 
1379
{
 
1380
  g_idle_add (on_text_changed_main_context, g_object_ref (self));
 
1381
}
 
1382
 
 
1383
static gboolean
 
1384
on_current_text_changed_main_context (gpointer data)
 
1385
{
 
1386
  ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (data);
 
1387
 
 
1388
  CLUTTER_GST_NOTE (AUDIO_STREAM, "text stream changed");
 
1389
  g_object_notify (G_OBJECT (self), "subtitle-track");
 
1390
 
 
1391
  g_object_unref (self);
 
1392
 
 
1393
  return FALSE;
 
1394
}
 
1395
 
 
1396
static void
 
1397
on_current_text_changed (GstElement         *pipeline,
 
1398
                         GParamSpec         *pspec,
 
1399
                         ClutterGstPlayback *self)
 
1400
{
 
1401
  g_idle_add (on_current_text_changed_main_context, g_object_ref (self));
 
1402
}
 
1403
 
 
1404
/**/
 
1405
 
 
1406
static ClutterGstFrame *
 
1407
clutter_gst_playback_get_frame (ClutterGstPlayer *self)
 
1408
{
 
1409
  ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (self)->priv;
 
1410
 
 
1411
  return priv->current_frame;
 
1412
}
 
1413
 
 
1414
static GstElement *
 
1415
clutter_gst_playback_get_pipeline (ClutterGstPlayer *self)
 
1416
{
 
1417
  ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (self)->priv;
 
1418
 
 
1419
  return priv->pipeline;
 
1420
}
 
1421
 
 
1422
static ClutterGstVideoSink *
 
1423
clutter_gst_playback_get_video_sink (ClutterGstPlayer *self)
 
1424
{
 
1425
  ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (self)->priv;
 
1426
 
 
1427
  return priv->video_sink;
 
1428
}
 
1429
 
 
1430
static gboolean
 
1431
clutter_gst_playback_get_idle (ClutterGstPlayer *self)
 
1432
{
 
1433
  ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (self)->priv;
 
1434
 
 
1435
  return priv->is_idle;
 
1436
}
 
1437
 
 
1438
static gdouble
 
1439
clutter_gst_playback_get_audio_volume (ClutterGstPlayer *self)
 
1440
{
 
1441
  return get_audio_volume (CLUTTER_GST_PLAYBACK (self));
 
1442
}
 
1443
 
 
1444
static void
 
1445
clutter_gst_playback_set_audio_volume (ClutterGstPlayer *self,
 
1446
                                       gdouble           volume)
 
1447
{
 
1448
  set_audio_volume (CLUTTER_GST_PLAYBACK (self), volume);
 
1449
}
 
1450
 
 
1451
static gboolean
 
1452
clutter_gst_playback_get_playing (ClutterGstPlayer *self)
 
1453
{
 
1454
  return get_playing (CLUTTER_GST_PLAYBACK (self));
 
1455
}
 
1456
 
 
1457
static void
 
1458
clutter_gst_playback_set_playing (ClutterGstPlayer *self,
 
1459
                                  gboolean          playing)
 
1460
{
 
1461
  set_playing (CLUTTER_GST_PLAYBACK (self), playing);
 
1462
}
 
1463
 
 
1464
static void
 
1465
player_iface_init (ClutterGstPlayerIface *iface)
 
1466
{
 
1467
  iface->get_frame = clutter_gst_playback_get_frame;
 
1468
  iface->get_pipeline = clutter_gst_playback_get_pipeline;
 
1469
  iface->get_video_sink = clutter_gst_playback_get_video_sink;
 
1470
 
 
1471
  iface->get_idle = clutter_gst_playback_get_idle;
 
1472
 
 
1473
  iface->get_audio_volume = clutter_gst_playback_get_audio_volume;
 
1474
  iface->set_audio_volume = clutter_gst_playback_set_audio_volume;
 
1475
 
 
1476
  iface->get_playing = clutter_gst_playback_get_playing;
 
1477
  iface->set_playing = clutter_gst_playback_set_playing;
 
1478
}
 
1479
 
 
1480
/**/
 
1481
 
 
1482
static void
 
1483
clutter_gst_playback_get_property (GObject    *object,
 
1484
                                   guint       property_id,
 
1485
                                   GValue     *value,
 
1486
                                   GParamSpec *pspec)
 
1487
{
 
1488
  ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (object);
 
1489
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1490
  gchar *str;
 
1491
 
 
1492
  switch (property_id)
 
1493
    {
 
1494
    case PROP_URI:
 
1495
      g_value_set_string (value, priv->uri);
 
1496
      break;
 
1497
 
 
1498
    case PROP_PLAYING:
 
1499
      g_value_set_boolean (value, get_playing (self));
 
1500
      break;
 
1501
 
 
1502
    case PROP_PROGRESS:
 
1503
      g_value_set_double (value, get_progress (self));
 
1504
      break;
 
1505
 
 
1506
    case PROP_SUBTITLE_URI:
 
1507
      g_object_get (priv->pipeline, "suburi", &str, NULL);
 
1508
      g_value_take_string (value, str);
 
1509
      break;
 
1510
 
 
1511
    case PROP_SUBTITLE_FONT_NAME:
 
1512
      g_value_set_string (value, priv->font_name);
 
1513
      break;
 
1514
 
 
1515
    case PROP_AUDIO_VOLUME:
 
1516
      g_value_set_double (value, get_audio_volume (self));
 
1517
      break;
 
1518
 
 
1519
    case PROP_CAN_SEEK:
 
1520
      g_value_set_boolean (value, priv->can_seek);
 
1521
      break;
 
1522
 
 
1523
    case PROP_BUFFER_FILL:
 
1524
      g_value_set_double (value, priv->buffer_fill);
 
1525
      break;
 
1526
 
 
1527
    case PROP_DURATION:
 
1528
      g_value_set_double (value, priv->duration);
 
1529
      break;
 
1530
 
 
1531
    case PROP_IDLE:
 
1532
      g_value_set_boolean (value, priv->is_idle);
 
1533
      break;
 
1534
 
 
1535
    case PROP_USER_AGENT:
 
1536
      {
 
1537
        gchar *user_agent;
 
1538
 
 
1539
        user_agent = clutter_gst_playback_get_user_agent (self);
 
1540
        g_value_take_string (value, user_agent);
 
1541
      }
 
1542
      break;
 
1543
 
 
1544
    case PROP_SEEK_FLAGS:
 
1545
      {
 
1546
        ClutterGstSeekFlags seek_flags;
 
1547
 
 
1548
        seek_flags = clutter_gst_playback_get_seek_flags (self);
 
1549
        g_value_set_flags (value, seek_flags);
 
1550
      }
 
1551
      break;
 
1552
 
 
1553
    case PROP_AUDIO_STREAMS:
 
1554
      g_value_set_pointer (value, priv->audio_streams);
 
1555
      break;
 
1556
 
 
1557
    case PROP_AUDIO_STREAM:
 
1558
      {
 
1559
        gint index_;
 
1560
 
 
1561
        index_ = clutter_gst_playback_get_audio_stream (self);
 
1562
        g_value_set_int (value, index_);
 
1563
      }
 
1564
      break;
 
1565
 
 
1566
    case PROP_SUBTITLE_TRACKS:
 
1567
      g_value_set_pointer (value, priv->subtitle_tracks);
 
1568
      break;
 
1569
 
 
1570
    case PROP_SUBTITLE_TRACK:
 
1571
      {
 
1572
        gint index_;
 
1573
 
 
1574
        index_ = clutter_gst_playback_get_subtitle_track (self);
 
1575
        g_value_set_int (value, index_);
 
1576
      }
 
1577
      break;
 
1578
 
 
1579
    case PROP_IN_SEEK:
 
1580
      g_value_set_boolean (value, priv->in_seek);
 
1581
      break;
 
1582
 
 
1583
    default:
 
1584
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
1585
    }
 
1586
}
 
1587
 
 
1588
static void
 
1589
clutter_gst_playback_set_property (GObject      *object,
 
1590
                                   guint         property_id,
 
1591
                                   const GValue *value,
 
1592
                                   GParamSpec   *pspec)
 
1593
{
 
1594
  ClutterGstPlayback *self = CLUTTER_GST_PLAYBACK (object);
 
1595
 
 
1596
  switch (property_id)
 
1597
    {
 
1598
    case PROP_URI:
 
1599
      set_uri (self, g_value_get_string (value));
 
1600
      break;
 
1601
 
 
1602
    case PROP_PLAYING:
 
1603
      set_playing (self, g_value_get_boolean (value));
 
1604
      break;
 
1605
 
 
1606
    case PROP_PROGRESS:
 
1607
      set_progress (self, g_value_get_double (value));
 
1608
      break;
 
1609
 
 
1610
    case PROP_SUBTITLE_URI:
 
1611
      set_subtitle_uri (self, g_value_get_string (value));
 
1612
      break;
 
1613
 
 
1614
    case PROP_SUBTITLE_FONT_NAME:
 
1615
      set_subtitle_font_name (self, g_value_get_string (value));
 
1616
      break;
 
1617
 
 
1618
    case PROP_AUDIO_VOLUME:
 
1619
      set_audio_volume (self, g_value_get_double (value));
 
1620
      break;
 
1621
 
 
1622
    case PROP_USER_AGENT:
 
1623
      clutter_gst_playback_set_user_agent (self,
 
1624
                                           g_value_get_string (value));
 
1625
      break;
 
1626
 
 
1627
    case PROP_SEEK_FLAGS:
 
1628
      clutter_gst_playback_set_seek_flags (self,
 
1629
                                           g_value_get_flags (value));
 
1630
      break;
 
1631
 
 
1632
    case PROP_AUDIO_STREAM:
 
1633
      clutter_gst_playback_set_audio_stream (self,
 
1634
                                             g_value_get_int (value));
 
1635
      break;
 
1636
 
 
1637
    case PROP_SUBTITLE_TRACK:
 
1638
      clutter_gst_playback_set_subtitle_track (self,
 
1639
                                               g_value_get_int (value));
 
1640
      break;
 
1641
 
 
1642
    default:
 
1643
      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
 
1644
    }
 
1645
}
 
1646
 
 
1647
static void
 
1648
clutter_gst_playback_dispose (GObject *object)
 
1649
{
 
1650
  ClutterGstPlaybackPrivate *priv = CLUTTER_GST_PLAYBACK (object)->priv;
 
1651
  guint i;
 
1652
 
 
1653
  if (priv->tick_timeout_id)
 
1654
    {
 
1655
      g_source_remove (priv->tick_timeout_id);
 
1656
      priv->tick_timeout_id = 0;
 
1657
    }
 
1658
 
 
1659
  if (priv->buffering_timeout_id)
 
1660
    {
 
1661
      g_source_remove (priv->buffering_timeout_id);
 
1662
      priv->buffering_timeout_id = 0;
 
1663
    }
 
1664
 
 
1665
  if (priv->bus)
 
1666
    {
 
1667
      for (i = 0; i < priv->gst_bus_sigs->len; i++)
 
1668
        g_signal_handler_disconnect (priv->bus,
 
1669
                                     g_array_index (priv->gst_bus_sigs, gulong, i));
 
1670
      gst_bus_remove_signal_watch (priv->bus);
 
1671
      priv->bus = NULL;
 
1672
    }
 
1673
 
 
1674
  if (priv->pipeline)
 
1675
    {
 
1676
      for (i = 0; i < priv->gst_pipe_sigs->len; i++)
 
1677
        g_signal_handler_disconnect (priv->pipeline,
 
1678
                                     g_array_index (priv->gst_pipe_sigs, gulong, i));
 
1679
      gst_element_set_state (priv->pipeline, GST_STATE_NULL);
 
1680
      g_clear_object (&priv->pipeline);
 
1681
    }
 
1682
 
 
1683
  if (priv->current_frame)
 
1684
    {
 
1685
      g_boxed_free (CLUTTER_GST_TYPE_FRAME, priv->current_frame);
 
1686
      priv->current_frame = NULL;
 
1687
    }
 
1688
 
 
1689
  g_free (priv->uri);
 
1690
  g_free (priv->font_name);
 
1691
  g_free (priv->user_agent);
 
1692
  priv->uri = priv->font_name = priv->user_agent = NULL;
 
1693
  free_tags_list (&priv->audio_streams);
 
1694
  free_tags_list (&priv->subtitle_tracks);
 
1695
 
 
1696
  G_OBJECT_CLASS (clutter_gst_playback_parent_class)->dispose (object);
 
1697
}
 
1698
 
 
1699
static void
 
1700
clutter_gst_playback_finalize (GObject *object)
 
1701
{
 
1702
  G_OBJECT_CLASS (clutter_gst_playback_parent_class)->finalize (object);
 
1703
}
 
1704
 
 
1705
static void
 
1706
clutter_gst_playback_class_init (ClutterGstPlaybackClass *klass)
 
1707
{
 
1708
  GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
1709
  GParamSpec *pspec;
 
1710
 
 
1711
  g_type_class_add_private (klass, sizeof (ClutterGstPlaybackPrivate));
 
1712
 
 
1713
  object_class->get_property = clutter_gst_playback_get_property;
 
1714
  object_class->set_property = clutter_gst_playback_set_property;
 
1715
  object_class->dispose = clutter_gst_playback_dispose;
 
1716
  object_class->finalize = clutter_gst_playback_finalize;
 
1717
  klass->should_buffer = player_should_buffer;
 
1718
 
 
1719
  /**
 
1720
   * ClutterGstPlayback:uri:
 
1721
   *
 
1722
   * The location of a media file, expressed as a valid URI.
 
1723
   */
 
1724
  pspec = g_param_spec_string ("uri",
 
1725
                               "URI",
 
1726
                               "URI of a media file",
 
1727
                               NULL,
 
1728
                               CLUTTER_GST_PARAM_READWRITE);
 
1729
  g_object_class_install_property (object_class, PROP_URI, pspec);
 
1730
 
 
1731
  /**
 
1732
   * ClutterGstPlayback:progress:
 
1733
   *
 
1734
   * The current progress of the playback, as a normalized
 
1735
   * value between 0.0 and 1.0.
 
1736
   */
 
1737
  pspec = g_param_spec_double ("progress",
 
1738
                               "Progress",
 
1739
                               "Current progress of the playback",
 
1740
                               0.0, 1.0, 0.0,
 
1741
                               CLUTTER_GST_PARAM_READWRITE);
 
1742
  g_object_class_install_property (object_class, PROP_PROGRESS, pspec);
 
1743
 
 
1744
  /**
 
1745
   * ClutterGstPlayback:subtitle-uri:
 
1746
   *
 
1747
   * The location of a subtitle file, expressed as a valid URI.
 
1748
   */
 
1749
  pspec = g_param_spec_string ("subtitle-uri",
 
1750
                               "Subtitle URI",
 
1751
                               "URI of a subtitle file",
 
1752
                               NULL,
 
1753
                               CLUTTER_GST_PARAM_READWRITE);
 
1754
  g_object_class_install_property (object_class, PROP_SUBTITLE_URI, pspec);
 
1755
 
 
1756
  /**
 
1757
   * ClutterGstPlayback:subtitle-font-name:
 
1758
   *
 
1759
   * The font used to display subtitles. The font description has to
 
1760
   * follow the same grammar as the one recognized by
 
1761
   * pango_font_description_from_string().
 
1762
   */
 
1763
  pspec = g_param_spec_string ("subtitle-font-name",
 
1764
                               "Subtitle Font Name",
 
1765
                               "The font used to display subtitles",
 
1766
                               NULL,
 
1767
                               CLUTTER_GST_PARAM_READWRITE);
 
1768
  g_object_class_install_property (object_class, PROP_SUBTITLE_FONT_NAME, pspec);
 
1769
 
 
1770
  /**
 
1771
   * ClutterGstPlayback:can-seek:
 
1772
   *
 
1773
   * Whether the current stream is seekable.
 
1774
   */
 
1775
  pspec = g_param_spec_boolean ("can-seek",
 
1776
                                "Can Seek",
 
1777
                                "Whether the current stream is seekable",
 
1778
                                FALSE,
 
1779
                                CLUTTER_GST_PARAM_READABLE);
 
1780
  g_object_class_install_property (object_class, PROP_CAN_SEEK, pspec);
 
1781
 
 
1782
  /**
 
1783
   * ClutterGstPlayback:buffer-fill:
 
1784
   *
 
1785
   * The fill level of the buffer for the current stream,
 
1786
   * as a value between 0.0 and 1.0.
 
1787
   */
 
1788
  pspec = g_param_spec_double ("buffer-fill",
 
1789
                               "Buffer Fill",
 
1790
                               "The fill level of the buffer",
 
1791
                               0.0, 1.0, 0.0,
 
1792
                               CLUTTER_GST_PARAM_READABLE);
 
1793
  g_object_class_install_property (object_class, PROP_BUFFER_FILL, pspec);
 
1794
 
 
1795
  /**
 
1796
   * ClutterGstPlayback:duration:
 
1797
   *
 
1798
   * The duration of the current stream, in seconds
 
1799
   */
 
1800
  pspec = g_param_spec_double ("duration",
 
1801
                               "Duration",
 
1802
                               "The duration of the stream, in seconds",
 
1803
                               0, G_MAXDOUBLE, 0,
 
1804
                               CLUTTER_GST_PARAM_READABLE);
 
1805
  g_object_class_install_property (object_class, PROP_DURATION, pspec);
 
1806
 
 
1807
  /**
 
1808
   * ClutterGstPlayback:user-agent:
 
1809
   *
 
1810
   * The User Agent used by #ClutterGstPlayback with network protocols.
 
1811
   *
 
1812
   * Since: 1.4
 
1813
   */
 
1814
  pspec = g_param_spec_string ("user-agent",
 
1815
                               "User Agent",
 
1816
                               "User Agent used with network protocols",
 
1817
                               NULL,
 
1818
                               CLUTTER_GST_PARAM_READWRITE);
 
1819
  g_object_class_install_property (object_class, PROP_USER_AGENT, pspec);
 
1820
 
 
1821
  /**
 
1822
   * ClutterGstPlayback:seek-flags:
 
1823
   *
 
1824
   * Flags to use when seeking.
 
1825
   *
 
1826
   * Since: 1.4
 
1827
   */
 
1828
  pspec = g_param_spec_flags ("seek-flags",
 
1829
                              "Seek Flags",
 
1830
                              "Flags to use when seeking",
 
1831
                              CLUTTER_GST_TYPE_SEEK_FLAGS,
 
1832
                              CLUTTER_GST_SEEK_FLAG_NONE,
 
1833
                              CLUTTER_GST_PARAM_READWRITE);
 
1834
  g_object_class_install_property (object_class, PROP_SEEK_FLAGS, pspec);
 
1835
 
 
1836
  /**
 
1837
   * ClutterGstPlayback:audio-streams:
 
1838
   *
 
1839
   * List of audio streams available on the current media.
 
1840
   *
 
1841
   * Since: 1.4
 
1842
   */
 
1843
  pspec = g_param_spec_pointer ("audio-streams",
 
1844
                                "Audio Streams",
 
1845
                                "List of the audio streams of the media",
 
1846
                                CLUTTER_GST_PARAM_READABLE);
 
1847
  g_object_class_install_property (object_class, PROP_AUDIO_STREAMS, pspec);
 
1848
 
 
1849
  /**
 
1850
   * ClutterGstPlayback:audio-stream:
 
1851
   *
 
1852
   * Index of the current audio stream.
 
1853
   *
 
1854
   * Since: 1.4
 
1855
   */
 
1856
  pspec = g_param_spec_int ("audio-stream",
 
1857
                            "Audio Stream",
 
1858
                            "Index of the current audio stream",
 
1859
                            -1, G_MAXINT, -1,
 
1860
                            CLUTTER_GST_PARAM_READWRITE);
 
1861
  g_object_class_install_property (object_class, PROP_AUDIO_STREAM, pspec);
 
1862
 
 
1863
  /**
 
1864
   * ClutterGstPlayback:subtitle-tracks:
 
1865
   *
 
1866
   * List of subtitle tracks available.
 
1867
   *
 
1868
   * Since: 1.4
 
1869
   */
 
1870
  pspec = g_param_spec_pointer ("subtitle-tracks",
 
1871
                                "Subtitles Tracks",
 
1872
                                "List of the subtitles tracks of the media",
 
1873
                                CLUTTER_GST_PARAM_READABLE);
 
1874
  g_object_class_install_property (object_class, PROP_SUBTITLE_TRACKS, pspec);
 
1875
 
 
1876
  /**
 
1877
   * ClutterGstPlayback:subtitle-track:
 
1878
   *
 
1879
   * Current subtitle track being displayed.
 
1880
   *
 
1881
   * Since: 1.4
 
1882
   */
 
1883
  pspec = g_param_spec_int ("subtitle-track",
 
1884
                            "Subtitle Track",
 
1885
                            "Index of the current subtitles track",
 
1886
                            -1, G_MAXINT, -1,
 
1887
                            CLUTTER_GST_PARAM_READWRITE);
 
1888
  g_object_class_install_property (object_class, PROP_SUBTITLE_TRACK, pspec);
 
1889
 
 
1890
  /**
 
1891
   * ClutterGstPlayback:in-seek:
 
1892
   *
 
1893
   * Whether or not the stream is being seeked.
 
1894
   *
 
1895
   * Since: 1.6
 
1896
   */
 
1897
  pspec = g_param_spec_boolean ("in-seek",
 
1898
                                "In seek mode",
 
1899
                                "If currently seeking",
 
1900
                                FALSE,
 
1901
                                CLUTTER_GST_PARAM_READABLE);
 
1902
  g_object_class_install_property (object_class, PROP_IN_SEEK, pspec);
 
1903
 
 
1904
 
 
1905
  g_object_class_override_property (object_class,
 
1906
                                    PROP_IDLE, "idle");
 
1907
  g_object_class_override_property (object_class,
 
1908
                                    PROP_PLAYING, "playing");
 
1909
  g_object_class_override_property (object_class,
 
1910
                                    PROP_AUDIO_VOLUME, "audio-volume");
 
1911
 
 
1912
 
 
1913
  /* Signals */
 
1914
 
 
1915
  /**
 
1916
   * ClutterGstPlayback::should-buffer:
 
1917
   * @player: the #ClutterGstPlayback instance that received the signal
 
1918
   * @query: A gst buffering query of format bytes
 
1919
   *
 
1920
   * The ::should-buffer signal is emitted every time the base class needs to
 
1921
   * decide whether it should continue buffering in download-buffering mode.
 
1922
   *
 
1923
   * Since: 1.4
 
1924
   */
 
1925
  signals[SHOULD_BUFFER] =
 
1926
    g_signal_new ("should-buffer",
 
1927
                  CLUTTER_GST_TYPE_PLAYBACK,
 
1928
                  G_SIGNAL_RUN_LAST,
 
1929
                  G_STRUCT_OFFSET (ClutterGstPlaybackClass,
 
1930
                                   should_buffer),
 
1931
                  g_signal_accumulator_first_wins, NULL,
 
1932
                  _clutter_gst_marshal_BOOL__OBJECT,
 
1933
                  G_TYPE_BOOLEAN, 1, GST_TYPE_QUERY);
 
1934
}
 
1935
 
 
1936
static void
 
1937
_new_frame_from_pipeline (ClutterGstVideoSink *sink, ClutterGstPlayback *self)
 
1938
{
 
1939
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1940
 
 
1941
  clutter_gst_player_update_frame (CLUTTER_GST_PLAYER (self),
 
1942
                                   &priv->current_frame,
 
1943
                                   clutter_gst_video_sink_get_frame (sink));
 
1944
}
 
1945
 
 
1946
static void
 
1947
_ready_from_pipeline (ClutterGstVideoSink *sink, ClutterGstPlayback *self)
 
1948
{
 
1949
  g_signal_emit_by_name (self, "ready");
 
1950
}
 
1951
 
 
1952
static void
 
1953
_pixel_aspect_ratio_changed (ClutterGstVideoSink   *sink,
 
1954
                             GParamSpec            *spec,
 
1955
                             ClutterGstPlayback    *self)
 
1956
{
 
1957
  clutter_gst_frame_update_pixel_aspect_ratio (self->priv->current_frame, sink);
 
1958
}
 
1959
 
 
1960
static GstElement *
 
1961
get_pipeline (ClutterGstPlayback *self)
 
1962
{
 
1963
  ClutterGstPlaybackPrivate *priv = self->priv;
 
1964
  GstElement *pipeline;
 
1965
 
 
1966
  pipeline = gst_element_factory_make ("playbin", "pipeline");
 
1967
  if (!pipeline)
 
1968
    {
 
1969
      g_critical ("Unable to create playbin element");
 
1970
      return NULL;
 
1971
    }
 
1972
 
 
1973
  priv->video_sink = clutter_gst_video_sink_new ();
 
1974
 
 
1975
  g_signal_connect (priv->video_sink, "new-frame",
 
1976
                    G_CALLBACK (_new_frame_from_pipeline), self);
 
1977
  g_signal_connect (priv->video_sink, "pipeline-ready",
 
1978
                    G_CALLBACK (_ready_from_pipeline), self);
 
1979
  g_signal_connect (priv->video_sink, "notify::pixel-aspect-ratio",
 
1980
                    G_CALLBACK (_pixel_aspect_ratio_changed), self);
 
1981
 
 
1982
  g_object_set (G_OBJECT (pipeline),
 
1983
                "video-sink", priv->video_sink,
 
1984
                "subtitle-font-desc", "Sans 16",
 
1985
                NULL);
 
1986
 
 
1987
  return pipeline;
 
1988
}
 
1989
 
 
1990
#define connect_signal_custom(store, object, signal, callback, data) \
 
1991
  do {                                                               \
 
1992
    gulong s = g_signal_connect (object, signal, callback, data);    \
 
1993
    g_array_append_val (store, s);                                   \
 
1994
  } while (0)
 
1995
 
 
1996
#define connect_object_custom(store, object, signal, callback, data, flags)   \
 
1997
  do {                                                                  \
 
1998
    gulong s = g_signal_connect_object (object, signal, callback, data, flags); \
 
1999
    g_array_append_val (store, s);                                      \
 
2000
  } while (0)
 
2001
 
 
2002
static void
 
2003
clutter_gst_playback_init (ClutterGstPlayback *self)
 
2004
{
 
2005
  ClutterGstPlaybackPrivate *priv;
 
2006
 
 
2007
  self->priv = priv = GST_PLAYBACK_PRIVATE (self);
 
2008
 
 
2009
  priv->gst_pipe_sigs = g_array_new (FALSE, FALSE, sizeof (gulong));
 
2010
  priv->gst_bus_sigs = g_array_new (FALSE, FALSE, sizeof (gulong));
 
2011
 
 
2012
  priv->is_idle = TRUE;
 
2013
  priv->in_seek = FALSE;
 
2014
  priv->is_changing_uri = FALSE;
 
2015
  priv->in_download_buffering = FALSE;
 
2016
 
 
2017
  priv->pipeline = get_pipeline (self);
 
2018
  g_assert (priv->pipeline != NULL);
 
2019
 
 
2020
  priv->current_frame = clutter_gst_create_blank_frame (NULL);
 
2021
 
 
2022
  connect_signal_custom (priv->gst_pipe_sigs, priv->pipeline,
 
2023
                         "notify::source",
 
2024
                         G_CALLBACK (on_source_changed), self);
 
2025
 
 
2026
  /* We default to not playing until someone calls set_playing(TRUE) */
 
2027
  priv->target_state = GST_STATE_PAUSED;
 
2028
 
 
2029
  /* Default to a fast seek, ie. same effect than set_seek_flags (NONE); */
 
2030
  priv->seek_flags = GST_SEEK_FLAG_KEY_UNIT;
 
2031
 
 
2032
  priv->bus = gst_pipeline_get_bus (GST_PIPELINE (priv->pipeline));
 
2033
 
 
2034
  gst_bus_add_signal_watch (priv->bus);
 
2035
 
 
2036
  connect_object_custom (priv->gst_bus_sigs,
 
2037
                         priv->bus, "message::error",
 
2038
                         G_CALLBACK (bus_message_error_cb),
 
2039
                         self, 0);
 
2040
  connect_object_custom (priv->gst_bus_sigs,
 
2041
                         priv->bus, "message::eos",
 
2042
                         G_CALLBACK (bus_message_eos_cb),
 
2043
                         self, 0);
 
2044
  connect_object_custom (priv->gst_bus_sigs,
 
2045
                         priv->bus, "message::buffering",
 
2046
                         G_CALLBACK (bus_message_buffering_cb),
 
2047
                         self, 0);
 
2048
  connect_object_custom (priv->gst_bus_sigs,
 
2049
                         priv->bus, "message::duration-changed",
 
2050
                         G_CALLBACK (bus_message_duration_changed_cb),
 
2051
                         self, 0);
 
2052
  connect_object_custom (priv->gst_bus_sigs,
 
2053
                         priv->bus, "message::state-changed",
 
2054
                         G_CALLBACK (bus_message_state_change_cb),
 
2055
                         self, 0);
 
2056
  connect_object_custom (priv->gst_bus_sigs,
 
2057
                         priv->bus, "message::async-done",
 
2058
                         G_CALLBACK (bus_message_async_done_cb),
 
2059
                         self, 0);
 
2060
 
 
2061
 
 
2062
  connect_signal_custom (priv->gst_pipe_sigs,
 
2063
                         priv->pipeline, "notify::volume",
 
2064
                         G_CALLBACK (on_volume_changed),
 
2065
                         self);
 
2066
 
 
2067
  connect_signal_custom (priv->gst_pipe_sigs,
 
2068
                         priv->pipeline, "audio-changed",
 
2069
                         G_CALLBACK (on_audio_changed),
 
2070
                         self);
 
2071
  connect_signal_custom (priv->gst_pipe_sigs,
 
2072
                         priv->pipeline, "audio-tags-changed",
 
2073
                         G_CALLBACK (on_audio_tags_changed),
 
2074
                         self);
 
2075
  connect_signal_custom (priv->gst_pipe_sigs,
 
2076
                         priv->pipeline, "notify::current-audio",
 
2077
                         G_CALLBACK (on_current_audio_changed),
 
2078
                         self);
 
2079
 
 
2080
  connect_signal_custom (priv->gst_pipe_sigs,
 
2081
                         priv->pipeline, "text-changed",
 
2082
                         G_CALLBACK (on_text_changed),
 
2083
                         self);
 
2084
  connect_signal_custom (priv->gst_pipe_sigs,
 
2085
                         priv->pipeline, "text-tags-changed",
 
2086
                         G_CALLBACK (on_text_tags_changed),
 
2087
                         self);
 
2088
  connect_signal_custom (priv->gst_pipe_sigs,
 
2089
                         priv->pipeline, "notify::current-text",
 
2090
                         G_CALLBACK (on_current_text_changed),
 
2091
                         self);
 
2092
 
 
2093
#if defined(CLUTTER_WINDOWING_X11) && defined (HAVE_HW_DECODER_SUPPORT)
 
2094
  if (clutter_check_windowing_backend (CLUTTER_WINDOWING_X11))
 
2095
    gst_bus_set_sync_handler (priv->bus, on_sync_message,
 
2096
        clutter_x11_get_default_display (), NULL);
 
2097
#endif
 
2098
 
 
2099
  gst_object_unref (GST_OBJECT (priv->bus));
 
2100
}
 
2101
 
 
2102
ClutterGstPlayback *
 
2103
clutter_gst_playback_new (void)
 
2104
{
 
2105
  return g_object_new (CLUTTER_GST_TYPE_PLAYBACK, NULL);
 
2106
}
 
2107
 
 
2108
/**
 
2109
 * clutter_gst_playback_set_uri:
 
2110
 * @self: a #ClutterGstPlayback
 
2111
 * @uri: the URI of the media stream
 
2112
 *
 
2113
 * Sets the URI of @self to @uri.
 
2114
 */
 
2115
void
 
2116
clutter_gst_playback_set_uri (ClutterGstPlayback *self,
 
2117
                              const gchar        *uri)
 
2118
{
 
2119
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2120
 
 
2121
  g_object_set (G_OBJECT (self), "uri", uri, NULL);
 
2122
}
 
2123
 
 
2124
/**
 
2125
 * clutter_gst_playback_get_uri:
 
2126
 * @self: a #ClutterGstPlayback
 
2127
 *
 
2128
 * Retrieves the URI from @self.
 
2129
 *
 
2130
 * Return value: the URI of the media stream. Use g_free()
 
2131
 *   to free the returned string
 
2132
 */
 
2133
gchar *
 
2134
clutter_gst_playback_get_uri (ClutterGstPlayback *self)
 
2135
{
 
2136
  gchar *retval = NULL;
 
2137
 
 
2138
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL);
 
2139
 
 
2140
  g_object_get (G_OBJECT (self), "uri", &retval, NULL);
 
2141
 
 
2142
  return retval;
 
2143
}
 
2144
 
 
2145
/**
 
2146
 * clutter_gst_playback_set_filename:
 
2147
 * @self: a #ClutterGstPlayback
 
2148
 * @filename: A filename
 
2149
 *
 
2150
 * Sets the source of @self using a file path.
 
2151
 */
 
2152
void
 
2153
clutter_gst_playback_set_filename (ClutterGstPlayback *self,
 
2154
                                 const gchar      *filename)
 
2155
{
 
2156
  gchar *uri;
 
2157
  GError *uri_error = NULL;
 
2158
 
 
2159
  if (!g_path_is_absolute (filename))
 
2160
    {
 
2161
      gchar *abs_path;
 
2162
 
 
2163
      abs_path = g_build_filename (g_get_current_dir (), filename, NULL);
 
2164
      uri = g_filename_to_uri (abs_path, NULL, &uri_error);
 
2165
      g_free (abs_path);
 
2166
    }
 
2167
  else
 
2168
    uri = g_filename_to_uri (filename, NULL, &uri_error);
 
2169
 
 
2170
  if (uri_error)
 
2171
    {
 
2172
      g_signal_emit_by_name (self, "error", uri_error);
 
2173
      g_error_free (uri_error);
 
2174
      return;
 
2175
    }
 
2176
 
 
2177
  clutter_gst_playback_set_uri (self, uri);
 
2178
 
 
2179
  g_free (uri);
 
2180
}
 
2181
 
 
2182
/**
 
2183
 * clutter_gst_playback_get_user_agent:
 
2184
 * @self: a #ClutterGstPlayback
 
2185
 *
 
2186
 * Retrieves the user agent used when streaming.
 
2187
 *
 
2188
 * Return value: the user agent used. The returned string has to be freed with
 
2189
 * g_free()
 
2190
 *
 
2191
 * Since: 1.4
 
2192
 */
 
2193
gchar *
 
2194
clutter_gst_playback_get_user_agent (ClutterGstPlayback *self)
 
2195
{
 
2196
  ClutterGstPlaybackPrivate *priv;
 
2197
  GstElement *source;
 
2198
  GParamSpec *pspec;
 
2199
  gchar *user_agent;
 
2200
 
 
2201
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL);
 
2202
 
 
2203
  priv = self->priv;
 
2204
 
 
2205
  /* If the user has set a custom user agent, we just return it even if it is
 
2206
   * not used by the current source element of the pipeline */
 
2207
  if (priv->user_agent)
 
2208
    return g_strdup (priv->user_agent);
 
2209
 
 
2210
  /* If not, we try to retrieve the user agent used by the current source */
 
2211
  g_object_get (priv->pipeline, "source", &source, NULL);
 
2212
  if (source == NULL)
 
2213
    return NULL;
 
2214
 
 
2215
  pspec = g_object_class_find_property (G_OBJECT_GET_CLASS (source),
 
2216
                                        "user-agent");
 
2217
  if (pspec == NULL)
 
2218
    return NULL;
 
2219
 
 
2220
  g_object_get (source, "user-agent", &user_agent, NULL);
 
2221
 
 
2222
  return user_agent;
 
2223
}
 
2224
 
 
2225
/**
 
2226
 * clutter_gst_playback_set_user_agent:
 
2227
 * @self: a #ClutterGstPlayback
 
2228
 * @user_agent: the user agent
 
2229
 *
 
2230
 * Sets the user agent to use when streaming.
 
2231
 *
 
2232
 * When streaming content, you might want to set a custom user agent, eg. to
 
2233
 * promote your software, make it appear in statistics or because the server
 
2234
 * requires a special user agent you want to impersonate.
 
2235
 *
 
2236
 * Since: 1.4
 
2237
 */
 
2238
void
 
2239
clutter_gst_playback_set_user_agent (ClutterGstPlayback *self,
 
2240
                                     const gchar        *user_agent)
 
2241
{
 
2242
  ClutterGstPlaybackPrivate *priv;
 
2243
 
 
2244
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2245
 
 
2246
  priv = self->priv;
 
2247
 
 
2248
  g_free (priv->user_agent);
 
2249
  if (user_agent)
 
2250
    priv->user_agent = g_strdup (user_agent);
 
2251
  else
 
2252
    priv->user_agent = NULL;
 
2253
 
 
2254
  player_set_user_agent (self, user_agent);
 
2255
}
 
2256
 
 
2257
/**
 
2258
 * clutter_gst_playback_get_seek_flags:
 
2259
 * @self: a #ClutterGstPlayback
 
2260
 *
 
2261
 * Get the current value of the seek-flags property.
 
2262
 *
 
2263
 * Return value: a combination of #ClutterGstSeekFlags
 
2264
 *
 
2265
 * Since: 1.4
 
2266
 */
 
2267
ClutterGstSeekFlags
 
2268
clutter_gst_playback_get_seek_flags (ClutterGstPlayback *self)
 
2269
{
 
2270
  ClutterGstPlaybackPrivate *priv;
 
2271
 
 
2272
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self),
 
2273
                        CLUTTER_GST_SEEK_FLAG_NONE);
 
2274
 
 
2275
  priv = self->priv;
 
2276
 
 
2277
  if (priv->seek_flags == GST_SEEK_FLAG_ACCURATE)
 
2278
    return CLUTTER_GST_SEEK_FLAG_ACCURATE;
 
2279
  else
 
2280
    return CLUTTER_GST_SEEK_FLAG_NONE;
 
2281
}
 
2282
 
 
2283
/**
 
2284
 * clutter_gst_playback_set_seek_flags:
 
2285
 * @self: a #ClutterGstPlayback
 
2286
 * @flags: a combination of #ClutterGstSeekFlags
 
2287
 *
 
2288
 * Seeking can be done with several trade-offs. Clutter-gst defaults
 
2289
 * to %CLUTTER_GST_SEEK_FLAG_NONE.
 
2290
 *
 
2291
 * Since: 1.4
 
2292
 */
 
2293
void
 
2294
clutter_gst_playback_set_seek_flags (ClutterGstPlayback  *self,
 
2295
                                     ClutterGstSeekFlags  flags)
 
2296
{
 
2297
  ClutterGstPlaybackPrivate *priv;
 
2298
 
 
2299
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2300
 
 
2301
  priv = self->priv;
 
2302
 
 
2303
  if (flags == CLUTTER_GST_SEEK_FLAG_NONE)
 
2304
    priv->seek_flags = GST_SEEK_FLAG_KEY_UNIT;
 
2305
  else if (flags & CLUTTER_GST_SEEK_FLAG_ACCURATE)
 
2306
    priv->seek_flags = GST_SEEK_FLAG_ACCURATE;
 
2307
}
 
2308
 
 
2309
/**
 
2310
 * clutter_gst_playback_get_buffering_mode:
 
2311
 * @self: a #ClutterGstPlayback
 
2312
 *
 
2313
 * Return value: a #ClutterGstBufferingMode
 
2314
 *
 
2315
 * Since: 1.4
 
2316
 */
 
2317
ClutterGstBufferingMode
 
2318
clutter_gst_playback_get_buffering_mode (ClutterGstPlayback *self)
 
2319
{
 
2320
  ClutterGstPlaybackPrivate *priv;
 
2321
  GstPlayFlags flags;
 
2322
 
 
2323
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self),
 
2324
                        CLUTTER_GST_BUFFERING_MODE_STREAM);
 
2325
 
 
2326
  priv = self->priv;
 
2327
 
 
2328
  g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL);
 
2329
 
 
2330
  if (flags & GST_PLAY_FLAG_DOWNLOAD)
 
2331
    return CLUTTER_GST_BUFFERING_MODE_DOWNLOAD;
 
2332
 
 
2333
  return CLUTTER_GST_BUFFERING_MODE_STREAM;
 
2334
}
 
2335
 
 
2336
/**
 
2337
 * clutter_gst_playback_set_buffering_mode:
 
2338
 * @self: a #ClutterGstPlayback
 
2339
 * @mode: a #ClutterGstBufferingMode
 
2340
 *
 
2341
 * Since: 1.4
 
2342
 */
 
2343
void
 
2344
clutter_gst_playback_set_buffering_mode (ClutterGstPlayback      *self,
 
2345
                                         ClutterGstBufferingMode  mode)
 
2346
{
 
2347
  ClutterGstPlaybackPrivate *priv;
 
2348
  GstPlayFlags flags;
 
2349
 
 
2350
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2351
 
 
2352
  priv = self->priv;
 
2353
 
 
2354
  g_object_get (G_OBJECT (priv->pipeline), "flags", &flags, NULL);
 
2355
 
 
2356
  switch (mode)
 
2357
    {
 
2358
    case CLUTTER_GST_BUFFERING_MODE_STREAM:
 
2359
      flags &= ~GST_PLAY_FLAG_DOWNLOAD;
 
2360
      break;
 
2361
 
 
2362
    case CLUTTER_GST_BUFFERING_MODE_DOWNLOAD:
 
2363
      flags |= GST_PLAY_FLAG_DOWNLOAD;
 
2364
      break;
 
2365
 
 
2366
    default:
 
2367
      g_warning ("Unexpected buffering mode %d", mode);
 
2368
      break;
 
2369
    }
 
2370
 
 
2371
  g_object_set (G_OBJECT (priv->pipeline), "flags", flags, NULL);
 
2372
}
 
2373
 
 
2374
/**
 
2375
 * clutter_gst_playback_get_buffer_fill:
 
2376
 * @self: a #ClutterGstPlayback
 
2377
 *
 
2378
 * Retrieves the amount of the stream that is buffered.
 
2379
 *
 
2380
 * Return value: the fill level, between 0.0 and 1.0
 
2381
 */
 
2382
gdouble
 
2383
clutter_gst_playback_get_buffer_fill (ClutterGstPlayback *self)
 
2384
{
 
2385
  gdouble retval = 0.0;
 
2386
 
 
2387
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0);
 
2388
 
 
2389
  g_object_get (G_OBJECT (self), "buffer-fill", &retval, NULL);
 
2390
 
 
2391
  return retval;
 
2392
}
 
2393
 
 
2394
/**
 
2395
 * clutter_gst_playback_get_buffer_size:
 
2396
 * @self: a #ClutterGstPlayback
 
2397
 *
 
2398
 * Retrieves the buffer size when buffering network streams.
 
2399
 *
 
2400
 * Return value: The buffer size
 
2401
 */
 
2402
gint
 
2403
clutter_gst_playback_get_buffer_size (ClutterGstPlayback *self)
 
2404
{
 
2405
  ClutterGstPlaybackPrivate *priv;
 
2406
  gint size;
 
2407
 
 
2408
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0);
 
2409
 
 
2410
  priv = self->priv;
 
2411
 
 
2412
  g_object_get (G_OBJECT (priv->pipeline), "buffer-size", &size, NULL);
 
2413
 
 
2414
  return size;
 
2415
}
 
2416
 
 
2417
/**
 
2418
 * clutter_gst_playback_set_buffer_size:
 
2419
 * @self: a #ClutterGstPlayback
 
2420
 * @size: The new size
 
2421
 *
 
2422
 * Sets the buffer size to be used when buffering network streams.
 
2423
 */
 
2424
void
 
2425
clutter_gst_playback_set_buffer_size (ClutterGstPlayback *self,
 
2426
                                      gint                size)
 
2427
{
 
2428
  ClutterGstPlaybackPrivate *priv;
 
2429
 
 
2430
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2431
 
 
2432
  priv = self->priv;
 
2433
 
 
2434
  g_object_set (G_OBJECT (priv->pipeline), "buffer-size", size, NULL);
 
2435
}
 
2436
 
 
2437
/**
 
2438
 * clutter_gst_playback_get_buffer_duration:
 
2439
 * @self: a #ClutterGstPlayback
 
2440
 *
 
2441
 * Retrieves the buffer duration when buffering network streams.
 
2442
 *
 
2443
 * Return value: The buffer duration
 
2444
 */
 
2445
gint64
 
2446
clutter_gst_playback_get_buffer_duration (ClutterGstPlayback *self)
 
2447
{
 
2448
  ClutterGstPlaybackPrivate *priv;
 
2449
  gint64 duration;
 
2450
 
 
2451
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0);
 
2452
 
 
2453
  priv = self->priv;
 
2454
 
 
2455
  g_object_get (G_OBJECT (priv->pipeline), "buffer-duration", &duration, NULL);
 
2456
 
 
2457
  return duration;
 
2458
}
 
2459
 
 
2460
/**
 
2461
 * clutter_gst_playback_set_buffer_duration:
 
2462
 * @self: a #ClutterGstPlayback
 
2463
 * @duration: The new duration
 
2464
 *
 
2465
 * Sets the buffer duration to be used when buffering network streams.
 
2466
 */
 
2467
void
 
2468
clutter_gst_playback_set_buffer_duration (ClutterGstPlayback *self,
 
2469
                                          gint64              duration)
 
2470
{
 
2471
  ClutterGstPlaybackPrivate *priv;
 
2472
 
 
2473
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2474
 
 
2475
  priv = self->priv;
 
2476
 
 
2477
  g_object_set (G_OBJECT (priv->pipeline), "buffer-duration", duration, NULL);
 
2478
}
 
2479
 
 
2480
/**
 
2481
 * clutter_gst_playback_get_audio_streams:
 
2482
 * @self: a #ClutterGstPlayback
 
2483
 *
 
2484
 * Get the list of audio streams of the current media.
 
2485
 *
 
2486
 * Return value: (transfer none) (element-type utf8): a list of
 
2487
 * strings describing the available audio streams
 
2488
 *
 
2489
 * Since: 1.4
 
2490
 */
 
2491
GList *
 
2492
clutter_gst_playback_get_audio_streams (ClutterGstPlayback *self)
 
2493
{
 
2494
  ClutterGstPlaybackPrivate *priv;
 
2495
 
 
2496
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL);
 
2497
 
 
2498
  priv = self->priv;
 
2499
 
 
2500
  if (CLUTTER_GST_DEBUG_ENABLED (AUDIO_STREAM))
 
2501
    {
 
2502
      gchar *streams;
 
2503
 
 
2504
      streams = list_to_string (priv->audio_streams);
 
2505
      CLUTTER_GST_NOTE (AUDIO_STREAM, "audio streams: %s", streams);
 
2506
      g_free (streams);
 
2507
    }
 
2508
 
 
2509
  return priv->audio_streams;
 
2510
}
 
2511
 
 
2512
/**
 
2513
 * clutter_gst_playback_get_audio_stream:
 
2514
 * @self: a #ClutterGstPlayback
 
2515
 *
 
2516
 * Get the current audio stream. The number returned in the index of the
 
2517
 * audio stream playing in the list returned by
 
2518
 * clutter_gst_playback_get_audio_streams().
 
2519
 *
 
2520
 * Return value: the index of the current audio stream, -1 if the media has no
 
2521
 * audio stream
 
2522
 *
 
2523
 * Since: 1.4
 
2524
 */
 
2525
gint
 
2526
clutter_gst_playback_get_audio_stream (ClutterGstPlayback *self)
 
2527
{
 
2528
  ClutterGstPlaybackPrivate *priv;
 
2529
  gint index_ = -1;
 
2530
 
 
2531
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), -1);
 
2532
 
 
2533
  priv = self->priv;
 
2534
 
 
2535
  g_object_get (G_OBJECT (priv->pipeline),
 
2536
                "current-audio", &index_,
 
2537
                NULL);
 
2538
 
 
2539
  CLUTTER_GST_NOTE (AUDIO_STREAM, "audio stream is #%d", index_);
 
2540
 
 
2541
  return index_;
 
2542
}
 
2543
 
 
2544
/**
 
2545
 * clutter_gst_playback_set_audio_stream:
 
2546
 * @self: a #ClutterGstPlayback
 
2547
 * @index_: the index of the audio stream
 
2548
 *
 
2549
 * Set the audio stream to play. @index_ is the index of the stream
 
2550
 * in the list returned by clutter_gst_playback_get_audio_streams().
 
2551
 *
 
2552
 * Since: 1.4
 
2553
 */
 
2554
void
 
2555
clutter_gst_playback_set_audio_stream (ClutterGstPlayback *self,
 
2556
                                       gint                index_)
 
2557
{
 
2558
  ClutterGstPlaybackPrivate *priv;
 
2559
 
 
2560
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2561
 
 
2562
  priv = self->priv;
 
2563
 
 
2564
  g_return_if_fail (index_ >= 0 &&
 
2565
                    index_ < (gint) g_list_length (priv->audio_streams));
 
2566
 
 
2567
  CLUTTER_GST_NOTE (AUDIO_STREAM, "set audio audio stream to #%d", index_);
 
2568
 
 
2569
  g_object_set (G_OBJECT (priv->pipeline),
 
2570
                "current-audio", index_,
 
2571
                NULL);
 
2572
}
 
2573
 
 
2574
/**
 
2575
 * clutter_gst_playback_set_subtitle_uri:
 
2576
 * @self: a #ClutterGstPlayback
 
2577
 * @uri: the URI of a subtitle file
 
2578
 *
 
2579
 * Sets the location of a subtitle file to display while playing @self.
 
2580
 */
 
2581
void
 
2582
clutter_gst_playback_set_subtitle_uri (ClutterGstPlayback *self,
 
2583
                                       const char         *uri)
 
2584
{
 
2585
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2586
 
 
2587
  g_object_set (G_OBJECT (self), "subtitle-uri", uri, NULL);
 
2588
}
 
2589
 
 
2590
/**
 
2591
 * clutter_gst_playback_get_subtitle_uri:
 
2592
 * @self: a #ClutterGstPlayback
 
2593
 *
 
2594
 * Retrieves the URI of the subtitle file in use.
 
2595
 *
 
2596
 * Return value: the URI of the subtitle file. Use g_free()
 
2597
 *   to free the returned string
 
2598
 */
 
2599
gchar *
 
2600
clutter_gst_playback_get_subtitle_uri (ClutterGstPlayback *self)
 
2601
{
 
2602
  gchar *retval = NULL;
 
2603
 
 
2604
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL);
 
2605
 
 
2606
  g_object_get (G_OBJECT (self), "subtitle-uri", &retval, NULL);
 
2607
 
 
2608
  return retval;
 
2609
}
 
2610
 
 
2611
/**
 
2612
 * clutter_gst_playback_set_subtitle_font_name:
 
2613
 * @self: a #ClutterGstPlayback
 
2614
 * @font_name: a font name, or %NULL to set the default font name
 
2615
 *
 
2616
 * Sets the font used by the subtitle renderer. The @font_name string must be
 
2617
 * either %NULL, which means that the default font name of the underlying
 
2618
 * implementation will be used; or must follow the grammar recognized by
 
2619
 * pango_font_description_from_string() like:
 
2620
 *
 
2621
 * |[
 
2622
 *   clutter_gst_playback_set_subtitle_font_name (player, "Sans 24pt");
 
2623
 * ]|
 
2624
 */
 
2625
void
 
2626
clutter_gst_playback_set_subtitle_font_name (ClutterGstPlayback *self,
 
2627
                                             const char         *font_name)
 
2628
{
 
2629
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2630
 
 
2631
  g_object_set (G_OBJECT (self), "subtitle-font-name", font_name, NULL);
 
2632
}
 
2633
 
 
2634
/**
 
2635
 * clutter_gst_playback_get_subtitle_font_name:
 
2636
 * @self: a #ClutterGstPlayback
 
2637
 *
 
2638
 * Retrieves the font name currently used.
 
2639
 *
 
2640
 * Return value: a string containing the font name. Use g_free()
 
2641
 *   to free the returned string
 
2642
 */
 
2643
gchar *
 
2644
clutter_gst_playback_get_subtitle_font_name (ClutterGstPlayback *self)
 
2645
{
 
2646
  gchar *retval = NULL;
 
2647
 
 
2648
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL);
 
2649
 
 
2650
  g_object_get (G_OBJECT (self), "subtitle-font-name", &retval, NULL);
 
2651
 
 
2652
  return retval;
 
2653
}
 
2654
 
 
2655
/**
 
2656
 * clutter_gst_playback_get_subtitle_tracks:
 
2657
 * @self: a #ClutterGstPlayback
 
2658
 *
 
2659
 * Get the list of subtitles tracks of the current media.
 
2660
 *
 
2661
 * Return value: (transfer none) (element-type utf8): a list of
 
2662
 * strings describing the available subtitles tracks
 
2663
 *
 
2664
 * Since: 1.4
 
2665
 */
 
2666
GList *
 
2667
clutter_gst_playback_get_subtitle_tracks (ClutterGstPlayback *self)
 
2668
{
 
2669
  ClutterGstPlaybackPrivate *priv;
 
2670
 
 
2671
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), NULL);
 
2672
 
 
2673
  priv = self->priv;
 
2674
 
 
2675
  if (CLUTTER_GST_DEBUG_ENABLED (SUBTITLES))
 
2676
    {
 
2677
      gchar *tracks;
 
2678
 
 
2679
      tracks = list_to_string (priv->subtitle_tracks);
 
2680
      CLUTTER_GST_NOTE (SUBTITLES, "subtitle tracks: %s", tracks);
 
2681
      g_free (tracks);
 
2682
    }
 
2683
 
 
2684
  return priv->subtitle_tracks;
 
2685
}
 
2686
 
 
2687
/**
 
2688
 * clutter_gst_playback_get_subtitle_track:
 
2689
 * @self: a #ClutterGstPlayback
 
2690
 *
 
2691
 * Get the current subtitles track. The number returned is the index of the
 
2692
 * subtiles track in the list returned by
 
2693
 * clutter_gst_playback_get_subtitle_tracks().
 
2694
 *
 
2695
 * Return value: the index of the current subtitlest track, -1 if the media has
 
2696
 * no subtitles track or if the subtitles have been turned off
 
2697
 *
 
2698
 * Since: 1.4
 
2699
 */
 
2700
gint
 
2701
clutter_gst_playback_get_subtitle_track (ClutterGstPlayback *self)
 
2702
{
 
2703
  ClutterGstPlaybackPrivate *priv;
 
2704
  gint index_ = -1;
 
2705
 
 
2706
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), -1);
 
2707
 
 
2708
  priv = self->priv;
 
2709
 
 
2710
  g_object_get (G_OBJECT (priv->pipeline),
 
2711
                "current-text", &index_,
 
2712
                NULL);
 
2713
 
 
2714
  CLUTTER_GST_NOTE (SUBTITLES, "text track is #%d", index_);
 
2715
 
 
2716
  return index_;
 
2717
}
 
2718
 
 
2719
/**
 
2720
 * clutter_gst_playback_set_subtitle_track:
 
2721
 * @self: a #ClutterGstPlayback
 
2722
 * @index_: the index of the subtitles track
 
2723
 *
 
2724
 * Set the subtitles track to play. @index_ is the index of the stream
 
2725
 * in the list returned by clutter_gst_playback_get_subtitle_tracks().
 
2726
 *
 
2727
 * If @index_ is -1, the subtitles are turned off.
 
2728
 *
 
2729
 * Since: 1.4
 
2730
 */
 
2731
void
 
2732
clutter_gst_playback_set_subtitle_track (ClutterGstPlayback *self,
 
2733
                                         gint                index_)
 
2734
{
 
2735
  ClutterGstPlaybackPrivate *priv;
 
2736
  GstPlayFlags flags;
 
2737
 
 
2738
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2739
 
 
2740
  priv = self->priv;
 
2741
 
 
2742
  g_return_if_fail (index_ >= -1 &&
 
2743
                    index_ < (gint) g_list_length (priv->subtitle_tracks));
 
2744
 
 
2745
  CLUTTER_GST_NOTE (SUBTITLES, "set subtitle track to #%d", index_);
 
2746
 
 
2747
  g_object_get (priv->pipeline, "flags", &flags, NULL);
 
2748
  flags &= ~GST_PLAY_FLAG_TEXT;
 
2749
  g_object_set (priv->pipeline, "flags", flags, NULL);
 
2750
 
 
2751
  if (index_ >= 0)
 
2752
    {
 
2753
      g_object_set (G_OBJECT (priv->pipeline),
 
2754
                    "current-text", index_,
 
2755
                    NULL);
 
2756
 
 
2757
      flags |= GST_PLAY_FLAG_TEXT;
 
2758
      g_object_set (priv->pipeline, "flags", flags, NULL);
 
2759
    }
 
2760
}
 
2761
 
 
2762
/**
 
2763
 * clutter_gst_playback_get_in_seek:
 
2764
 * @self: a #ClutterGstPlayback
 
2765
 *
 
2766
 * Whether the player is seeking.
 
2767
 *
 
2768
 * Return value: TRUE if the player is seeking, FALSE otherwise.
 
2769
 *
 
2770
 * Since: 1.6
 
2771
 */
 
2772
gboolean
 
2773
clutter_gst_playback_get_in_seek (ClutterGstPlayback *self)
 
2774
{
 
2775
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), FALSE);
 
2776
 
 
2777
  return self->priv->in_seek;
 
2778
}
 
2779
 
 
2780
/**
 
2781
 * clutter_gst_playback_get_can_seek:
 
2782
 * @self: a #ClutterGstPlayback
 
2783
 *
 
2784
 * Retrieves whether @self is seekable or not.
 
2785
 *
 
2786
 * Return value: %TRUE if @self can seek, %FALSE otherwise.
 
2787
 */
 
2788
gboolean
 
2789
clutter_gst_playback_get_can_seek (ClutterGstPlayback *self)
 
2790
{
 
2791
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), FALSE);
 
2792
 
 
2793
  return self->priv->can_seek;
 
2794
}
 
2795
 
 
2796
/**
 
2797
 * clutter_gst_playback_set_progress:
 
2798
 * @self: a #ClutterGstPlayback
 
2799
 * @progress: the progress of the playback, between 0.0 and 1.0
 
2800
 *
 
2801
 * Sets the playback progress of @self. The @progress is
 
2802
 * a normalized value between 0.0 (begin) and 1.0 (end).
 
2803
 */
 
2804
void
 
2805
clutter_gst_playback_set_progress (ClutterGstPlayback *self,
 
2806
                                   gdouble             progress)
 
2807
{
 
2808
  g_return_if_fail (CLUTTER_GST_IS_PLAYBACK (self));
 
2809
 
 
2810
  set_progress (self, progress);
 
2811
}
 
2812
 
 
2813
/**
 
2814
 * clutter_gst_playback_get_progress:
 
2815
 * @self: a #ClutterGstPlayback
 
2816
 *
 
2817
 * Retrieves the playback progress of @self.
 
2818
 *
 
2819
 * Return value: the playback progress, between 0.0 and 1.0
 
2820
 */
 
2821
gdouble
 
2822
clutter_gst_playback_get_progress (ClutterGstPlayback *self)
 
2823
{
 
2824
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0);
 
2825
 
 
2826
  return get_progress (self);
 
2827
}
 
2828
/**
 
2829
 * clutter_gst_playback_get_position:
 
2830
 * @self: a #ClutterGstPlayback
 
2831
 *
 
2832
 * Retrieves the position in the media stream that @self represents.
 
2833
 *
 
2834
 * Return value: the position in the media stream, in seconds
 
2835
 */
 
2836
gdouble
 
2837
clutter_gst_playback_get_position (ClutterGstPlayback *self)
 
2838
{
 
2839
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0);
 
2840
 
 
2841
  return get_position (self);
 
2842
}
 
2843
 
 
2844
/**
 
2845
 * clutter_gst_playback_get_duration:
 
2846
 * @self: a #ClutterGstPlayback
 
2847
 *
 
2848
 * Retrieves the duration of the media stream that @self represents.
 
2849
 *
 
2850
 * Return value: the duration of the media stream, in seconds
 
2851
 */
 
2852
gdouble
 
2853
clutter_gst_playback_get_duration (ClutterGstPlayback *self)
 
2854
{
 
2855
  gdouble retval = 0;
 
2856
 
 
2857
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), 0);
 
2858
 
 
2859
  g_object_get (G_OBJECT (self), "duration", &retval, NULL);
 
2860
 
 
2861
  return retval;
 
2862
}
 
2863
 
 
2864
/**
 
2865
 * clutter_gst_playback_is_live_media:
 
2866
 * @self: a #ClutterGstPlayback
 
2867
 *
 
2868
 * Whether the player is using a live media.
 
2869
 *
 
2870
 * Return value: TRUE if the player is using a live media, FALSE otherwise.
 
2871
 */
 
2872
gboolean
 
2873
clutter_gst_playback_is_live_media (ClutterGstPlayback *self)
 
2874
{
 
2875
  g_return_val_if_fail (CLUTTER_GST_IS_PLAYBACK (self), FALSE);
 
2876
 
 
2877
  return self->priv->is_live;
 
2878
}