gstplaybin2.c 123 KB
Newer Older
Wim Taymans's avatar
Wim Taymans committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
/* GStreamer
 * Copyright (C) <2007> Wim Taymans <wim.taymans@gmail.com>
 *
 * 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
 *
23
 * Playbin2 provides a stand-alone everything-in-one abstraction for an
Wim Taymans's avatar
Wim Taymans committed
24
 * audio and/or video player.
25
 *
26
27
 * playbin2 is considered stable now. It is the prefered playback API now,
 * and replaces the old #playbin element, which is no longer supported.
28
 *
Wim Taymans's avatar
Wim Taymans committed
29
30
31
32
33
34
35
36
37
38
 * 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>
39
40
 * subtitle support for video files. Subtitles can be store in external
 * files.
Wim Taymans's avatar
Wim Taymans committed
41
42
 * </listitem>
 * <listitem>
43
 * stream selection between different video/audio/subtitles streams
Wim Taymans's avatar
Wim Taymans committed
44
45
46
47
48
49
50
51
52
53
54
 * </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>
55
 * volume control with mute option
Wim Taymans's avatar
Wim Taymans committed
56
57
 * </listitem>
 * </itemizedlist>
58
59
 *
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
60
61
 * <title>Usage</title>
 * <para>
62
 * A playbin2 element can be created just like any other element using
63
 * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin2:uri
Wim Taymans's avatar
Wim Taymans committed
64
65
 * 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
66
 *
Wim Taymans's avatar
Wim Taymans committed
67
68
69
70
 * 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.
71
 *
Wim Taymans's avatar
Wim Taymans committed
72
73
74
75
76
77
 * 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.
78
 *
Wim Taymans's avatar
Wim Taymans committed
79
80
81
 * 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
82
 * READY or NULL state, then the #GstPlayBin2:uri property should be set to the
83
84
 * new location and then playbin be set to PLAYING state again.
 *
Wim Taymans's avatar
Wim Taymans committed
85
86
87
88
89
90
91
 * 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.
92
 *
Wim Taymans's avatar
Wim Taymans committed
93
94
95
96
97
 * 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>
98
99
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
100
101
102
 * <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
103
 * #GstPlayBin2:audio-sink or #GstPlayBin2:video-sink property, playbin will use the autoaudiosink
Wim Taymans's avatar
Wim Taymans committed
104
105
106
107
 * 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.
108
 *
Wim Taymans's avatar
Wim Taymans committed
109
110
111
 * 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
112
113
 * #GstPlayBin2:audio-sink or #GstPlayBin2:video-sink property.
 *
Wim Taymans's avatar
Wim Taymans committed
114
115
116
 * 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
117
 * Multimedia System Selector configuration dialog.
118
 *
Wim Taymans's avatar
Wim Taymans committed
119
120
121
122
123
124
125
126
 * 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.
127
 *
Wim Taymans's avatar
Wim Taymans committed
128
129
130
131
 * 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>
132
133
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
134
135
136
137
 * <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).
138
 *
Wim Taymans's avatar
Wim Taymans committed
139
140
 * Other more specific meta information like width/height/framerate of video
 * streams or samplerate/number of channels of audio streams can be obtained
141
 * from the negotiated caps on the sink pads of the sinks.
Wim Taymans's avatar
Wim Taymans committed
142
 * </para>
143
144
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
145
146
147
148
149
150
151
152
153
 * <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):
154
 * |[
Wim Taymans's avatar
Wim Taymans committed
155
156
157
158
159
160
161
162
163
 * 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;
 *   }
 *   ...
 * }
164
 * ]|
Wim Taymans's avatar
Wim Taymans committed
165
166
167
168
 * 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).
169
170
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
171
172
173
 * <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
174
 * own, however. This can be done using the #GstXOverlay interface, which most
Wim Taymans's avatar
Wim Taymans committed
175
 * video sinks implement. See the documentation there for more details.
176
177
 * </refsect2>
 * <refsect2>
Wim Taymans's avatar
Wim Taymans committed
178
179
180
181
182
183
184
185
186
 * <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.
187
188
 * </refsect2>
 * <refsect2>
189
190
191
192
193
194
195
196
197
198
 * <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
199
 * <title>Examples</title>
200
 * |[
Wim Taymans's avatar
Wim Taymans committed
201
 * gst-launch -v playbin uri=file:///path/to/somefile.avi
202
 * ]| This will play back the given AVI video file, given that the video and
Wim Taymans's avatar
Wim Taymans committed
203
204
205
206
 * 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.
207
 * |[
Wim Taymans's avatar
Wim Taymans committed
208
 * gst-launch -v playbin uri=cdda://4
209
 * ]| This will play back track 4 on an audio CD in your disc drive (assuming
Wim Taymans's avatar
Wim Taymans committed
210
 * the drive is detected automatically by the plugin).
211
 * |[
Wim Taymans's avatar
Wim Taymans committed
212
 * gst-launch -v playbin uri=dvd://1
213
 * ]| This will play back title 1 of a DVD in your disc drive (assuming
Wim Taymans's avatar
Wim Taymans committed
214
215
216
217
218
219
220
221
222
223
224
225
226
 * 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>
227
#include <gst/interfaces/streamvolume.h>
Wim Taymans's avatar
Wim Taymans committed
228

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

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;

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

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

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

270
271
272
273
#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)))

Wim Taymans's avatar
Wim Taymans committed
274
275
276
277
278
279
/* a structure to hold the objects for decoding a uri and the subtitle uri
 */
