gstplaybin2.c 124 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
/* GStreamer
 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
3
 * Copyright (C) <2011> Sebastian Dröge <sebastian.droege@collabora.co.uk>
Wim Taymans's avatar
Wim Taymans committed
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the
 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
 * Boston, MA 02111-1307, USA.
 */

/**
 * SECTION:element-playbin2
 *
24
 * Playbin2 provides a stand-alone everything-in-one abstraction for an
Wim Taymans's avatar
Wim Taymans committed
25
 * audio and/or video player.
26
 *
27
28
 * playbin2 is considered stable now. It is the prefered playback API now,
 * and replaces the old #playbin element, which is no longer supported.
29
 *
Wim Taymans's avatar
Wim Taymans committed
30
31
32
33
34
35
36
37
38
39
 * It can handle both audio and video files and features
 * <itemizedlist>
 * <listitem>
 * automatic file type recognition and based on that automatic
 * selection and usage of the right audio/video/subtitle demuxers/decoders
 * </listitem>
 * <listitem>
 * visualisations for audio files
 * </listitem>
 * <listitem>
40
41
 * subtitle support for video files. Subtitles can be store in external
 * files.
Wim Taymans's avatar
Wim Taymans committed
42
43
 * </listitem>
 * <listitem>
44
 * stream selection between different video/audio/subtitles streams
Wim Taymans's avatar
Wim Taymans committed
45
46
47
48
49
50
51
52
53
54
55
 * </listitem>
 * <listitem>
 * meta info (tag) extraction
 * </listitem>
 * <listitem>
 * easy access to the last video frame
 * </listitem>
 * <listitem>
 * buffering when playing streams over a network
 * </listitem>
 * <listitem>
56
 * volume control with mute option
Wim Taymans's avatar
Wim Taymans committed
57
58
 * </listitem>
 * </itemizedlist>
59
60
 *
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
61
62
 * <title>Usage</title>
 * <para>
63
 * A playbin2 element can be created just like any other element using
64
 * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin2:uri
Wim Taymans's avatar
Wim Taymans committed
65
66
 * property. This must be an absolute URI, relative file paths are not allowed.
 * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
67
 *
Wim Taymans's avatar
Wim Taymans committed
68
69
70
71
 * Playbin is a #GstPipeline. It will notify the application of everything
 * that's happening (errors, end of stream, tags found, state changes, etc.)
 * by posting messages on its #GstBus. The application needs to watch the
 * bus.
72
 *
Wim Taymans's avatar
Wim Taymans committed
73
74
75
76
77
78
 * Playback can be initiated by setting the element to PLAYING state using
 * gst_element_set_state(). Note that the state change will take place in
 * the background in a separate thread, when the function returns playback
 * is probably not happening yet and any errors might not have occured yet.
 * Applications using playbin should ideally be written to deal with things
 * completely asynchroneous.
79
 *
Wim Taymans's avatar
Wim Taymans committed
80
81
82
 * When playback has finished (an EOS message has been received on the bus)
 * or an error has occured (an ERROR message has been received on the bus) or
 * the user wants to play a different track, playbin should be set back to
Wim Taymans's avatar
Wim Taymans committed
83
 * READY or NULL state, then the #GstPlayBin2:uri property should be set to the
84
85
 * new location and then playbin be set to PLAYING state again.
 *
Wim Taymans's avatar
Wim Taymans committed
86
87
88
89
90
91
92
 * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
 * on the playbin element. Again, the seek will not be executed
 * instantaneously, but will be done in a background thread. When the seek
 * call returns the seek will most likely still be in process. An application
 * may wait for the seek to finish (or fail) using gst_element_get_state() with
 * -1 as the timeout, but this will block the user interface and is not
 * recommended at all.
93
 *
Wim Taymans's avatar
Wim Taymans committed
94
95
96
97
98
 * Applications may query the current position and duration of the stream
 * via gst_element_query_position() and gst_element_query_duration() and
 * setting the format passed to GST_FORMAT_TIME. If the query was successful,
 * the duration or position will have been returned in units of nanoseconds.
 * </para>
99
100
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
101
102
103
 * <title>Advanced Usage: specifying the audio and video sink</title>
 * <para>
 * By default, if no audio sink or video sink has been specified via the
104
 * #GstPlayBin2:audio-sink or #GstPlayBin2:video-sink property, playbin will use the autoaudiosink
Wim Taymans's avatar
Wim Taymans committed
105
106
107
108
 * and autovideosink elements to find the first-best available output method.
 * This should work in most cases, but is not always desirable. Often either
 * the user or application might want to specify more explicitly what to use
 * for audio and video output.
109
 *
Wim Taymans's avatar
Wim Taymans committed
110
111
112
 * If the application wants more control over how audio or video should be
 * output, it may create the audio/video sink elements itself (for example
 * using gst_element_factory_make()) and provide them to playbin using the
113
114
 * #GstPlayBin2:audio-sink or #GstPlayBin2:video-sink property.
 *
Wim Taymans's avatar
Wim Taymans committed
115
116
117
 * GNOME-based applications, for example, will usually want to create
 * gconfaudiosink and gconfvideosink elements and make playbin use those,
 * so that output happens to whatever the user has configured in the GNOME
118
 * Multimedia System Selector configuration dialog.
119
 *
Wim Taymans's avatar
Wim Taymans committed
120
121
122
123
124
125
126
127
 * The sink elements do not necessarily need to be ready-made sinks. It is
 * possible to create container elements that look like a sink to playbin,
 * but in reality contain a number of custom elements linked together. This
 * can be achieved by creating a #GstBin and putting elements in there and
 * linking them, and then creating a sink #GstGhostPad for the bin and pointing
 * it to the sink pad of the first element within the bin. This can be used
 * for a number of purposes, for example to force output to a particular
 * format or to modify or observe the data before it is output.
128
 *
Wim Taymans's avatar
Wim Taymans committed
129
130
131
132
 * It is also possible to 'suppress' audio and/or video output by using
 * 'fakesink' elements (or capture it from there using the fakesink element's
 * "handoff" signal, which, nota bene, is fired from the streaming thread!).
 * </para>
133
134
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
135
136
137
138
 * <title>Retrieving Tags and Other Meta Data</title>
 * <para>
 * Most of the common meta data (artist, title, etc.) can be retrieved by
 * watching for TAG messages on the pipeline's bus (see above).
139
 *
Wim Taymans's avatar
Wim Taymans committed
140
141
 * Other more specific meta information like width/height/framerate of video
 * streams or samplerate/number of channels of audio streams can be obtained
142
 * from the negotiated caps on the sink pads of the sinks.
Wim Taymans's avatar
Wim Taymans committed
143
 * </para>
144
145
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
146
147
148
149
150
151
152
153
154
 * <title>Buffering</title>
 * Playbin handles buffering automatically for the most part, but applications
 * need to handle parts of the buffering process as well. Whenever playbin is
 * buffering, it will post BUFFERING messages on the bus with a percentage
 * value that shows the progress of the buffering process. Applications need
 * to set playbin to PLAYING or PAUSED state in response to these messages.
 * They may also want to convey the buffering progress to the user in some
 * way. Here is how to extract the percentage information from the message
 * (requires GStreamer >= 0.10.11):
155
 * |[
Wim Taymans's avatar
Wim Taymans committed
156
157
158
159
160
161
162
163
164
 * switch (GST_MESSAGE_TYPE (msg)) {
 *   case GST_MESSAGE_BUFFERING: {
 *     gint percent = 0;
 *     gst_message_parse_buffering (msg, &amp;percent);
 *     g_print ("Buffering (%%u percent done)", percent);
 *     break;
 *   }
 *   ...
 * }
165
 * ]|
Wim Taymans's avatar
Wim Taymans committed
166
167
168
169
 * Note that applications should keep/set the pipeline in the PAUSED state when
 * a BUFFERING message is received with a buffer percent value < 100 and set
 * the pipeline back to PLAYING state when a BUFFERING message with a value
 * of 100 percent is received (if PLAYING is the desired state, that is).
170
171
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
172
173
174
 * <title>Embedding the video window in your application</title>
 * By default, playbin (or rather the video sinks used) will create their own
 * window. Applications will usually want to force output to a window of their
175
 * own, however. This can be done using the #GstXOverlay interface, which most
Wim Taymans's avatar
Wim Taymans committed
176
 * video sinks implement. See the documentation there for more details.
177
178
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
179
180
181
182
183
184
185
186
187
 * <title>Specifying which CD/DVD device to use</title>
 * The device to use for CDs/DVDs needs to be set on the source element
 * playbin creates before it is opened. The only way to do this at the moment
 * is to connect to playbin's "notify::source" signal, which will be emitted
 * by playbin when it has created the source element for a particular URI.
 * In the signal callback you can check if the source element has a "device"
 * property and set it appropriately. In future ways might be added to specify
 * the device as part of the URI, but at the time of writing this is not
 * possible yet.
188
189
 * </refsect2>
 * <refsect2>
190
191
192
193
194
195
196
197
198
199
 * <title>Handling redirects</title>
 * <para>
 * Some elements may post 'redirect' messages on the bus to tell the
 * application to open another location. These are element messages containing
 * a structure named 'redirect' along with a 'new-location' field of string
 * type. The new location may be a relative or an absolute URI. Examples
 * for such redirects can be found in many quicktime movie trailers.
 * </para>
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
200
 * <title>Examples</title>
201
 * |[
Wim Taymans's avatar
Wim Taymans committed
202
 * gst-launch -v playbin uri=file:///path/to/somefile.avi
203
 * ]| This will play back the given AVI video file, given that the video and
Wim Taymans's avatar
Wim Taymans committed
204
205
206
207
 * audio decoders required to decode the content are installed. Since no
 * special audio sink or video sink is supplied (not possible via gst-launch),
 * playbin will try to find a suitable audio and video sink automatically
 * using the autoaudiosink and autovideosink elements.
208
 * |[
Wim Taymans's avatar
Wim Taymans committed
209
 * gst-launch -v playbin uri=cdda://4
210
 * ]| This will play back track 4 on an audio CD in your disc drive (assuming
Wim Taymans's avatar
Wim Taymans committed
211
 * the drive is detected automatically by the plugin).
212
 * |[
Wim Taymans's avatar
Wim Taymans committed
213
 * gst-launch -v playbin uri=dvd://1
214
 * ]| This will play back title 1 of a DVD in your disc drive (assuming
Wim Taymans's avatar
Wim Taymans committed
215
216
217
218
219
220
221
222
223
224
225
226
227
 * the drive is detected automatically by the plugin).
 * </refsect2>
 */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <string.h>
#include <gst/gst.h>

#include <gst/gst-i18n-plugin.h>
#include <gst/pbutils/pbutils.h>
228
#include <gst/interfaces/streamvolume.h>
Wim Taymans's avatar
Wim Taymans committed
229

230
#include "gstplay-enum.h"
231
#include "gstplay-marshal.h"
232
#include "gstplayback.h"
Wim Taymans's avatar
Wim Taymans committed
233
#include "gstplaysink.h"
234
#include "gstsubtitleoverlay.h"
Wim Taymans's avatar
Wim Taymans committed
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251

GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
#define GST_CAT_DEFAULT gst_play_bin_debug

#define GST_TYPE_PLAY_BIN               (gst_play_bin_get_type())
#define GST_PLAY_BIN(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin))
#define GST_PLAY_BIN_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBinClass))
#define GST_IS_PLAY_BIN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
#define GST_IS_PLAY_BIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))

#define VOLUME_MAX_DOUBLE 10.0

typedef struct _GstPlayBin GstPlayBin;
typedef struct _GstPlayBinClass GstPlayBinClass;
typedef struct _GstSourceGroup GstSourceGroup;
typedef struct _GstSourceSelect GstSourceSelect;

252
253
typedef GstCaps *(*SourceSelectGetMediaCapsFunc) (void);

Wim Taymans's avatar
Wim Taymans committed
254
255
256
/* has the info for a selector and provides the link to the sink */
struct _GstSourceSelect
{
257
  const gchar *media_list[8];   /* the media types for the selector */
258
  SourceSelectGetMediaCapsFunc get_media_caps;  /* more complex caps for the selector */
Wim Taymans's avatar
Wim Taymans committed
259
260
261
  GstPlaySinkType type;         /* the sink pad type of the selector */

  GstElement *selector;         /* the selector */
262
  GPtrArray *channels;
Wim Taymans's avatar
Wim Taymans committed
263
  GstPad *srcpad;               /* the source pad of the selector */
264
265
266
  GstPad *sinkpad;              /* the sinkpad of the sink when the selector
                                 * is linked
                                 */
267
268
  GstEvent *sinkpad_delayed_event;
  gulong sinkpad_data_probe;
Wim Taymans's avatar
Wim Taymans committed
269
270
};

271
272
273
274
#define GST_SOURCE_GROUP_GET_LOCK(group) (((GstSourceGroup*)(group))->lock)
#define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
#define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))

275
276
277
278
279
280
281
282
enum
{
  PLAYBIN_STREAM_AUDIO = 0,
  PLAYBIN_STREAM_VIDEO,
  PLAYBIN_STREAM_TEXT,
  PLAYBIN_STREAM_LAST
};

Wim Taymans's avatar
Wim Taymans committed
283
284
285
286
287
288
/* a structure to hold the objects for decoding a uri and the subtitle uri
 */
struct _GstSourceGroup
{
  GstPlayBin *playbin;

289
290
  GMutex *lock;

Wim Taymans's avatar
Wim Taymans committed
291
  gboolean valid;               /* the group has valid info to start playback */
292
  gboolean active;              /* the group is active */
Wim Taymans's avatar
Wim Taymans committed
293
294
295
296
297
298
299