struct _GstSourceGroup
{
  GstPlayBin *playbin;

280
281
  GMutex *lock;

Wim Taymans's avatar
Wim Taymans committed
282
  gboolean valid;               /* the group has valid info to start playback */
283
  gboolean active;              /* the group is active */
Wim Taymans's avatar
Wim Taymans committed
284
285
286
287
288
289
290

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

291
292
293
294
  GPtrArray *video_channels;    /* links to selector pads */
  GPtrArray *audio_channels;    /* links to selector pads */
  GPtrArray *text_channels;     /* links to selector pads */

295
296
297
  GstElement *audio_sink;       /* autoplugged audio and video sinks */
  GstElement *video_sink;

Wim Taymans's avatar
Wim Taymans committed
298
299
300
  /* uridecodebins for uri and subtitle uri */
  GstElement *uridecodebin;
  GstElement *suburidecodebin;
301
  gint pending;
302
  gboolean sub_pending;
Wim Taymans's avatar
Wim Taymans committed
303

304
305
306
307
308
309
310
  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;
311
  gulong autoplug_continue_id;
312
313
314
315

  gulong sub_pad_added_id;
  gulong sub_pad_removed_id;
  gulong sub_no_more_pads_id;
316
  gulong sub_autoplug_continue_id;
317

318
319
  GMutex *stream_changed_pending_lock;
  GList *stream_changed_pending;
320

Wim Taymans's avatar
Wim Taymans committed
321
322
323
324
  /* selectors for different streams */
  GstSourceSelect selector[GST_PLAY_SINK_TYPE_LAST];
};

325
326
327
#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)))
328

329
330
331
332
333
/* 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 */
334
335
336
337
338
339
340
341
342
#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;                                         \
  }                                                     \
343
344
345
346
347
348
} G_STMT_END

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

349
350
351
352
353
/**
 * GstPlayBin2:
 *
 * playbin element structure
 */
Wim Taymans's avatar
Wim Taymans committed
354
355
356
357
struct _GstPlayBin
{
  GstPipeline parent;

358
  GStaticRecMutex lock;         /* to protect group switching */
359

Wim Taymans's avatar
Wim Taymans committed
360
361
362
363
364
365
366
  /* 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) */
367
368
369
  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
370

371
372
373
  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
374
375
  /* our play sink */
  GstPlaySink *playsink;
376

377
378
379
  /* the last activated source */
  GstElement *source;

380
381
382
383
384
  /* lock protecting dynamic adding/removing */
  GMutex *dyn_lock;
  /* if we are shutting down or not */
  gint shutdown;

385
  GMutex *elements_lock;
386
  guint32 elements_cookie;
387
  GList *elements;              /* factories we can use for selecting elements */
388
389
390
391

  gboolean have_selector;       /* set to FALSE when we fail to create an
                                 * input-selector, so that we only post a
                                 * warning once */
392
393
394
395

  GstElement *audio_sink;       /* configured audio sink, or NULL      */
  GstElement *video_sink;       /* configured video sink, or NULL      */
  GstElement *text_sink;        /* configured text sink, or NULL       */
396
397
398
399
400
401
402

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

  guint64 ring_buffer_max_size; /* 0 means disabled */
Wim Taymans's avatar
Wim Taymans committed
405
406
407
408
409
410
};

struct _GstPlayBinClass
{
  GstPipelineClass parent_class;

411
412
  /* notify app that the current uri finished decoding and it is possible to
   * queue a new one for gapless playback */
413
414
  void (*about_to_finish) (GstPlayBin * playbin);

415
  /* notify app that number of audio/video/text streams changed */
416
417
418
419
  void (*video_changed) (GstPlayBin * playbin);
  void (*audio_changed) (GstPlayBin * playbin);
  void (*text_changed) (GstPlayBin * playbin);

420
421
422
423
424
  /* 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);

425
  /* get audio/video/text tags for a stream */
426
427
428
  GstTagList *(*get_video_tags) (GstPlayBin * playbin, gint stream);
  GstTagList *(*get_audio_tags) (GstPlayBin * playbin, gint stream);
  GstTagList *(*get_text_tags) (GstPlayBin * playbin, gint stream);
429
430
431

  /* get the last video frame and convert it to the given caps */
  GstBuffer *(*convert_frame) (GstPlayBin * playbin, GstCaps * caps);
432
433
434
435
436

  /* 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
437
438
439
};

/* props */
440
441
442
443
#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
444
                                  GST_PLAY_FLAG_SOFT_VOLUME
445
446
447
448
449
450
451
452
453
454
#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
455
#define DEFAULT_TEXT_SINK         NULL
456
#define DEFAULT_VOLUME            1.0
457
#define DEFAULT_MUTE              FALSE
458
459
460
#define DEFAULT_FRAME             NULL
#define DEFAULT_FONT_DESC         NULL
#define DEFAULT_CONNECTION_SPEED  0
461
462
#define DEFAULT_BUFFER_DURATION   -1
#define DEFAULT_BUFFER_SIZE       -1
463
#define DEFAULT_RING_BUFFER_MAX_SIZE 0
464

Wim Taymans's avatar
Wim Taymans committed
465
466
467
468
469
470
enum
{
  PROP_0,
  PROP_URI,
  PROP_SUBURI,
  PROP_SOURCE,
471
472
  PROP_FLAGS,
  PROP_N_VIDEO,
Wim Taymans's avatar
Wim Taymans committed
473
  PROP_CURRENT_VIDEO,
474
  PROP_N_AUDIO,
Wim Taymans's avatar
Wim Taymans committed
475
  PROP_CURRENT_AUDIO,
476
  PROP_N_TEXT,
Wim Taymans's avatar
Wim Taymans committed
477
478
479
480
481
  PROP_CURRENT_TEXT,
  PROP_SUBTITLE_ENCODING,
  PROP_AUDIO_SINK,
  PROP_VIDEO_SINK,
  PROP_VIS_PLUGIN,
482
  PROP_TEXT_SINK,
Wim Taymans's avatar
Wim Taymans committed
483
  PROP_VOLUME,
484
  PROP_MUTE,
Wim Taymans's avatar
Wim Taymans committed
485
486
  PROP_FRAME,
  PROP_FONT_DESC,
487
488
  PROP_CONNECTION_SPEED,
  PROP_BUFFER_SIZE,
489
  PROP_BUFFER_DURATION,
Wim Taymans's avatar
Wim Taymans committed
490
  PROP_AV_OFFSET,
491
  PROP_RING_BUFFER_MAX_SIZE,
492
  PROP_LAST
Wim Taymans's avatar
Wim Taymans committed
493
494
495
496
497
498
};

/* signals */
enum
{
  SIGNAL_ABOUT_TO_FINISH,
499
  SIGNAL_CONVERT_FRAME,
500
501
502
  SIGNAL_VIDEO_CHANGED,
  SIGNAL_AUDIO_CHANGED,
  SIGNAL_TEXT_CHANGED,
503
504
505
  SIGNAL_VIDEO_TAGS_CHANGED,
  SIGNAL_AUDIO_TAGS_CHANGED,
  SIGNAL_TEXT_TAGS_CHANGED,
506
507
508
  SIGNAL_GET_VIDEO_TAGS,
  SIGNAL_GET_AUDIO_TAGS,
  SIGNAL_GET_TEXT_TAGS,
509
510
511
  SIGNAL_GET_VIDEO_PAD,
  SIGNAL_GET_AUDIO_PAD,
  SIGNAL_GET_TEXT_PAD,
512
  SIGNAL_SOURCE_SETUP,
Wim Taymans's avatar
Wim Taymans committed
513
514
515
516
  LAST_SIGNAL
};

static void gst_play_bin_class_init (GstPlayBinClass * klass);
517
static void gst_play_bin_init (GstPlayBin * playbin);
518
static void gst_play_bin_finalize (GObject * object);
Wim Taymans's avatar
Wim Taymans committed
519
520
521
522
523
524
525
526
527
528

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);
529
static gboolean gst_play_bin_query (GstElement * element, GstQuery * query);
Wim Taymans's avatar
Wim Taymans committed
530