  /* properties */
  gchar *uri;
  gchar *suburi;
  GValueArray *streaminfo;
  GstElement *source;

300
301
302
303
  GPtrArray *video_channels;    /* links to selector pads */
  GPtrArray *audio_channels;    /* links to selector pads */
  GPtrArray *text_channels;     /* links to selector pads */

304
305
306
  GstElement *audio_sink;       /* autoplugged audio and video sinks */
  GstElement *video_sink;

Wim Taymans's avatar
Wim Taymans committed
307
308
309
  /* uridecodebins for uri and subtitle uri */
  GstElement *uridecodebin;
  GstElement *suburidecodebin;
310
  gint pending;
311
  gboolean sub_pending;
Wim Taymans's avatar
Wim Taymans committed
312

313
314
315
316
317
318
319
  gulong pad_added_id;
  gulong pad_removed_id;
  gulong no_more_pads_id;
  gulong notify_source_id;
  gulong drained_id;
  gulong autoplug_factories_id;
  gulong autoplug_select_id;
320
  gulong autoplug_continue_id;
321
322
323
324

  gulong sub_pad_added_id;
  gulong sub_pad_removed_id;
  gulong sub_no_more_pads_id;
325
  gulong sub_autoplug_continue_id;
326

327
328
  GMutex *stream_changed_pending_lock;
  GList *stream_changed_pending;
329

Wim Taymans's avatar
Wim Taymans committed
330
  /* selectors for different streams */
331
  GstSourceSelect selector[PLAYBIN_STREAM_LAST];
Wim Taymans's avatar
Wim Taymans committed
332
333
};

334
335
336
#define GST_PLAY_BIN_GET_LOCK(bin) (&((GstPlayBin*)(bin))->lock)
#define GST_PLAY_BIN_LOCK(bin) (g_static_rec_mutex_lock (GST_PLAY_BIN_GET_LOCK(bin)))
#define GST_PLAY_BIN_UNLOCK(bin) (g_static_rec_mutex_unlock (GST_PLAY_BIN_GET_LOCK(bin)))
337

338
339
340
341
342
/* lock to protect dynamic callbacks, like no-more-pads */
#define GST_PLAY_BIN_DYN_LOCK(bin)    g_mutex_lock ((bin)->dyn_lock)
#define GST_PLAY_BIN_DYN_UNLOCK(bin)  g_mutex_unlock ((bin)->dyn_lock)

/* lock for shutdown */
343
344
345
346
347
348
349
350
351
#define GST_PLAY_BIN_SHUTDOWN_LOCK(bin,label)           \
G_STMT_START {                                          \
  if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown)))   \
    goto label;                                         \
  GST_PLAY_BIN_DYN_LOCK (bin);                          \
  if (G_UNLIKELY (g_atomic_int_get (&bin->shutdown))) { \
    GST_PLAY_BIN_DYN_UNLOCK (bin);                      \
    goto label;                                         \
  }                                                     \
352
353
354
355
356
357
} G_STMT_END

/* unlock for shutdown */
#define GST_PLAY_BIN_SHUTDOWN_UNLOCK(bin)         \
  GST_PLAY_BIN_DYN_UNLOCK (bin);                  \

358
359
360
361
362
/**
 * GstPlayBin2:
 *
 * playbin element structure
 */
Wim Taymans's avatar
Wim Taymans committed
363
364
365
366
struct _GstPlayBin
{
  GstPipeline parent;

367
  GStaticRecMutex lock;         /* to protect group switching */
368

Wim Taymans's avatar
Wim Taymans committed
369
370
371
372
373
374
375
  /* the groups, we use a double buffer to switch between current and next */
  GstSourceGroup groups[2];     /* array with group info */
  GstSourceGroup *curr_group;   /* pointer to the currently playing group */
  GstSourceGroup *next_group;   /* pointer to the next group */

  /* properties */
  guint connection_speed;       /* connection speed in bits/sec (0 = unknown) */
376
377
378
  gint current_video;           /* the currently selected stream */
  gint current_audio;           /* the currently selected stream */
  gint current_text;            /* the currently selected stream */
Wim Taymans's avatar
Wim Taymans committed
379

380
381
382
  guint64 buffer_duration;      /* When buffering, the max buffer duration (ns) */
  guint buffer_size;            /* When buffering, the max buffer size (bytes) */

Wim Taymans's avatar
Wim Taymans committed
383
384
  /* our play sink */
  GstPlaySink *playsink;
385

386
387
388
  /* the last activated source */
  GstElement *source;

389
390
391
392
393
  /* lock protecting dynamic adding/removing */
  GMutex *dyn_lock;
  /* if we are shutting down or not */
  gint shutdown;

394
  GMutex *elements_lock;
395
  guint32 elements_cookie;
396
  GList *elements;              /* factories we can use for selecting elements */
397
398
399
400

  gboolean have_selector;       /* set to FALSE when we fail to create an
                                 * input-selector, so that we only post a
                                 * warning once */
401
402
403
404

  GstElement *audio_sink;       /* configured audio sink, or NULL      */
  GstElement *video_sink;       /* configured video sink, or NULL      */
  GstElement *text_sink;        /* configured text sink, or NULL       */
405
406
407
408
409
410
411

  struct
  {
    gboolean valid;
    GstFormat format;
    gint64 duration;
  } duration[5];                /* cached durations */
412
413

  guint64 ring_buffer_max_size; /* 0 means disabled */
Wim Taymans's avatar
Wim Taymans committed
414
415
416
417
418
419
};

struct _GstPlayBinClass
{
  GstPipelineClass parent_class;

420
421
  /* notify app that the current uri finished decoding and it is possible to
   * queue a new one for gapless playback */
422
423
  void (*about_to_finish) (GstPlayBin * playbin);

424
  /* notify app that number of audio/video/text streams changed */
425
426
427
428
  void (*video_changed) (GstPlayBin * playbin);
  void (*audio_changed) (GstPlayBin * playbin);
  void (*text_changed) (GstPlayBin * playbin);

429
430
431
432
433
  /* notify app that the tags of audio/video/text streams changed */
  void (*video_tags_changed) (GstPlayBin * playbin, gint stream);
  void (*audio_tags_changed) (GstPlayBin * playbin, gint stream);
  void (*text_tags_changed) (GstPlayBin * playbin, gint stream);

434
  /* get audio/video/text tags for a stream */
435
436
437
  GstTagList *(*get_video_tags) (GstPlayBin * playbin, gint stream);
  GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
  GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
438
439
440

  /* get the last video frame and convert it to the given caps */
  GstBuffer *(*convert_frame) (GstPlayBin * playbin, GstCaps * caps);
441
442
443
444
445

  /* get audio/video/text pad for a stream */
  GstPad *(*get_video_pad) (GstPlayBin * playbin, gint stream);
  GstPad *(*get_audio_pad) (GstPlayBin * playbin, gint stream);
  GstPad *(*get_text_pad) (GstPlayBin * playbin, gint stream);
Wim Taymans's avatar
Wim Taymans committed
446
447
448
};

/* props */
449
450
451
452
#define DEFAULT_URI               NULL
#define DEFAULT_SUBURI            NULL
#define DEFAULT_SOURCE            NULL
#define DEFAULT_FLAGS             GST_PLAY_FLAG_AUDIO | GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_TEXT | \
Wim Taymans's avatar
Wim Taymans committed
453
                                  GST_PLAY_FLAG_SOFT_VOLUME
454
455
456
457
458
459
460
461
462
463
#define DEFAULT_N_VIDEO           0
#define DEFAULT_CURRENT_VIDEO     -1
#define DEFAULT_N_AUDIO           0
#define DEFAULT_CURRENT_AUDIO     -1
#define DEFAULT_N_TEXT            0
#define DEFAULT_CURRENT_TEXT      -1
#define DEFAULT_SUBTITLE_ENCODING NULL
#define DEFAULT_AUDIO_SINK        NULL
#define DEFAULT_VIDEO_SINK        NULL
#define DEFAULT_VIS_PLUGIN        NULL
464
#define DEFAULT_TEXT_SINK         NULL
465
#define DEFAULT_VOLUME            1.0
466
#define DEFAULT_MUTE              FALSE
467
468
469
#define DEFAULT_FRAME             NULL
#define DEFAULT_FONT_DESC         NULL
#define DEFAULT_CONNECTION_SPEED  0
470
471
#define DEFAULT_BUFFER_DURATION   -1
#define DEFAULT_BUFFER_SIZE       -1
472
#define DEFAULT_RING_BUFFER_MAX_SIZE 0
473

Wim Taymans's avatar
Wim Taymans committed
474
475
476
477
478
479
enum
{
  PROP_0,
  PROP_URI,
  PROP_SUBURI,
  PROP_SOURCE,
480
481
  PROP_FLAGS,
  PROP_N_VIDEO,
Wim Taymans's avatar
Wim Taymans committed
482
  PROP_CURRENT_VIDEO,
483
  PROP_N_AUDIO,
Wim Taymans's avatar
Wim Taymans committed
484
  PROP_CURRENT_AUDIO,
485
  PROP_N_TEXT,
Wim Taymans's avatar
Wim Taymans committed
486
487
488
489
490
  PROP_CURRENT_TEXT,
  PROP_SUBTITLE_ENCODING,
  PROP_AUDIO_SINK,
  PROP_VIDEO_SINK,
  PROP_VIS_PLUGIN,
491
  PROP_TEXT_SINK,
Wim Taymans's avatar
Wim Taymans committed
492
  PROP_VOLUME,
493
  PROP_MUTE,
Wim Taymans's avatar
Wim Taymans committed
494
495
  PROP_FRAME,
  PROP_FONT_DESC,
496
497
  PROP_CONNECTION_SPEED,
  PROP_BUFFER_SIZE,
498
  PROP_BUFFER_DURATION,
Wim Taymans's avatar
Wim Taymans committed
499
  PROP_AV_OFFSET,
500
  PROP_RING_BUFFER_MAX_SIZE,
501
  PROP_LAST
Wim Taymans's avatar
Wim Taymans committed
502
503
504
505
506
507
};

/* signals */
enum
{
  SIGNAL_ABOUT_TO_FINISH,
508
  SIGNAL_CONVERT_FRAME,
509
510
511
  SIGNAL_VIDEO_CHANGED,
  SIGNAL_AUDIO_CHANGED,
  SIGNAL_TEXT_CHANGED,
512
513
514
  SIGNAL_VIDEO_TAGS_CHANGED,
  SIGNAL_AUDIO_TAGS_CHANGED,
  SIGNAL_TEXT_TAGS_CHANGED,
515
516
517
  SIGNAL_GET_VIDEO_TAGS,
  SIGNAL_GET_AUDIO_TAGS,
  SIGNAL_GET_TEXT_TAGS,
518
519
520
  SIGNAL_GET_VIDEO_PAD,
  SIGNAL_GET_AUDIO_PAD,
  SIGNAL_GET_TEXT_PAD,
521
  SIGNAL_SOURCE_SETUP,
Wim Taymans's avatar
Wim Taymans committed
522
523
524
525
  LAST_SIGNAL
};

static void gst_play_bin_class_init (GstPlayBinClass * klass);
526
static void gst_play_bin_init (GstPlayBin * playbin);
527
static void gst_play_bin_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
528
529
530
531
532
533
534
535
536
537

static void gst_play_bin_set_property (GObject * object, guint prop_id,
    const GValue * value, GParamSpec * spec);
static void gst_play_bin_get_property (GObject * object, guint prop_id,
    GValue * value, GParamSpec * spec);

static GstStateChangeReturn gst_play_bin_change_state (GstElement * element,
    GstStateChange transition);

static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message);
538
static gboolean gst_play_bin_query (GstElement * element, GstQuery * query);
Wim Taymans's avatar
Wim Taymans committed
539

540
static GstTagList *gst_play_bin_get_video_tags (GstPlayBin * playbin,
541
    gint stream);
542
static GstTagList *gst_play_bin_get_audio_tags (GstPlayBin * playbin,
543
    gint stream);
544
static GstTagList *gst_play_bin_get_text_tags (GstPlayBin * playbin,
545
546
    gint stream);

547
548
549
static GstBuffer *gst_play_bin_convert_frame (GstPlayBin * playbin,
    GstCaps * caps);

550
551
552
553
static GstPad *gst_play_bin_get_video_pad (GstPlayBin * playbin, gint stream);
static GstPad *gst_play_bin_get_audio_pad (GstPlayBin * playbin, gint stream);
static GstPad *gst_play_bin_get_text_pad (GstPlayBin * playbin, gint stream);

554
static gboolean setup_next_source (GstPlayBin * playbin, GstState target);
Wim Taymans's avatar
Wim Taymans committed
555

556
557
558
559
static void no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group);
static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
    GstSourceGroup * group);

560
561
562
563
564
static void gst_play_bin_suburidecodebin_block (GstElement * suburidecodebin,
    gboolean block);
static void gst_play_bin_suburidecodebin_seek_to_start (GstElement *
    suburidecodebin);

Wim Taymans's avatar
Wim Taymans committed
565
566
567
568
static GstElementClass *parent_class;

static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };

569
570
571
572
573
574
#define REMOVE_SIGNAL(obj,id)            \
if (id) {                                \
  g_signal_handler_disconnect (obj, id); \
  id = 0;                                \
}

Wim Taymans's avatar
Wim Taymans committed
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
static GType
gst_play_bin_get_type (void)
{
  static GType gst_play_bin_type = 0;

  if (!gst_play_bin_type) {
    static const GTypeInfo gst_play_bin_info = {
      sizeof (GstPlayBinClass),
      NULL,
      NULL,
      (GClassInitFunc) gst_play_bin_class_init,
      NULL,
      NULL,
      sizeof (GstPlayBin),
      0,
      (GInstanceInitFunc) gst_play_bin_init,
      NULL
    };
593
594
595
    static const GInterfaceInfo svol_info = {
      NULL, NULL, NULL
    };
Wim Taymans's avatar
Wim Taymans committed
596
597
598

    gst_play_bin_type = g_type_register_static (GST_TYPE_PIPELINE,
        "GstPlayBin2", &gst_play_bin_info, 0);
599
600
601

    g_type_add_interface_static (gst_play_bin_type, GST_TYPE_STREAM_VOLUME,
        &svol_info);
Wim Taymans's avatar
Wim Taymans committed
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
  }

  return gst_play_bin_type;
}