531
static GstTagList *gst_play_bin_get_video_tags (GstPlayBin * playbin,
532
    gint stream);
533
static GstTagList *gst_play_bin_get_audio_tags (GstPlayBin * playbin,
534
    gint stream);
535
static GstTagList *gst_play_bin_get_text_tags (GstPlayBin * playbin,
536
537
    gint stream);

538
539
540
static GstBuffer *gst_play_bin_convert_frame (GstPlayBin * playbin,
    GstCaps * caps);

541
542
543
544
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);

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

547
548
549
550
static void no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group);
static void pad_removed_cb (GstElement * decodebin, GstPad * pad,
    GstSourceGroup * group);

551
552
553
554
555
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
556
557
558
559
static GstElementClass *parent_class;

static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };

560
561
562
563
564
565
#define REMOVE_SIGNAL(obj,id)            \
if (id) {                                \
  g_signal_handler_disconnect (obj, id); \
  id = 0;                                \
}

Wim Taymans's avatar
Wim Taymans committed
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
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
    };
584
585
586
    static const GInterfaceInfo svol_info = {
      NULL, NULL, NULL
    };
Wim Taymans's avatar
Wim Taymans committed
587
588
589

    gst_play_bin_type = g_type_register_static (GST_TYPE_PIPELINE,
        "GstPlayBin2", &gst_play_bin_info, 0);
590
591
592

    g_type_add_interface_static (gst_play_bin_type, GST_TYPE_STREAM_VOLUME,
        &svol_info);
Wim Taymans's avatar
Wim Taymans committed
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
  }

  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;

614
  gobject_klass->finalize = gst_play_bin_finalize;
Wim Taymans's avatar
Wim Taymans committed
615

616
  /**
617
   * GstPlayBin2:uri
618
619
620
621
   *
   * 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
622
623
  g_object_class_install_property (gobject_klass, PROP_URI,
      g_param_spec_string ("uri", "URI", "URI of the media to play",
624
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
625
626

  /**
627
   * GstPlayBin2:suburi
628
629
630
631
   *
   * 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
632
633
  g_object_class_install_property (gobject_klass, PROP_SUBURI,
      g_param_spec_string ("suburi", ".sub-URI", "Optional URI of a subtitle",
634
          NULL, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
635
636
637

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

640
  /**
641
   * GstPlayBin2:flags
642
643
644
   *
   * Control the behaviour of playbin.
   */
645
646
  g_object_class_install_property (gobject_klass, PROP_FLAGS,
      g_param_spec_flags ("flags", "Flags", "Flags to control behaviour",
647
648
          GST_TYPE_PLAY_FLAGS, DEFAULT_FLAGS,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
649
650

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

  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, "
713
714
          "ISO-8859-15 will be assumed.", NULL,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
715
716
717
718

  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)",
719
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
720
721
722
  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)",
723
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
724
725
  g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
      g_param_spec_object ("vis-plugin", "Vis plugin",
726
          "the visualization element to use (NULL = default)",
727
          GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
728
729
730
731
  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));
732

733
734
735
736
737
738
739
  /**
   * 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
740
  g_object_class_install_property (gobject_klass, PROP_VOLUME,
741
      g_param_spec_double ("volume", "Volume", "The audio volume, 1.0=100%",
742
743
          0.0, VOLUME_MAX_DOUBLE, 1.0,
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
744
745
746
  g_object_class_install_property (gobject_klass, PROP_MUTE,
      g_param_spec_boolean ("mute", "Mute",
          "Mute the audio channel without changing the volume", FALSE,
747
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
748

749
  /**
750
751
   * GstPlayBin2:frame:
   * @playbin: a #GstPlayBin2
752
   *
753
   * Get the currently rendered or prerolled frame in the video sink.
754
755
   * The #GstCaps on the buffer will describe the format of the buffer.
   */