static void
gst_play_bin_class_init (GstPlayBinClass * klass)
{
  GObjectClass *gobject_klass;
  GstElementClass *gstelement_klass;
  GstBinClass *gstbin_klass;

  gobject_klass = (GObjectClass *) klass;
  gstelement_klass = (GstElementClass *) klass;
  gstbin_klass = (GstBinClass *) klass;

  parent_class = g_type_class_peek_parent (klass);

  gobject_klass->set_property = gst_play_bin_set_property;
  gobject_klass->get_property = gst_play_bin_get_property;

623
  gobject_klass->finalize = gst_play_bin_finalize;
Wim Taymans's avatar
Wim Taymans committed
624

625
  /**
626
   * GstPlayBin2:uri
627
628
629
630
   *
   * Set the next URI that playbin will play. This property can be set from the
   * about-to-finish signal to queue the next media file.
   */
Wim Taymans's avatar
Wim Taymans committed
631
632
  g_object_class_install_property (gobject_klass, PROP_URI,
      g_param_spec_string ("uri", "URI", "URI of the media to play",
633
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
634
635

  /**
636
   * GstPlayBin2:suburi
637
638
639
640
   *
   * Set the next subtitle URI that playbin will play. This property can be
   * set from the about-to-finish signal to queue the next subtitle media file.
   */
Wim Taymans's avatar
Wim Taymans committed
641
642
  g_object_class_install_property (gobject_klass, PROP_SUBURI,
      g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
643
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
644
645
646

  g_object_class_install_property (gobject_klass, PROP_SOURCE,
      g_param_spec_object ("source", "Source", "Source element",
647
          GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
648

649
  /**
650
   * GstPlayBin2:flags
651
652
653
   *
   * Control the behaviour of playbin.
   */
654
655
  g_object_class_install_property (gobject_klass, PROP_FLAGS,
      g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
656
657
          GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
658
659

  /**
660
   * GstPlayBin2:n-video
661
   *
Wim Taymans's avatar
Wim Taymans committed
662
   * Get the total number of available video streams.
663
664
665
   */
  g_object_class_install_property (gobject_klass, PROP_N_VIDEO,
      g_param_spec_int ("n-video", "Number Video",
666
667
          "Total number of video streams", 0, G_MAXINT, 0,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
668
  /**
669
   * GstPlayBin2:current-video
670
671
672
673
   *
   * Get or set the currently playing video stream. By default the first video
   * stream with data is played.
   */
Wim Taymans's avatar
Wim Taymans committed
674
  g_object_class_install_property (gobject_klass, PROP_CURRENT_VIDEO,
675
      g_param_spec_int ("current-video", "Current Video",
676
          "Currently playing video stream (-1 = auto)",
677
          -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
678
  /**
679
   * GstPlayBin2:n-audio
680
   *
Wim Taymans's avatar
Wim Taymans committed
681
   * Get the total number of available audio streams.
682
683
684
   */
  g_object_class_install_property (gobject_klass, PROP_N_AUDIO,
      g_param_spec_int ("n-audio", "Number Audio",
685
686
          "Total number of audio streams", 0, G_MAXINT, 0,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
687
  /**
688
   * GstPlayBin2:current-audio
689
690
691
692
   *
   * Get or set the currently playing audio stream. By default the first audio
   * stream with data is played.
   */
Wim Taymans's avatar
Wim Taymans committed
693
694
  g_object_class_install_property (gobject_klass, PROP_CURRENT_AUDIO,
      g_param_spec_int ("current-audio", "Current audio",
695
          "Currently playing audio stream (-1 = auto)",
696
          -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
697
  /**
698
   * GstPlayBin2:n-text
699
   *
Wim Taymans's avatar
Wim Taymans committed
700
   * Get the total number of available subtitle streams.
701
702
703
   */
  g_object_class_install_property (gobject_klass, PROP_N_TEXT,
      g_param_spec_int ("n-text", "Number Text",
704
705
          "Total number of text streams", 0, G_MAXINT, 0,
          G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
706
  /**
707
   * GstPlayBin2:current-text:
708
   *
709
710
   * Get or set the currently playing subtitle stream. By default the first
   * subtitle stream with data is played.
711
   */
Wim Taymans's avatar
Wim Taymans committed
712
  g_object_class_install_property (gobject_klass, PROP_CURRENT_TEXT,
713
      g_param_spec_int ("current-text", "Current Text",
714
          "Currently playing text stream (-1 = auto)",
715
          -1, G_MAXINT, -1, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
716
717
718
719
720
721

  g_object_class_install_property (gobject_klass, PROP_SUBTITLE_ENCODING,
      g_param_spec_string ("subtitle-encoding", "subtitle encoding",
          "Encoding to assume if input subtitles are not in UTF-8 encoding. "
          "If not set, the GST_SUBTITLE_ENCODING environment variable will "
          "be checked for an encoding to use. If that is not set either, "
722
723
          "ISO-8859-15 will be assumed.", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
724
725
726
727

  g_object_class_install_property (gobject_klass, PROP_VIDEO_SINK,
      g_param_spec_object ("video-sink", "Video Sink",
          "the video output element to use (NULL = default sink)",
728
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
729
730
731
  g_object_class_install_property (gobject_klass, PROP_AUDIO_SINK,
      g_param_spec_object ("audio-sink", "Audio Sink",
          "the audio output element to use (NULL = default sink)",
732
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
733
734
  g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
      g_param_spec_object ("vis-plugin", "Vis plugin",
735
          "the visualization element to use (NULL = default)",
736
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
737
738
739
740
  g_object_class_install_property (gobject_klass, PROP_TEXT_SINK,
      g_param_spec_object ("text-sink", "Text plugin",
          "the text output element to use (NULL = default textoverlay)",
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
741

742
743
744
745
746
747
748
  /**
   * GstPlayBin2:volume:
   *
   * Get or set the current audio stream volume. 1.0 means 100%,
   * 0.0 means mute. This uses a linear volume scale.
   *
   */
Wim Taymans's avatar
Wim Taymans committed
749
  g_object_class_install_property (gobject_klass, PROP_VOLUME,
750
      g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
751
752
          0.0, VOLUME_MAX_DOUBLE, 1.0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
753
754
755
  g_object_class_install_property (gobject_klass, PROP_MUTE,
      g_param_spec_boolean ("mute", "Mute",
          "Mute the audio channel without changing the volume", FALSE,
756
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
757

758
  /**
759
760
   * GstPlayBin2:frame:
   * @playbin: a #GstPlayBin2
761
   *
762
   * Get the currently rendered or prerolled frame in the video sink.
763
764
   * The #GstCaps on the buffer will describe the format of the buffer.
   */
Wim Taymans's avatar
Wim Taymans committed
765
  g_object_class_install_property (gobject_klass, PROP_FRAME,
766
      g_param_spec_boxed ("frame", "Frame",
Wim Taymans's avatar
Wim Taymans committed
767
          "The last frame (NULL = no video available)",
768
          GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
769
770
771
772
  g_object_class_install_property (gobject_klass, PROP_FONT_DESC,
      g_param_spec_string ("subtitle-font-desc",
          "Subtitle font description",
          "Pango font description of font "
773
774
          "to be used for subtitle rendering", NULL,
          G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
775
776
777
778

  g_object_class_install_property (gobject_klass, PROP_CONNECTION_SPEED,
      g_param_spec_uint ("connection-speed", "Connection Speed",
          "Network connection speed in kbps (0 = unknown)",
779
          0, G_MAXUINT / 1000, DEFAULT_CONNECTION_SPEED,
780
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
781
782
783
784
785
786
787
788
789
790
791

  g_object_class_install_property (gobject_klass, PROP_BUFFER_SIZE,
      g_param_spec_int ("buffer-size", "Buffer size (bytes)",
          "Buffer size when buffering network streams",
          -1, G_MAXINT, DEFAULT_BUFFER_SIZE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
  g_object_class_install_property (gobject_klass, PROP_BUFFER_DURATION,
      g_param_spec_int64 ("buffer-duration", "Buffer duration (ns)",
          "Buffer duration when buffering network streams",
          -1, G_MAXINT64, DEFAULT_BUFFER_DURATION,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
792
793
794
795
796
797
798
799
800
801
802
803
804
805
  /**
   * GstPlayBin2:av-offset:
   *
   * Control the synchronisation offset between the audio and video streams.
   * Positive values make the audio ahead of the video and negative values make
   * the audio go behind the video.
   *
   * Since: 0.10.30
   */
  g_object_class_install_property (gobject_klass, PROP_AV_OFFSET,
      g_param_spec_int64 ("av-offset", "AV Offset",
          "The synchronisation offset between audio and video in nanoseconds",
          G_MININT64, G_MAXINT64, 0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
806

807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
  /**
   * GstQueue2:ring-buffer-max-size
   *
   * The maximum size of the ring buffer in bytes. If set to 0, the ring
   * buffer is disabled. Default 0.
   *
   * Since: 0.10.31
   */
  g_object_class_install_property (gobject_klass, PROP_RING_BUFFER_MAX_SIZE,
      g_param_spec_uint64 ("ring-buffer-max-size",
          "Max. ring buffer size (bytes)",
          "Max. amount of data in the ring buffer (bytes, 0 = ring buffer disabled)",
          0, G_MAXUINT, DEFAULT_RING_BUFFER_MAX_SIZE,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));

Wim Taymans's avatar
Wim Taymans committed
822
  /**
823
824
   * GstPlayBin2::about-to-finish
   * @playbin: a #GstPlayBin2
Wim Taymans's avatar
Wim Taymans committed
825
826
   *
   * This signal is emitted when the current uri is about to finish. You can
827
   * set the uri and suburi to make sure that playback continues.
Wim Taymans's avatar
Wim Taymans committed
828
829
830
831
832
833
834
   */
  gst_play_bin_signals[SIGNAL_ABOUT_TO_FINISH] =
      g_signal_new ("about-to-finish", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstPlayBinClass, about_to_finish), NULL, NULL,
      gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);

835
  /**
836
837
   * GstPlayBin2::video-changed
   * @playbin: a #GstPlayBin2
838
   *
839
   * This signal is emitted whenever the number or order of the video
840
841
842
843
844
845
846
847
848
   * streams has changed. The application will most likely want to select
   * a new video stream.
   */
  gst_play_bin_signals[SIGNAL_VIDEO_CHANGED] =
      g_signal_new ("video-changed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstPlayBinClass, video_changed), NULL, NULL,
      gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
  /**
849
850
   * GstPlayBin2::audio-changed
   * @playbin: a #GstPlayBin2
851
   *
852
   * This signal is emitted whenever the number or order of the audio
853
854
855
856
857
858
859
860
861
   * streams has changed. The application will most likely want to select
   * a new audio stream.
   */
  gst_play_bin_signals[SIGNAL_AUDIO_CHANGED] =
      g_signal_new ("audio-changed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstPlayBinClass, audio_changed), NULL, NULL,
      gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);
  /**
862
863
   * GstPlayBin2::text-changed
   * @playbin: a #GstPlayBin2
864
   *
865
   * This signal is emitted whenever the number or order of the text
866
867
868
869
870
871
872
873
874
   * streams has changed. The application will most likely want to select
   * a new text stream.
   */
  gst_play_bin_signals[SIGNAL_TEXT_CHANGED] =
      g_signal_new ("text-changed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstPlayBinClass, text_changed), NULL, NULL,
      gst_marshal_VOID__VOID, G_TYPE_NONE, 0, G_TYPE_NONE);

875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
  /**
   * GstPlayBin2::video-tags-changed
   * @playbin: a #GstPlayBin2
   * @stream: stream index with changed tags
   *
   * This signal is emitted whenever the tags of a video stream have changed.
   * The application will most likely want to get the new tags.
   *
   * Since: 0.10.24
   */
  gst_play_bin_signals[SIGNAL_VIDEO_TAGS_CHANGED] =
      g_signal_new ("video-tags-changed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstPlayBinClass, video_tags_changed), NULL, NULL,
      gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);

  /**
   * GstPlayBin2::audio-tags-changed
   * @playbin: a #GstPlayBin2
   * @stream: stream index with changed tags
   *
   * This signal is emitted whenever the tags of an audio stream have changed.
   * The application will most likely want to get the new tags.
   *
   * Since: 0.10.24
   */
  gst_play_bin_signals[SIGNAL_AUDIO_TAGS_CHANGED] =
      g_signal_new ("audio-tags-changed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstPlayBinClass, audio_tags_changed), NULL, NULL,
      gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);

  /**
   * GstPlayBin2::text-tags-changed
   * @playbin: a #GstPlayBin2
   * @stream: stream index with changed tags
   *
   * This signal is emitted whenever the tags of a text stream have changed.
   * The application will most likely want to get the new tags.
   *
   * Since: 0.10.24
   */
  gst_play_bin_signals[SIGNAL_TEXT_TAGS_CHANGED] =
      g_signal_new ("text-tags-changed", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST,
      G_STRUCT_OFFSET (GstPlayBinClass, text_tags_changed), NULL, NULL,
      gst_marshal_VOID__INT, G_TYPE_NONE, 1, G_TYPE_INT);

923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
  /**
   * GstPlayBin2::source-setup:
   * @playbin: a #GstPlayBin2
   * @source: source element
   *
   * This signal is emitted after the source element has been created, so
   * it can be configured by setting additional properties (e.g. set a
   * proxy server for an http source, or set the device and read speed for
   * an audio cd source). This is functionally equivalent to connecting to
   * the notify::source signal, but more convenient.
   *
   * Since: 0.10.33
   */
  gst_play_bin_signals[SIGNAL_SOURCE_SETUP] =
      g_signal_new ("source-setup", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST, 0, NULL, NULL,
      gst_marshal_VOID__OBJECT, G_TYPE_NONE, 1, GST_TYPE_ELEMENT);

941
  /**
942
943
   * GstPlayBin2::get-video-tags
   * @playbin: a #GstPlayBin2
944
945
946
947
948
949
950
951
952
953
954
955
956
957
   * @stream: a video stream number
   *
   * Action signal to retrieve the tags of a specific video stream number.
   * This information can be used to select a stream.
   *
   * Returns: a GstTagList with tags or NULL when the stream number does not
   * exist.
   */
  gst_play_bin_signals[SIGNAL_GET_VIDEO_TAGS] =
      g_signal_new ("get-video-tags", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstPlayBinClass, get_video_tags), NULL, NULL,
      gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
  /**
958
959
   * GstPlayBin2::get-audio-tags
   * @playbin: a #GstPlayBin2
960
961
962
963
964
965
966
967
968
969
970
971
972
973
   * @stream: an audio stream number
   *
   * Action signal to retrieve the tags of a specific audio stream number.
   * This information can be used to select a stream.
   *
   * Returns: a GstTagList with tags or NULL when the stream number does not
   * exist.
   */
  gst_play_bin_signals[SIGNAL_GET_AUDIO_TAGS] =
      g_signal_new ("get-audio-tags", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstPlayBinClass, get_audio_tags), NULL, NULL,
      gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
  /**
974
975
   * GstPlayBin2::get-text-tags
   * @playbin: a #GstPlayBin2
976
977
978
979
980
981
982
983
984
985
986
987
988
   * @stream: a text stream number
   *
   * Action signal to retrieve the tags of a specific text stream number.
   * This information can be used to select a stream.
   *
   * Returns: a GstTagList with tags or NULL when the stream number does not
   * exist.
   */
  gst_play_bin_signals[SIGNAL_GET_TEXT_TAGS] =
      g_signal_new ("get-text-tags", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstPlayBinClass, get_text_tags), NULL, NULL,
      gst_play_marshal_BOXED__INT, GST_TYPE_TAG_LIST, 1, G_TYPE_INT);
989
  /**
990
991
   * GstPlayBin2::convert-frame
   * @playbin: a #GstPlayBin2
992
993
994
995
996
997
998
   * @caps: the target format of the frame
   *
   * Action signal to retrieve the currently playing video frame in the format
   * specified by @caps.
   * If @caps is %NULL, no conversion will be performed and this function is
   * equivalent to the #GstPlayBin::frame property.
   *
Wim Taymans's avatar
Wim Taymans committed
999
   * Returns: a #GstBuffer of the current video frame converted to #caps.
1000
1001
1002
1003
   * The caps on the buffer will describe the final layout of the buffer data.
   * %NULL is returned when no current buffer can be retrieved or when the
   * conversion failed.
   */
1004
  gst_play_bin_signals[SIGNAL_CONVERT_FRAME] =
1005
1006
1007
1008
      g_signal_new ("convert-frame", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstPlayBinClass, convert_frame), NULL, NULL,
      gst_play_marshal_BUFFER__BOXED, GST_TYPE_BUFFER, 1, GST_TYPE_CAPS);
1009

1010
1011
1012
1013
1014
  /**
   * GstPlayBin2::get-video-pad
   * @playbin: a #GstPlayBin2
   * @stream: a video stream number
   *
Wim Taymans's avatar
Wim Taymans committed
1015
   * Action signal to retrieve the stream-selector sinkpad for a specific
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
   * video stream.
   * This pad can be used for notifications of caps changes, stream-specific
   * queries, etc.
   *
   * Returns: a #GstPad, or NULL when the stream number does not exist.
   */
  gst_play_bin_signals[SIGNAL_GET_VIDEO_PAD] =
      g_signal_new ("get-video-pad", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstPlayBinClass, get_video_pad), NULL, NULL,
      gst_play_marshal_OBJECT__INT, GST_TYPE_PAD, 1, G_TYPE_INT);
  /**
   * GstPlayBin2::get-audio-pad
   * @playbin: a #GstPlayBin2
   * @stream: an audio stream number
   *
Wim Taymans's avatar
Wim Taymans committed
1032
   * Action signal to retrieve the stream-selector sinkpad for a specific
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
   * audio stream.
   * This pad can be used for notifications of caps changes, stream-specific
   * queries, etc.
   *
   * Returns: a #GstPad, or NULL when the stream number does not exist.
   */
  gst_play_bin_signals[SIGNAL_GET_AUDIO_PAD] =
      g_signal_new ("get-audio-pad", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstPlayBinClass, get_audio_pad), NULL, NULL,
      gst_play_marshal_OBJECT__INT, GST_TYPE_PAD, 1, G_TYPE_INT);
  /**
   * GstPlayBin2::get-text-pad
   * @playbin: a #GstPlayBin2
   * @stream: a text stream number
   *
Wim Taymans's avatar
Wim Taymans committed
1049
   * Action signal to retrieve the stream-selector sinkpad for a specific
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
   * text stream.
   * This pad can be used for notifications of caps changes, stream-specific
   * queries, etc.
   *
   * Returns: a #GstPad, or NULL when the stream number does not exist.
   */
  gst_play_bin_signals[SIGNAL_GET_TEXT_PAD] =
      g_signal_new ("get-text-pad", G_TYPE_FROM_CLASS (klass),
      G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION,
      G_STRUCT_OFFSET (GstPlayBinClass, get_text_pad), NULL, NULL,
      gst_play_marshal_OBJECT__INT, GST_TYPE_PAD, 1, G_TYPE_INT);

1062
1063
1064
1065
  klass->get_video_tags = gst_play_bin_get_video_tags;
  klass->get_audio_tags = gst_play_bin_get_audio_tags;
  klass->get_text_tags = gst_play_bin_get_text_tags;

1066
1067
  klass->convert_frame = gst_play_bin_convert_frame;

1068
1069
1070
1071
  klass->get_video_pad = gst_play_bin_get_video_pad;
  klass->get_audio_pad = gst_play_bin_get_audio_pad;
  klass->get_text_pad = gst_play_bin_get_text_pad;

1072
1073
1074
1075
  gst_element_class_set_details_simple (gstelement_klass,
      "Player Bin 2", "Generic/Bin/Player",
      "Autoplug and play media from an uri",
      "Wim Taymans <wim.taymans@gmail.com>");
Wim Taymans's avatar
Wim Taymans committed
1076
1077
1078

  gstelement_klass->change_state =
      GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
1079
  gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query);
Wim Taymans's avatar
Wim Taymans committed
1080
1081
1082
1083
1084
1085
1086
1087

  gstbin_klass->handle_message =
      GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
}

static void
init_group (GstPlayBin * playbin, GstSourceGroup * group)
{
1088
1089
  int n;

1090
1091
1092
1093
  /* store the array for the different channels */
  group->video_channels = g_ptr_array_new ();
  group->audio_channels = g_ptr_array_new ();
  group->text_channels = g_ptr_array_new ();
1094
  group->lock = g_mutex_new ();
1095
1096
  /* init selectors. The selector is found by finding the first prefix that
   * matches the media. */
Wim Taymans's avatar
Wim Taymans committed
1097
  group->playbin = playbin;
1098
1099
1100
  /* If you add any items to these lists, check that media_list[] is defined
   * above to be large enough to hold MAX(items)+1, so as to accomodate a
   * NULL terminator (set when the memory is zeroed on allocation) */
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
  group->selector[PLAYBIN_STREAM_AUDIO].media_list[0] = "audio/";
  group->selector[PLAYBIN_STREAM_AUDIO].type = GST_PLAY_SINK_TYPE_AUDIO;
  group->selector[PLAYBIN_STREAM_AUDIO].channels = group->audio_channels;
  group->selector[PLAYBIN_STREAM_VIDEO].media_list[0] = "video/";
  group->selector[PLAYBIN_STREAM_VIDEO].type = GST_PLAY_SINK_TYPE_VIDEO;
  group->selector[PLAYBIN_STREAM_VIDEO].channels = group->video_channels;
  group->selector[PLAYBIN_STREAM_TEXT].media_list[0] = "text/";
  group->selector[PLAYBIN_STREAM_TEXT].media_list[1] = "application/x-subtitle";
  group->selector[PLAYBIN_STREAM_TEXT].media_list[2] = "application/x-ssa";
  group->selector[PLAYBIN_STREAM_TEXT].media_list[3] = "application/x-ass";
  group->selector[PLAYBIN_STREAM_TEXT].media_list[4] = "video/x-dvd-subpicture";
  group->selector[PLAYBIN_STREAM_TEXT].media_list[5] = "subpicture/";
  group->selector[PLAYBIN_STREAM_TEXT].media_list[6] = "subtitle/";
  group->selector[PLAYBIN_STREAM_TEXT].get_media_caps =
      gst_subtitle_overlay_create_factory_caps;
  group->selector[PLAYBIN_STREAM_TEXT].type = GST_PLAY_SINK_TYPE_TEXT;
  group->selector[PLAYBIN_STREAM_TEXT].channels = group->text_channels;

  for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
1120
1121
1122
1123
    GstSourceSelect *select = &group->selector[n];
    select->sinkpad_delayed_event = NULL;
    select->sinkpad_data_probe = 0;
  }
Wim Taymans's avatar
Wim Taymans committed
1124
1125
}

1126
1127
1128
static void
free_group (GstPlayBin * playbin, GstSourceGroup * group)
{
1129
1130
  int n;

1131
  for (n = 0; n < PLAYBIN_STREAM_LAST; n++) {
1132
1133
1134
1135
1136
1137
1138
    GstSourceSelect *select = &group->selector[n];
    if (select->sinkpad && select->sinkpad_data_probe)
      gst_pad_remove_data_probe (select->sinkpad, select->sinkpad_data_probe);
    if (select->sinkpad_delayed_event)
      gst_event_unref (select->sinkpad_delayed_event);
  }

1139
  g_free (group->uri);
1140
  g_free (group->suburi);
1141
1142
1143
  g_ptr_array_free (group->video_channels, TRUE);
  g_ptr_array_free (group->audio_channels, TRUE);
  g_ptr_array_free (group->text_channels, TRUE);
1144

1145
  g_mutex_free (group->lock);
1146
1147
1148
  if (group->audio_sink) {
    if (group->audio_sink != playbin->audio_sink)
      gst_element_set_state (group->audio_sink, GST_STATE_NULL);
1149
    gst_object_unref (group->audio_sink);
1150
  }
1151
  group->audio_sink = NULL;
1152
1153
1154
  if (group->video_sink) {
    if (group->video_sink != playbin->video_sink)
      gst_element_set_state (group->video_sink, GST_STATE_NULL);
1155
    gst_object_unref (group->video_sink);
1156
  }
1157
  group->video_sink = NULL;
1158
1159
1160
1161

  g_list_free (group->stream_changed_pending);
  group->stream_changed_pending = NULL;

1162
1163
  if (group->stream_changed_pending_lock)
    g_mutex_free (group->stream_changed_pending_lock);
1164
  group->stream_changed_pending_lock = NULL;
1165
1166
}

1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
static void
notify_volume_cb (GObject * selector, GParamSpec * pspec, GstPlayBin * playbin)
{
  g_object_notify (G_OBJECT (playbin), "volume");
}

static void
notify_mute_cb (GObject * selector, GParamSpec * pspec, GstPlayBin * playbin)
{
  g_object_notify (G_OBJECT (playbin), "mute");
}

1179
/* Must be called with elements lock! */
Wim Taymans's avatar
Wim Taymans committed
1180
static void
1181
gst_play_bin_update_elements_list (GstPlayBin * playbin)
Wim Taymans's avatar
Wim Taymans committed
1182
{
1183
1184
  GList *res, *tmp;

1185
1186
1187
1188
  if (!playbin->elements ||
      playbin->elements_cookie !=
      gst_default_registry_get_feature_list_cookie ()) {
    if (playbin->elements)
1189
1190
1191
1192
1193
1194
1195
1196
      gst_plugin_feature_list_free (playbin->elements);
    res =
        gst_element_factory_list_get_elements
        (GST_ELEMENT_FACTORY_TYPE_DECODABLE, GST_RANK_MARGINAL);
    tmp =
        gst_element_factory_list_get_elements
        (GST_ELEMENT_FACTORY_TYPE_AUDIOVIDEO_SINKS, GST_RANK_MARGINAL);
    playbin->elements = g_list_concat (res, tmp);
1197
    playbin->elements =
1198
        g_list_sort (playbin->elements, gst_plugin_feature_rank_compare_func);
1199
1200
1201
    playbin->elements_cookie = gst_default_registry_get_feature_list_cookie ();
  }
}
1202

1203
1204
1205
static void
gst_play_bin_init (GstPlayBin * playbin)
{
1206
  g_static_rec_mutex_init (&playbin->lock);
1207
  playbin->dyn_lock = g_mutex_new ();
1208

1209
1210
1211
  /* assume we can create a selector */
  playbin->have_selector = TRUE;

Wim Taymans's avatar
Wim Taymans committed
1212
1213
1214
1215
1216
  /* init groups */
  playbin->curr_group = &playbin->groups[0];
  playbin->next_group = &playbin->groups[1];
  init_group (playbin, &playbin->groups[0]);
  init_group (playbin, &playbin->groups[1]);
1217
1218

  /* first filter out the interesting element factories */
1219
  playbin->elements_lock = g_mutex_new ();
1220

1221
1222
1223
1224
  /* add sink */
  playbin->playsink = g_object_new (GST_TYPE_PLAY_SINK, NULL);
  gst_bin_add (GST_BIN_CAST (playbin), GST_ELEMENT_CAST (playbin->playsink));
  gst_play_sink_set_flags (playbin->playsink, DEFAULT_FLAGS);
1225
1226
1227
1228
1229
  /* Connect to notify::volume and notify::mute signals for proxying */
  g_signal_connect (playbin->playsink, "notify::volume",
      G_CALLBACK (notify_volume_cb), playbin);
  g_signal_connect (playbin->playsink,