Wim Taymans's avatar
Wim Taymans committed
756
757
758
  g_object_class_install_property (gobject_klass, PROP_FRAME,
      gst_param_spec_mini_object ("frame", "Frame",
          "The last frame (NULL = no video available)",
759
          GST_TYPE_BUFFER, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
760
761
762
763
  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 "
764
765
          "to be used for subtitle rendering", NULL,
          G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
Wim Taymans's avatar
Wim Taymans committed
766
767
768
769

  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)",
770
          0, G_MAXUINT / 1000, DEFAULT_CONNECTION_SPEED,
771
          G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
772
773
774
775
776
777
778
779
780
781
782

  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
783
784
785
786
787
788
789
790
791
792
793
794
795
796
  /**
   * 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));
797

798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
  /**
   * 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
813
  /**
814
815
   * GstPlayBin2::about-to-finish
   * @playbin: a #GstPlayBin2
Wim Taymans's avatar
Wim Taymans committed
816
817
   *
   * This signal is emitted when the current uri is about to finish. You can
818
   * set the uri and suburi to make sure that playback continues.
Wim Taymans's avatar
Wim Taymans committed
819
820
821
822
823
824
825
   */
  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);

826
  /**
827
828
   * GstPlayBin2::video-changed
   * @playbin: a #GstPlayBin2
829
   *
830
   * This signal is emitted whenever the number or order of the video
831
832
833
834
835
836
837
838
839
   * 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);
  /**
840
841
   * GstPlayBin2::audio-changed
   * @playbin: a #GstPlayBin2
842
   *
843
   * This signal is emitted whenever the number or order of the audio
844
845
846
847
848
849
850
851
852
   * 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);
  /**
853
854
   * GstPlayBin2::text-changed
   * @playbin: a #GstPlayBin2
855
   *
856
   * This signal is emitted whenever the number or order of the text
857
858
859
860
861
862
863
864
865
   * 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);

866
867
868
869
870
871
872
873
874
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
  /**
   * 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);

914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
  /**
   * 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);

932
  /**
933
934
   * GstPlayBin2::get-video-tags
   * @playbin: a #GstPlayBin2
935
936
937
938
939
940
941
942
943
944
945
946
947
948
   * @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);
  /**
949
950
   * GstPlayBin2::get-audio-tags
   * @playbin: a #GstPlayBin2
951
952
953
954
955
956
957
958
959
960
961
962
963
964
   * @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);
  /**
965
966
   * GstPlayBin2::get-text-tags
   * @playbin: a #GstPlayBin2
967
968
969
970
971
972
973
974
975
976
977
978
979
   * @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);
980
  /**
981
982
   * GstPlayBin2::convert-frame
   * @playbin: a #GstPlayBin2
983
984
985
986
987
988
989
   * @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
990
   * Returns: a #GstBuffer of the current video frame converted to #caps.
991
992
993
994
   * 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.
   */
995
  gst_play_bin_signals[SIGNAL_CONVERT_FRAME] =
996
997
998
999
      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);
1000

1001
1002
1003
1004
1005
  /**
   * GstPlayBin2::get-video-pad
   * @playbin: a #GstPlayBin2
   * @stream: a video stream number
   *
Wim Taymans's avatar
Wim Taymans committed
1006
   * Action signal to retrieve the stream-selector sinkpad for a specific
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
   * 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
1023
   * Action signal to retrieve the stream-selector sinkpad for a specific
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
   * 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
1040
   * Action signal to retrieve the stream-selector sinkpad for a specific
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
   * 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);

1053
1054
1055
1056
  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;

1057
1058
  klass->convert_frame = gst_play_bin_convert_frame;

1059
1060
1061
1062
  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;

1063
1064
1065
1066
  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
1067
1068
1069

  gstelement_klass->change_state =
      GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
1070
  gstelement_klass->query = GST_DEBUG_FUNCPTR (gst_play_bin_query);
Wim Taymans's avatar
Wim Taymans committed
1071
1072
1073
1074
1075
1076
1077
1078

  gstbin_klass->handle_message =
      GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
}

static void
init_group (GstPlayBin * playbin, GstSourceGroup * group)
{
1079
1080
  int n;

1081
1082
1083
1084
  /* 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 ();
1085
  group->lock = g_mutex_new ();
1086
1087
  /* init selectors. The selector is found by finding the first prefix that
   * matches the media. */
Wim Taymans's avatar
Wim Taymans committed
1088
  group->playbin = playbin;
1089
1090
1091
1092
  /* 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) */
  group->selector[0].media_list[0] = "audio/x-raw-";
1093
  group->selector[0].type = GST_PLAY_SINK_TYPE_AUDIO_RAW;
1094
  group->selector[0].channels = group->audio_channels;
1095
  group->selector[1].media_list[0] = "audio/";
1096
  group->selector[1].type = GST_PLAY_SINK_TYPE_AUDIO;
1097
  group->selector[1].channels = group->audio_channels;
1098
  group->selector[2].media_list[0] = "text/";
1099
1100
1101
1102
1103
1104
  group->selector[2].media_list[1] = "application/x-subtitle";
  group->selector[2].media_list[2] = "application/x-ssa";
  group->selector[2].media_list[3] = "application/x-ass";
  group->selector[2].media_list[4] = "video/x-dvd-subpicture";
  group->selector[2].media_list[5] = "subpicture/";
  group->selector[2].media_list[6] = "subtitle/";
1105
  group->selector[2].get_media_caps = gst_subtitle_overlay_create_factory_caps;
1106
1107
1108
1109
1110
  group->selector[2].type = GST_PLAY_SINK_TYPE_TEXT;
  group->selector[2].channels = group->text_channels;
  group->selector[3].media_list[0] = "video/x-raw-";
  group->selector[3].type = GST_PLAY_SINK_TYPE_VIDEO_RAW;
  group->selector[3].channels = group->video_channels;
1111
  group->selector[4].media_list[0] = "video/";
1112
1113
  group->selector[4].type = GST_PLAY_SINK_TYPE_VIDEO;
  group->selector[4].channels = group->video_channels;
1114
1115
1116
1117
1118
1119

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

1122
1123
1124
static void
free_group (GstPlayBin * playbin, GstSourceGroup * group)
{
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
  int n;

  for (n = 0; n < GST_PLAY_SINK_TYPE_LAST; n++) {
    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);
  }

1135
  g_free (group->uri);
1136
  g_free (group->suburi);
1137
1138
1139
  g_ptr_array_free (group->video_channels, TRUE);
  g_ptr_array_free (group->audio_channels, TRUE);
  g_ptr_array_free (group->text_channels, TRUE);
1140

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

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

1158
1159
  if (group->stream_changed_pending_lock)
    g_mutex_free (group->stream_changed_pending_lock);
1160
  group->stream_changed_pending_lock = NULL;
1161
1162
}

1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
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");
}

1175
/* Must be called with elements lock! */
Wim Taymans's avatar
Wim Taymans committed
1176
static void
1177
gst_play_bin_update_elements_list (GstPlayBin * playbin)
Wim Taymans's avatar
Wim Taymans committed
1178
{
1179
1180
  GList *res, *tmp;

1181
1182
1183
1184
  if (!playbin->elements ||
      playbin->elements_cookie !=
      gst_default_registry_get_feature_list_cookie ()) {
    if (playbin->elements)
1185
1186
1187
1188
1189
1190
1191
1192
      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);
1193
    playbin->elements =
1194
        g_list_sort (playbin->elements, gst_plugin_feature_rank_compare_func);
1195
1196
1197
    playbin->elements_cookie = gst_default_registry_get_feature_list_cookie ();
  }
}
1198

1199
1200
1201
static void
gst_play_bin_init (GstPlayBin * playbin)
{
1202
  g_static_rec_mutex_init (&playbin->lock);
1203
  playbin->dyn_lock = g_mutex_new ();
1204

1205
1206
1207
  /* assume we can create a selector */
  playbin->have_selector = TRUE;

Wim Taymans's avatar
Wim Taymans committed
1208
1209
1210
1211
1212
  /* 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]);
1213
1214

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

1217
1218
1219
1220
  /* 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);