gst_plugins_base/gst/playback/gstplaybin.c
changeset 0 0e761a78d257
child 8 4a7fac7dd34a
equal deleted inserted replaced
-1:000000000000 0:0e761a78d257
       
     1 /* GStreamer
       
     2  * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
       
     3  *
       
     4  * This library is free software; you can redistribute it and/or
       
     5  * modify it under the terms of the GNU Library General Public
       
     6  * License as published by the Free Software Foundation; either
       
     7  * version 2 of the License, or (at your option) any later version.
       
     8  *
       
     9  * This library is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
       
    12  * Library General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU Library General Public
       
    15  * License along with this library; if not, write to the
       
    16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
       
    17  * Boston, MA 02111-1307, USA.
       
    18  */
       
    19 
       
    20 /**
       
    21  * SECTION:element-playbin
       
    22  *
       
    23  * <refsect2>
       
    24  * <para>
       
    25  * Playbin provides a stand-alone everything-in-one abstraction for an
       
    26  * audio and/or video player.
       
    27  * </para>
       
    28  * <para>
       
    29  * It can handle both audio and video files and features
       
    30  * <itemizedlist>
       
    31  * <listitem>
       
    32  * automatic file type recognition and based on that automatic
       
    33  * selection and usage of the right audio/video/subtitle demuxers/decoders
       
    34  * </listitem>
       
    35  * <listitem>
       
    36  * visualisations for audio files
       
    37  * </listitem>
       
    38  * <listitem>
       
    39  * subtitle support for video files
       
    40  * </listitem>
       
    41  * <listitem>
       
    42  * stream selection between different audio/subtitles streams
       
    43  * </listitem>
       
    44  * <listitem>
       
    45  * meta info (tag) extraction
       
    46  * </listitem>
       
    47  * <listitem>
       
    48  * easy access to the last video frame
       
    49  * </listitem>
       
    50  * <listitem>
       
    51  * buffering when playing streams over a network
       
    52  * </listitem>
       
    53  * <listitem>
       
    54  * volume control
       
    55  * </listitem>
       
    56  * </itemizedlist>
       
    57  * </para>
       
    58  * <title>Usage</title>
       
    59  * <para>
       
    60  * A playbin element can be created just like any other element using
       
    61  * gst_element_factory_make(). The file/URI to play should be set via the "uri"
       
    62  * property. This must be an absolute URI, relative file paths are not allowed.
       
    63  * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
       
    64  * </para>
       
    65  * <para>
       
    66  * Playbin is a #GstPipeline. It will notify the application of everything
       
    67  * that's happening (errors, end of stream, tags found, state changes, etc.)
       
    68  * by posting messages on its #GstBus. The application needs to watch the
       
    69  * bus.
       
    70  * </para>
       
    71  * <para>
       
    72  * Playback can be initiated by setting the element to PLAYING state using
       
    73  * gst_element_set_state(). Note that the state change will take place in
       
    74  * the background in a separate thread, when the function returns playback
       
    75  * is probably not happening yet and any errors might not have occured yet.
       
    76  * Applications using playbin should ideally be written to deal with things
       
    77  * completely asynchroneous.
       
    78  * </para>
       
    79  * <para>
       
    80  * When playback has finished (an EOS message has been received on the bus)
       
    81  * or an error has occured (an ERROR message has been received on the bus) or
       
    82  * the user wants to play a different track, playbin should be set back to
       
    83  * READY or NULL state, then the "uri" property should be set to the new
       
    84  * location and then playbin be set to PLAYING state again.
       
    85  * </para>
       
    86  * <para>
       
    87  * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
       
    88  * on the playbin element. Again, the seek will not be executed
       
    89  * instantaneously, but will be done in a background thread. When the seek
       
    90  * call returns the seek will most likely still be in process. An application
       
    91  * may wait for the seek to finish (or fail) using gst_element_get_state() with
       
    92  * -1 as the timeout, but this will block the user interface and is not
       
    93  * recommended at all.
       
    94  * </para>
       
    95  * <para>
       
    96  * Applications may query the current position and duration of the stream
       
    97  * via gst_element_query_position() and gst_element_query_duration() and
       
    98  * setting the format passed to GST_FORMAT_TIME. If the query was successful,
       
    99  * the duration or position will have been returned in units of nanoseconds.
       
   100  * </para>
       
   101  * <title>Advanced Usage: specifying the audio and video sink</title>
       
   102  * <para>
       
   103  * By default, if no audio sink or video sink has been specified via the
       
   104  * "audio-sink" or "video-sink" property, playbin will use the autoaudiosink
       
   105  * and autovideosink elements to find the first-best available output method.
       
   106  * This should work in most cases, but is not always desirable. Often either
       
   107  * the user or application might want to specify more explicitly what to use
       
   108  * for audio and video output.
       
   109  * </para>
       
   110  * <para>
       
   111  * If the application wants more control over how audio or video should be
       
   112  * output, it may create the audio/video sink elements itself (for example
       
   113  * using gst_element_factory_make()) and provide them to playbin using the
       
   114  * "audio-sink" or "video-sink" property.
       
   115  * </para>
       
   116  * <para>
       
   117  * GNOME-based applications, for example, will usually want to create
       
   118  * gconfaudiosink and gconfvideosink elements and make playbin use those,
       
   119  * so that output happens to whatever the user has configured in the GNOME
       
   120  * Multimedia System Selector confinguration dialog.
       
   121  * </para>
       
   122  * <para>
       
   123  * The sink elements do not necessarily need to be ready-made sinks. It is
       
   124  * possible to create container elements that look like a sink to playbin,
       
   125  * but in reality contain a number of custom elements linked together. This
       
   126  * can be achieved by creating a #GstBin and putting elements in there and
       
   127  * linking them, and then creating a sink #GstGhostPad for the bin and pointing
       
   128  * it to the sink pad of the first element within the bin. This can be used
       
   129  * for a number of purposes, for example to force output to a particular
       
   130  * format or to modify or observe the data before it is output.
       
   131  * </para>
       
   132  * <para>
       
   133  * It is also possible to 'suppress' audio and/or video output by using
       
   134  * 'fakesink' elements (or capture it from there using the fakesink element's
       
   135  * "handoff" signal, which, nota bene, is fired from the streaming thread!).
       
   136  * </para>
       
   137  * <title>Retrieving Tags and Other Meta Data</title>
       
   138  * <para>
       
   139  * Most of the common meta data (artist, title, etc.) can be retrieved by
       
   140  * watching for TAG messages on the pipeline's bus (see above).
       
   141  * </para>
       
   142  * <para>
       
   143  * Other more specific meta information like width/height/framerate of video
       
   144  * streams or samplerate/number of channels of audio streams can be obtained
       
   145  * using the "stream-info" property, which will return a GList of stream info
       
   146  * objects, one for each stream. These are opaque objects that can only be
       
   147  * accessed via the standard GObject property interface, ie. g_object_get().
       
   148  * Each stream info object has the following properties:
       
   149  * <itemizedlist>
       
   150  * <listitem>"object" (GstObject) (the decoder source pad usually)</listitem>
       
   151  * <listitem>"type" (enum) (if this is an audio/video/subtitle stream)</listitem>
       
   152  * <listitem>"decoder" (string) (name of decoder used to decode this stream)</listitem>
       
   153  * <listitem>"mute" (boolean) (to mute or unmute this stream)</listitem>
       
   154  * <listitem>"caps" (GstCaps) (caps of the decoded stream)</listitem>
       
   155  * <listitem>"language-code" (string) (ISO-639 language code for this stream, mostly used for audio/subtitle streams)</listitem>
       
   156  * <listitem>"codec" (string) (format this stream was encoded in)</listitem>
       
   157  * </itemizedlist>
       
   158  * Stream information from the stream-info properties is best queried once
       
   159  * playbin has changed into PAUSED or PLAYING state (which can be detected
       
   160  * via a state-changed message on the bus where old_state=READY and
       
   161  * new_state=PAUSED), since before that the list might not be complete yet or
       
   162  * not contain all available information (like language-codes).
       
   163  * </para>
       
   164  * <title>Buffering</title>
       
   165  * <para>
       
   166  * Playbin handles buffering automatically for the most part, but applications
       
   167  * need to handle parts of the buffering process as well. Whenever playbin is
       
   168  * buffering, it will post BUFFERING messages on the bus with a percentage
       
   169  * value that shows the progress of the buffering process. Applications need
       
   170  * to set playbin to PLAYING or PAUSED state in response to these messages.
       
   171  * They may also want to convey the buffering progress to the user in some
       
   172  * way. Here is how to extract the percentage information from the message
       
   173  * (requires GStreamer >= 0.10.11):
       
   174  * </para>
       
   175  * <para>
       
   176  * <programlisting>
       
   177  * switch (GST_MESSAGE_TYPE (msg)) {
       
   178  *   case GST_MESSAGE_BUFFERING: {
       
   179  *     gint percent = 0;
       
   180  *     gst_message_parse_buffering (msg, &amp;percent);
       
   181  *     g_print ("Buffering (%%u percent done)", percent);
       
   182  *     break;
       
   183  *   }
       
   184  *   ...
       
   185  * }
       
   186  * </programlisting>
       
   187  * Note that applications should keep/set the pipeline in the PAUSED state when
       
   188  * a BUFFERING message is received with a buffer percent value < 100 and set
       
   189  * the pipeline back to PLAYING state when a BUFFERING message with a value
       
   190  * of 100 percent is received (if PLAYING is the desired state, that is).
       
   191  * </para>
       
   192  * <title>Embedding the video window in your application</title>
       
   193  * <para>
       
   194  * By default, playbin (or rather the video sinks used) will create their own
       
   195  * window. Applications will usually want to force output to a window of their
       
   196  * own, however. This can be done using the GstXOverlay interface, which most
       
   197  * video sinks implement. See the documentation there for more details.
       
   198  * </para>
       
   199  * <title>Specifying which CD/DVD device to use</title>
       
   200  * <para>
       
   201  * The device to use for CDs/DVDs needs to be set on the source element
       
   202  * playbin creates before it is opened. The only way to do this at the moment
       
   203  * is to connect to playbin's "notify::source" signal, which will be emitted
       
   204  * by playbin when it has created the source element for a particular URI.
       
   205  * In the signal callback you can check if the source element has a "device"
       
   206  * property and set it appropriately. In future ways might be added to specify
       
   207  * the device as part of the URI, but at the time of writing this is not
       
   208  * possible yet.
       
   209  * </para>
       
   210  * <title>Examples</title>
       
   211  * <para>
       
   212  * Here is a simple pipeline to play back a video or audio file:
       
   213  * <programlisting>
       
   214  * gst-launch -v playbin uri=file:///path/to/somefile.avi
       
   215  * </programlisting>
       
   216  * This will play back the given AVI video file, given that the video and
       
   217  * audio decoders required to decode the content are installed. Since no
       
   218  * special audio sink or video sink is supplied (not possible via gst-launch),
       
   219  * playbin will try to find a suitable audio and video sink automatically
       
   220  * using the autoaudiosink and autovideosink elements.
       
   221  * </para>
       
   222  * <para>
       
   223  * Here is a another pipeline to play track 4 of an audio CD:
       
   224  * <programlisting>
       
   225  * gst-launch -v playbin uri=cdda://4
       
   226  * </programlisting>
       
   227  * This will play back track 4 on an audio CD in your disc drive (assuming
       
   228  * the drive is detected automatically by the plugin).
       
   229  * </para>
       
   230  * <para>
       
   231  * Here is a another pipeline to play title 1 of a DVD:
       
   232  * <programlisting>
       
   233  * gst-launch -v playbin uri=dvd://1
       
   234  * </programlisting>
       
   235  * This will play back title 1 of a DVD in your disc drive (assuming
       
   236  * the drive is detected automatically by the plugin).
       
   237  * </para>
       
   238  * </refsect2>
       
   239  */
       
   240 
       
   241 #ifdef HAVE_CONFIG_H
       
   242 #include "config.h"
       
   243 #endif
       
   244 
       
   245 #include <string.h>
       
   246 #include <gst/gst.h>
       
   247 
       
   248 #include <gst/gst-i18n-plugin.h>
       
   249 #include <gst/pbutils/pbutils.h>
       
   250 
       
   251 #include "gstplaybasebin.h"
       
   252 
       
   253 #ifdef __SYMBIAN32__
       
   254 #include <glib_global.h>
       
   255 #endif
       
   256 
       
   257 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
       
   258 #define GST_CAT_DEFAULT gst_play_bin_debug
       
   259 
       
   260 #define GST_TYPE_PLAY_BIN               (gst_play_bin_get_type())
       
   261 #define GST_PLAY_BIN(obj)               (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_PLAY_BIN,GstPlayBin))
       
   262 #define GST_PLAY_BIN_CLASS(klass)       (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_PLAY_BIN,GstPlayBinClass))
       
   263 #define GST_IS_PLAY_BIN(obj)            (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_PLAY_BIN))
       
   264 #define GST_IS_PLAY_BIN_CLASS(klass)    (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_PLAY_BIN))
       
   265 
       
   266 #define VOLUME_MAX_DOUBLE 10.0
       
   267 
       
   268 typedef struct _GstPlayBin GstPlayBin;
       
   269 typedef struct _GstPlayBinClass GstPlayBinClass;
       
   270 
       
   271 struct _GstPlayBin
       
   272 {
       
   273   GstPlayBaseBin parent;
       
   274 
       
   275   /* the configurable elements */
       
   276   GstElement *fakesink;
       
   277   GstElement *audio_sink;
       
   278   GstElement *video_sink;
       
   279   GstElement *visualisation;
       
   280   GstElement *pending_visualisation;
       
   281   GstElement *volume_element;
       
   282   GstElement *textoverlay_element;
       
   283   gfloat volume;
       
   284 
       
   285   /* these are the currently active sinks */
       
   286   GList *sinks;
       
   287 
       
   288   /* the last captured frame for snapshots */
       
   289   GstBuffer *frame;
       
   290 
       
   291   /* our cache for the sinks */
       
   292   GHashTable *cache;
       
   293 
       
   294   /* font description */
       
   295   gchar *font_desc;
       
   296 
       
   297   /* indication if the pipeline is live */
       
   298   gboolean is_live;
       
   299 };
       
   300 
       
   301 struct _GstPlayBinClass
       
   302 {
       
   303   GstPlayBaseBinClass parent_class;
       
   304 };
       
   305 
       
   306 /* props */
       
   307 enum
       
   308 {
       
   309   ARG_0,
       
   310   ARG_AUDIO_SINK,
       
   311   ARG_VIDEO_SINK,
       
   312   ARG_VIS_PLUGIN,
       
   313   ARG_VOLUME,
       
   314   ARG_FRAME,
       
   315   ARG_FONT_DESC
       
   316 };
       
   317 
       
   318 /* signals */
       
   319 enum
       
   320 {
       
   321   LAST_SIGNAL
       
   322 };
       
   323 
       
   324 static void gst_play_bin_class_init (GstPlayBinClass * klass);
       
   325 static void gst_play_bin_init (GstPlayBin * play_bin);
       
   326 static void gst_play_bin_dispose (GObject * object);
       
   327 
       
   328 static gboolean setup_sinks (GstPlayBaseBin * play_base_bin,
       
   329     GstPlayBaseGroup * group);
       
   330 static void remove_sinks (GstPlayBin * play_bin);
       
   331 static void playbin_set_subtitles_visible (GstPlayBaseBin * play_base_bin,
       
   332     gboolean visible);
       
   333 
       
   334 static void gst_play_bin_set_property (GObject * object, guint prop_id,
       
   335     const GValue * value, GParamSpec * spec);
       
   336 static void gst_play_bin_get_property (GObject * object, guint prop_id,
       
   337     GValue * value, GParamSpec * spec);
       
   338 
       
   339 static gboolean gst_play_bin_send_event (GstElement * element,
       
   340     GstEvent * event);
       
   341 static GstStateChangeReturn gst_play_bin_change_state (GstElement * element,
       
   342     GstStateChange transition);
       
   343 
       
   344 static void gst_play_bin_handle_message (GstBin * bin, GstMessage * message);
       
   345 
       
   346 static GstElementClass *parent_class;
       
   347 
       
   348 //static guint gst_play_bin_signals[LAST_SIGNAL] = { 0 };
       
   349 
       
   350 static const GstElementDetails gst_play_bin_details =
       
   351 GST_ELEMENT_DETAILS ("Player Bin",
       
   352     "Generic/Bin/Player",
       
   353     "Autoplug and play media from an uri",
       
   354     "Wim Taymans <wim.taymans@gmail.com>");
       
   355 
       
   356 static GType
       
   357 gst_play_bin_get_type (void)
       
   358 {
       
   359   static GType gst_play_bin_type = 0;
       
   360 
       
   361   if (!gst_play_bin_type) {
       
   362     static const GTypeInfo gst_play_bin_info = {
       
   363       sizeof (GstPlayBinClass),
       
   364       NULL,
       
   365       NULL,
       
   366       (GClassInitFunc) gst_play_bin_class_init,
       
   367       NULL,
       
   368       NULL,
       
   369       sizeof (GstPlayBin),
       
   370       0,
       
   371       (GInstanceInitFunc) gst_play_bin_init,
       
   372       NULL
       
   373     };
       
   374 
       
   375     gst_play_bin_type = g_type_register_static (GST_TYPE_PLAY_BASE_BIN,
       
   376         "GstPlayBin", &gst_play_bin_info, 0);
       
   377   }
       
   378 
       
   379   return gst_play_bin_type;
       
   380 }
       
   381 
       
   382 static void
       
   383 gst_play_bin_class_init (GstPlayBinClass * klass)
       
   384 {
       
   385   GObjectClass *gobject_klass;
       
   386   GstElementClass *gstelement_klass;
       
   387   GstBinClass *gstbin_klass;
       
   388   GstPlayBaseBinClass *playbasebin_klass;
       
   389 
       
   390   gobject_klass = (GObjectClass *) klass;
       
   391   gstelement_klass = (GstElementClass *) klass;
       
   392   gstbin_klass = (GstBinClass *) klass;
       
   393   playbasebin_klass = (GstPlayBaseBinClass *) klass;
       
   394 
       
   395   parent_class = g_type_class_peek_parent (klass);
       
   396 
       
   397   gobject_klass->set_property = gst_play_bin_set_property;
       
   398   gobject_klass->get_property = gst_play_bin_get_property;
       
   399 
       
   400   g_object_class_install_property (gobject_klass, ARG_VIDEO_SINK,
       
   401       g_param_spec_object ("video-sink", "Video Sink",
       
   402           "the video output element to use (NULL = default sink)",
       
   403           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
       
   404   g_object_class_install_property (gobject_klass, ARG_AUDIO_SINK,
       
   405       g_param_spec_object ("audio-sink", "Audio Sink",
       
   406           "the audio output element to use (NULL = default sink)",
       
   407           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
       
   408   g_object_class_install_property (gobject_klass, ARG_VIS_PLUGIN,
       
   409       g_param_spec_object ("vis-plugin", "Vis plugin",
       
   410           "the visualization element to use (NULL = none)",
       
   411           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
       
   412   g_object_class_install_property (gobject_klass, ARG_VOLUME,
       
   413       g_param_spec_double ("volume", "volume", "volume",
       
   414           0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
       
   415   g_object_class_install_property (gobject_klass, ARG_FRAME,
       
   416       gst_param_spec_mini_object ("frame", "Frame",
       
   417           "The last frame (NULL = no video available)",
       
   418           GST_TYPE_BUFFER, G_PARAM_READABLE));
       
   419   g_object_class_install_property (gobject_klass, ARG_FONT_DESC,
       
   420       g_param_spec_string ("subtitle-font-desc",
       
   421           "Subtitle font description",
       
   422           "Pango font description of font "
       
   423           "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
       
   424 
       
   425   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose);
       
   426 
       
   427   gst_element_class_set_details (gstelement_klass, &gst_play_bin_details);
       
   428 
       
   429   gstelement_klass->change_state =
       
   430       GST_DEBUG_FUNCPTR (gst_play_bin_change_state);
       
   431   gstelement_klass->send_event = GST_DEBUG_FUNCPTR (gst_play_bin_send_event);
       
   432 
       
   433   gstbin_klass->handle_message =
       
   434       GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
       
   435 
       
   436   playbasebin_klass->setup_output_pads = setup_sinks;
       
   437   playbasebin_klass->set_subtitles_visible = playbin_set_subtitles_visible;
       
   438 }
       
   439 
       
   440 static void
       
   441 gst_play_bin_init (GstPlayBin * play_bin)
       
   442 {
       
   443   play_bin->video_sink = NULL;
       
   444   play_bin->audio_sink = NULL;
       
   445   play_bin->visualisation = NULL;
       
   446   play_bin->pending_visualisation = NULL;
       
   447   play_bin->volume_element = NULL;
       
   448   play_bin->textoverlay_element = NULL;
       
   449   play_bin->volume = 1.0;
       
   450   play_bin->sinks = NULL;
       
   451   play_bin->frame = NULL;
       
   452   play_bin->font_desc = NULL;
       
   453   play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal,
       
   454       NULL, (GDestroyNotify) gst_object_unref);
       
   455 }
       
   456 
       
   457 static void
       
   458 gst_play_bin_dispose (GObject * object)
       
   459 {
       
   460   GstPlayBin *play_bin;
       
   461 
       
   462   play_bin = GST_PLAY_BIN (object);
       
   463 
       
   464   if (play_bin->cache != NULL) {
       
   465     remove_sinks (play_bin);
       
   466     g_hash_table_destroy (play_bin->cache);
       
   467     play_bin->cache = NULL;
       
   468   }
       
   469 
       
   470   if (play_bin->audio_sink != NULL) {
       
   471     gst_element_set_state (play_bin->audio_sink, GST_STATE_NULL);
       
   472     gst_object_unref (play_bin->audio_sink);
       
   473     play_bin->audio_sink = NULL;
       
   474   }
       
   475   if (play_bin->video_sink != NULL) {
       
   476     gst_element_set_state (play_bin->video_sink, GST_STATE_NULL);
       
   477     gst_object_unref (play_bin->video_sink);
       
   478     play_bin->video_sink = NULL;
       
   479   }
       
   480   if (play_bin->visualisation != NULL) {
       
   481     gst_element_set_state (play_bin->visualisation, GST_STATE_NULL);
       
   482     gst_object_unref (play_bin->visualisation);
       
   483     play_bin->visualisation = NULL;
       
   484   }
       
   485   if (play_bin->pending_visualisation != NULL) {
       
   486     gst_element_set_state (play_bin->pending_visualisation, GST_STATE_NULL);
       
   487     gst_object_unref (play_bin->pending_visualisation);
       
   488     play_bin->pending_visualisation = NULL;
       
   489   }
       
   490   if (play_bin->textoverlay_element != NULL) {
       
   491     gst_object_unref (play_bin->textoverlay_element);
       
   492     play_bin->textoverlay_element = NULL;
       
   493   }
       
   494   g_free (play_bin->font_desc);
       
   495   play_bin->font_desc = NULL;
       
   496 
       
   497   G_OBJECT_CLASS (parent_class)->dispose (object);
       
   498 }
       
   499 
       
   500 static void
       
   501 gst_play_bin_vis_unblocked (GstPad * tee_pad, gboolean blocked,
       
   502     gpointer user_data)
       
   503 {
       
   504   GstPlayBin *play_bin = GST_PLAY_BIN (user_data);
       
   505 
       
   506   if (play_bin->pending_visualisation)
       
   507     gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_bin_vis_unblocked,
       
   508         play_bin);
       
   509 }
       
   510 
       
   511 static void
       
   512 gst_play_bin_vis_blocked (GstPad * tee_pad, gboolean blocked,
       
   513     gpointer user_data)
       
   514 {
       
   515   GstPlayBin *play_bin = GST_PLAY_BIN (user_data);
       
   516   GstBin *vis_bin = NULL;
       
   517   GstPad *vis_sink_pad = NULL, *vis_src_pad = NULL, *vqueue_pad = NULL;
       
   518   GstState bin_state;
       
   519   GstElement *pending_visualisation;
       
   520 
       
   521   GST_OBJECT_LOCK (play_bin);
       
   522   pending_visualisation = play_bin->pending_visualisation;
       
   523   play_bin->pending_visualisation = NULL;
       
   524   GST_OBJECT_UNLOCK (play_bin);
       
   525 
       
   526   /* We want to disable visualisation */
       
   527   if (!GST_IS_ELEMENT (pending_visualisation)) {
       
   528     /* Set visualisation element to READY */
       
   529     gst_element_set_state (play_bin->visualisation, GST_STATE_READY);
       
   530     goto beach;
       
   531   }
       
   532 
       
   533   vis_bin =
       
   534       GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (play_bin->
       
   535               visualisation)));
       
   536 
       
   537   if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) {
       
   538     goto beach;
       
   539   }
       
   540 
       
   541   vis_src_pad = gst_element_get_pad (play_bin->visualisation, "src");
       
   542   vis_sink_pad = gst_pad_get_peer (tee_pad);
       
   543 
       
   544   /* Can be fakesink */
       
   545   if (GST_IS_PAD (vis_src_pad)) {
       
   546     vqueue_pad = gst_pad_get_peer (vis_src_pad);
       
   547   }
       
   548 
       
   549   if (!GST_IS_PAD (vis_sink_pad)) {
       
   550     goto beach;
       
   551   }
       
   552 
       
   553   /* Check the bin's state */
       
   554   GST_OBJECT_LOCK (vis_bin);
       
   555   bin_state = GST_STATE (vis_bin);
       
   556   GST_OBJECT_UNLOCK (vis_bin);
       
   557 
       
   558   /* Unlink */
       
   559   gst_pad_unlink (tee_pad, vis_sink_pad);
       
   560   gst_object_unref (vis_sink_pad);
       
   561   vis_sink_pad = NULL;
       
   562 
       
   563   if (GST_IS_PAD (vqueue_pad)) {
       
   564     gst_pad_unlink (vis_src_pad, vqueue_pad);
       
   565     gst_object_unref (vis_src_pad);
       
   566     vis_src_pad = NULL;
       
   567   }
       
   568 
       
   569   /* Remove from vis_bin */
       
   570   gst_bin_remove (vis_bin, play_bin->visualisation);
       
   571   /* Set state to NULL */
       
   572   gst_element_set_state (play_bin->visualisation, GST_STATE_NULL);
       
   573   /* And loose our ref */
       
   574   gst_object_unref (play_bin->visualisation);
       
   575 
       
   576   if (pending_visualisation) {
       
   577     /* Ref this new visualisation element before adding to the bin */
       
   578     gst_object_ref (pending_visualisation);
       
   579     /* Add the new one */
       
   580     gst_bin_add (vis_bin, pending_visualisation);
       
   581     /* Synchronizing state */
       
   582     gst_element_set_state (pending_visualisation, bin_state);
       
   583 
       
   584     vis_sink_pad = gst_element_get_pad (pending_visualisation, "sink");
       
   585     vis_src_pad = gst_element_get_pad (pending_visualisation, "src");
       
   586 
       
   587     if (!GST_IS_PAD (vis_sink_pad) || !GST_IS_PAD (vis_src_pad)) {
       
   588       goto beach;
       
   589     }
       
   590 
       
   591     /* Link */
       
   592     gst_pad_link (tee_pad, vis_sink_pad);
       
   593     gst_pad_link (vis_src_pad, vqueue_pad);
       
   594   }
       
   595 
       
   596   /* We are done */
       
   597   gst_object_unref (play_bin->visualisation);
       
   598   play_bin->visualisation = pending_visualisation;
       
   599 
       
   600 beach:
       
   601   if (vis_sink_pad) {
       
   602     gst_object_unref (vis_sink_pad);
       
   603   }
       
   604   if (vis_src_pad) {
       
   605     gst_object_unref (vis_src_pad);
       
   606   }
       
   607   if (vqueue_pad) {
       
   608     gst_object_unref (vqueue_pad);
       
   609   }
       
   610   if (vis_bin) {
       
   611     gst_object_unref (vis_bin);
       
   612   }
       
   613 
       
   614   /* Unblock the pad */
       
   615   gst_pad_set_blocked_async (tee_pad, FALSE, gst_play_bin_vis_unblocked,
       
   616       play_bin);
       
   617 }
       
   618 
       
   619 static void
       
   620 gst_play_bin_set_property (GObject * object, guint prop_id,
       
   621     const GValue * value, GParamSpec * pspec)
       
   622 {
       
   623   GstPlayBin *play_bin;
       
   624 
       
   625   play_bin = GST_PLAY_BIN (object);
       
   626 
       
   627   switch (prop_id) {
       
   628     case ARG_VIDEO_SINK:
       
   629       if (play_bin->video_sink != NULL) {
       
   630         gst_object_unref (play_bin->video_sink);
       
   631       }
       
   632       play_bin->video_sink = g_value_get_object (value);
       
   633       if (play_bin->video_sink != NULL) {
       
   634         gst_object_ref (play_bin->video_sink);
       
   635         gst_object_sink (GST_OBJECT_CAST (play_bin->video_sink));
       
   636       }
       
   637       /* when changing the videosink, we just remove the
       
   638        * video pipeline from the cache so that it will be
       
   639        * regenerated with the new sink element */
       
   640       g_hash_table_remove (play_bin->cache, "vbin");
       
   641       break;
       
   642     case ARG_AUDIO_SINK:
       
   643       if (play_bin->audio_sink != NULL) {
       
   644         gst_object_unref (play_bin->audio_sink);
       
   645       }
       
   646       play_bin->audio_sink = g_value_get_object (value);
       
   647       if (play_bin->audio_sink != NULL) {
       
   648         gst_object_ref (play_bin->audio_sink);
       
   649         gst_object_sink (GST_OBJECT_CAST (play_bin->audio_sink));
       
   650       }
       
   651       g_hash_table_remove (play_bin->cache, "abin");
       
   652       break;
       
   653     case ARG_VIS_PLUGIN:
       
   654     {
       
   655       GstElement *pending_visualisation =
       
   656           GST_ELEMENT_CAST (g_value_get_object (value));
       
   657 
       
   658       /* Take ownership */
       
   659       if (pending_visualisation) {
       
   660         gst_object_ref (pending_visualisation);
       
   661         gst_object_sink (pending_visualisation);
       
   662       }
       
   663 
       
   664       /* Do we already have a visualisation change pending ? */
       
   665       GST_OBJECT_LOCK (play_bin);
       
   666       if (play_bin->pending_visualisation) {
       
   667         gst_object_unref (play_bin->pending_visualisation);
       
   668         play_bin->pending_visualisation = pending_visualisation;
       
   669         GST_OBJECT_UNLOCK (play_bin);
       
   670       } else {
       
   671         GST_OBJECT_UNLOCK (play_bin);
       
   672         /* Was there a visualisation already set ? */
       
   673         if (play_bin->visualisation != NULL) {
       
   674           GstBin *vis_bin = NULL;
       
   675 
       
   676           vis_bin =
       
   677               GST_BIN_CAST (gst_object_get_parent (GST_OBJECT_CAST (play_bin->
       
   678                       visualisation)));
       
   679 
       
   680           /* Check if the visualisation is already in a bin */
       
   681           if (GST_IS_BIN (vis_bin)) {
       
   682             GstPad *vis_sink_pad = NULL, *tee_pad = NULL;
       
   683 
       
   684             /* Now get tee pad and block it async */
       
   685             vis_sink_pad = gst_element_get_pad (play_bin->visualisation,
       
   686                 "sink");
       
   687             if (!GST_IS_PAD (vis_sink_pad)) {
       
   688               goto beach;
       
   689             }
       
   690             tee_pad = gst_pad_get_peer (vis_sink_pad);
       
   691             if (!GST_IS_PAD (tee_pad)) {
       
   692               goto beach;
       
   693             }
       
   694 
       
   695             play_bin->pending_visualisation = pending_visualisation;
       
   696             /* Block with callback */
       
   697             gst_pad_set_blocked_async (tee_pad, TRUE, gst_play_bin_vis_blocked,
       
   698                 play_bin);
       
   699           beach:
       
   700             if (vis_sink_pad) {
       
   701               gst_object_unref (vis_sink_pad);
       
   702             }
       
   703             if (tee_pad) {
       
   704               gst_object_unref (tee_pad);
       
   705             }
       
   706             gst_object_unref (vis_bin);
       
   707           } else {
       
   708             play_bin->visualisation = pending_visualisation;
       
   709           }
       
   710         } else {
       
   711           play_bin->visualisation = pending_visualisation;
       
   712         }
       
   713       }
       
   714       break;
       
   715     }
       
   716     case ARG_VOLUME:
       
   717       play_bin->volume = g_value_get_double (value);
       
   718       if (play_bin->volume_element) {
       
   719         g_object_set (G_OBJECT (play_bin->volume_element), "volume",
       
   720             play_bin->volume, NULL);
       
   721       }
       
   722       break;
       
   723     case ARG_FONT_DESC:
       
   724       g_free (play_bin->font_desc);
       
   725       play_bin->font_desc = g_strdup (g_value_get_string (value));
       
   726       if (play_bin->textoverlay_element) {
       
   727         g_object_set (G_OBJECT (play_bin->textoverlay_element),
       
   728             "font-desc", g_value_get_string (value), NULL);
       
   729       }
       
   730       break;
       
   731     default:
       
   732       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   733       break;
       
   734   }
       
   735 }
       
   736 
       
   737 static void
       
   738 gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
       
   739     GParamSpec * pspec)
       
   740 {
       
   741   GstPlayBin *play_bin;
       
   742 
       
   743   play_bin = GST_PLAY_BIN (object);
       
   744 
       
   745   switch (prop_id) {
       
   746     case ARG_VIDEO_SINK:
       
   747       g_value_set_object (value, play_bin->video_sink);
       
   748       break;
       
   749     case ARG_AUDIO_SINK:
       
   750       g_value_set_object (value, play_bin->audio_sink);
       
   751       break;
       
   752     case ARG_VIS_PLUGIN:
       
   753       g_value_set_object (value, play_bin->visualisation);
       
   754       break;
       
   755     case ARG_VOLUME:
       
   756       g_value_set_double (value, play_bin->volume);
       
   757       break;
       
   758     case ARG_FRAME:{
       
   759       GstBuffer *cur_frame = NULL;
       
   760 
       
   761       gst_buffer_replace (&cur_frame, play_bin->frame);
       
   762       gst_value_take_buffer (value, cur_frame);
       
   763       break;
       
   764     }
       
   765     default:
       
   766       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       
   767       break;
       
   768   }
       
   769 }
       
   770 
       
   771 /* signal fired when the identity has received a new buffer. This is used for
       
   772  * making screenshots.
       
   773  */
       
   774 static void
       
   775 handoff (GstElement * identity, GstBuffer * frame, gpointer data)
       
   776 {
       
   777   GstPlayBin *play_bin = GST_PLAY_BIN (data);
       
   778 
       
   779   /* applications need to know the buffer caps,
       
   780    * make sure they are always set on the frame */
       
   781   if (GST_BUFFER_CAPS (frame) == NULL) {
       
   782     GstPad *pad;
       
   783 
       
   784     if ((pad = gst_element_get_pad (identity, "sink"))) {
       
   785       gst_buffer_set_caps (frame, GST_PAD_CAPS (pad));
       
   786       gst_object_unref (pad);
       
   787     }
       
   788   }
       
   789 
       
   790   gst_buffer_replace (&play_bin->frame, frame);
       
   791 }
       
   792 
       
   793 static void
       
   794 post_missing_element_message (GstPlayBin * playbin, const gchar * name)
       
   795 {
       
   796   GstMessage *msg;
       
   797 
       
   798   msg = gst_missing_element_message_new (GST_ELEMENT_CAST (playbin), name);
       
   799   gst_element_post_message (GST_ELEMENT_CAST (playbin), msg);
       
   800 }
       
   801 
       
   802 /* make the element (bin) that contains the elements needed to perform
       
   803  * video display. We connect a handoff signal to identity so that we
       
   804  * can grab snapshots. Identity's sinkpad is ghosted to vbin.
       
   805  *
       
   806  *  +-------------------------------------------------------------+
       
   807  *  | vbin                                                        |
       
   808  *  |      +--------+   +----------+   +----------+   +---------+ |
       
   809  *  |      |identity|   |colorspace|   |videoscale|   |videosink| |
       
   810  *  |   +-sink     src-sink       src-sink       src-sink       | |
       
   811  *  |   |  +---+----+   +----------+   +----------+   +---------+ |
       
   812  * sink-+      |                                                  |
       
   813  *  +----------|--------------------------------------------------+
       
   814  *           handoff
       
   815  */
       
   816 static GstElement *
       
   817 gen_video_element (GstPlayBin * play_bin)
       
   818 {
       
   819   GstElement *element;
       
   820   GstElement *conv;
       
   821 
       
   822   GstElement *scale;
       
   823   GstElement *sink;
       
   824   GstElement *identity;
       
   825   GstPad *pad;
       
   826 
       
   827   /* first see if we have it in the cache */
       
   828   element = g_hash_table_lookup (play_bin->cache, "vbin");
       
   829   if (element != NULL) {
       
   830     return element;
       
   831   }
       
   832 
       
   833   if (play_bin->video_sink) {
       
   834     sink = play_bin->video_sink;
       
   835   } else {
       
   836     sink = gst_element_factory_make ("autovideosink", "videosink");
       
   837     if (sink == NULL) {
       
   838       sink = gst_element_factory_make ("xvimagesink", "videosink");
       
   839     }
       
   840     if (sink == NULL)
       
   841       goto no_sinks;
       
   842   }
       
   843   gst_object_ref (sink);
       
   844   g_hash_table_insert (play_bin->cache, "video_sink", sink);
       
   845 
       
   846   /* create a bin to hold objects, as we create them we add them to this bin so
       
   847    * that when something goes wrong we only need to unref the bin */
       
   848   element = gst_bin_new ("vbin");
       
   849   gst_bin_add (GST_BIN_CAST (element), sink);
       
   850 
       
   851   conv = gst_element_factory_make ("ffmpegcolorspace", "vconv");
       
   852   if (conv == NULL)
       
   853     goto no_colorspace;
       
   854   gst_bin_add (GST_BIN_CAST (element), conv);
       
   855 
       
   856   scale = gst_element_factory_make ("videoscale", "vscale");
       
   857   if (scale == NULL)
       
   858     goto no_videoscale;
       
   859   gst_bin_add (GST_BIN_CAST (element), scale);
       
   860 
       
   861   identity = gst_element_factory_make ("identity", "id");
       
   862   g_object_set (identity, "silent", TRUE, NULL);
       
   863   g_signal_connect (identity, "handoff", G_CALLBACK (handoff), play_bin);
       
   864   gst_bin_add (GST_BIN_CAST (element), identity);
       
   865 
       
   866   gst_element_link_pads (identity, "src", conv, "sink");
       
   867   gst_element_link_pads (conv, "src", scale, "sink");
       
   868   /* be more careful with the pad from the custom sink element, it might not
       
   869    * be named 'sink' */
       
   870   if (!gst_element_link_pads (scale, "src", sink, NULL))
       
   871     goto link_failed;
       
   872 
       
   873   pad = gst_element_get_pad (identity, "sink");
       
   874   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
       
   875   gst_object_unref (pad);
       
   876 
       
   877   gst_element_set_state (element, GST_STATE_READY);
       
   878 
       
   879   /* since we're gonna add it to a bin but don't want to lose it,
       
   880    * we keep a reference. */
       
   881   gst_object_ref (element);
       
   882   g_hash_table_insert (play_bin->cache, "vbin", element);
       
   883 
       
   884   return element;
       
   885 
       
   886   /* ERRORS */
       
   887 no_sinks:
       
   888   {
       
   889     post_missing_element_message (play_bin, "autovideosink");
       
   890     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
   891         (_("Both autovideosink and xvimagesink elements are missing.")),
       
   892         (NULL));
       
   893     return NULL;
       
   894   }
       
   895 no_colorspace:
       
   896   {
       
   897     post_missing_element_message (play_bin, "ffmpegcolorspace");
       
   898     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
   899         (_("Missing element '%s' - check your GStreamer installation."),
       
   900             "ffmpegcolorspace"), (NULL));
       
   901     gst_object_unref (element);
       
   902     return NULL;
       
   903   }
       
   904 
       
   905 no_videoscale:
       
   906   {
       
   907     post_missing_element_message (play_bin, "videoscale");
       
   908     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
   909         (_("Missing element '%s' - check your GStreamer installation."),
       
   910             "videoscale"), ("possibly a liboil version mismatch?"));
       
   911     gst_object_unref (element);
       
   912     return NULL;
       
   913   }
       
   914 link_failed:
       
   915   {
       
   916     GST_ELEMENT_ERROR (play_bin, CORE, PAD,
       
   917         (NULL), ("Failed to configure the video sink."));
       
   918     gst_object_unref (element);
       
   919     return NULL;
       
   920   }
       
   921 }
       
   922 
       
   923 /* make an element for playback of video with subtitles embedded.
       
   924  *
       
   925  *  +--------------------------------------------------+
       
   926  *  | tbin                  +-------------+            |
       
   927  *  |          +-----+      | textoverlay |   +------+ |
       
   928  *  |          | csp | +--video_sink      |   | vbin | |
       
   929  * video_sink-sink  src+ +-text_sink     src-sink    | |
       
   930  *  |          +-----+   |  +-------------+   +------+ |
       
   931  * text_sink-------------+                             |
       
   932  *  +--------------------------------------------------+
       
   933  *
       
   934  *  If there is no subtitle renderer this function will simply return the
       
   935  *  videosink without the text_sink pad.
       
   936  */
       
   937 static GstElement *
       
   938 gen_text_element (GstPlayBin * play_bin)
       
   939 {
       
   940   GstElement *element, *csp, *overlay, *vbin;
       
   941   GstPad *pad;
       
   942 
       
   943   /* Create the video rendering bin, error is posted when this fails. */
       
   944   vbin = gen_video_element (play_bin);
       
   945   if (!vbin)
       
   946     return NULL;
       
   947 
       
   948   /* Text overlay */
       
   949   overlay = gst_element_factory_make ("textoverlay", "overlay");
       
   950 
       
   951   /* If no overlay return the video bin without subtitle support. */
       
   952   if (!overlay)
       
   953     goto no_overlay;
       
   954 
       
   955   /* Create our bin */
       
   956   element = gst_bin_new ("textbin");
       
   957 
       
   958   /* Set some parameters */
       
   959   g_object_set (G_OBJECT (overlay),
       
   960       "halign", "center", "valign", "bottom", NULL);
       
   961   if (play_bin->font_desc) {
       
   962     g_object_set (G_OBJECT (overlay), "font-desc", play_bin->font_desc, NULL);
       
   963   }
       
   964 
       
   965   /* Take a ref */
       
   966   play_bin->textoverlay_element = GST_ELEMENT_CAST (gst_object_ref (overlay));
       
   967 
       
   968   /* we know this will succeed, as the video bin already created one before */
       
   969   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
       
   970 
       
   971   /* Add our elements */
       
   972   gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL);
       
   973 
       
   974   /* Link */
       
   975   gst_element_link_pads (csp, "src", overlay, "video_sink");
       
   976   gst_element_link_pads (overlay, "src", vbin, "sink");
       
   977 
       
   978   /* Add ghost pads on the subtitle bin */
       
   979   pad = gst_element_get_pad (overlay, "text_sink");
       
   980   gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad));
       
   981   gst_object_unref (pad);
       
   982 
       
   983   pad = gst_element_get_pad (csp, "sink");
       
   984   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
       
   985   gst_object_unref (pad);
       
   986 
       
   987   /* Set state to READY */
       
   988   gst_element_set_state (element, GST_STATE_READY);
       
   989 
       
   990   return element;
       
   991 
       
   992   /* ERRORS */
       
   993 no_overlay:
       
   994   {
       
   995     post_missing_element_message (play_bin, "textoverlay");
       
   996     GST_WARNING_OBJECT (play_bin,
       
   997         "No overlay (pango) element, subtitles disabled");
       
   998     return vbin;
       
   999   }
       
  1000 }
       
  1001 
       
  1002 /* make the element (bin) that contains the elements needed to perform
       
  1003  * audio playback.
       
  1004  *
       
  1005  *  +-------------------------------------------------------------+
       
  1006  *  | abin                                                        |
       
  1007  *  |      +---------+   +----------+   +---------+   +---------+ |
       
  1008  *  |      |audioconv|   |audioscale|   | volume  |   |audiosink| |
       
  1009  *  |   +-sink      src-sink       src-sink      src-sink       | |
       
  1010  *  |   |  +---------+   +----------+   +---------+   +---------+ |
       
  1011  * sink-+                                                         |
       
  1012  *  +-------------------------------------------------------------+
       
  1013  */
       
  1014 static GstElement *
       
  1015 gen_audio_element (GstPlayBin * play_bin)
       
  1016 {
       
  1017   gboolean res;
       
  1018   GstElement *element;
       
  1019   GstElement *conv;
       
  1020   GstElement *scale;
       
  1021   GstElement *sink;
       
  1022   GstElement *volume;
       
  1023   GstPad *pad;
       
  1024 
       
  1025   element = g_hash_table_lookup (play_bin->cache, "abin");
       
  1026   if (element != NULL)
       
  1027     return element;
       
  1028 
       
  1029   if (play_bin->audio_sink) {
       
  1030     sink = play_bin->audio_sink;
       
  1031   } else {
       
  1032     sink = gst_element_factory_make ("autoaudiosink", "audiosink");
       
  1033     if (sink == NULL) {
       
  1034     #ifdef __SYMBIAN32__
       
  1035       sink = gst_element_factory_make ("devsoundsink", "audiosink");
       
  1036     #else    
       
  1037       sink = gst_element_factory_make ("alsasink", "audiosink");
       
  1038     #endif
       
  1039     }
       
  1040     if (sink == NULL)
       
  1041       goto no_sinks;
       
  1042 
       
  1043     play_bin->audio_sink = GST_ELEMENT_CAST (gst_object_ref (sink));
       
  1044   }
       
  1045 
       
  1046   gst_object_ref (sink);
       
  1047   g_hash_table_insert (play_bin->cache, "audio_sink", sink);
       
  1048 
       
  1049   element = gst_bin_new ("abin");
       
  1050   gst_bin_add (GST_BIN_CAST (element), sink);
       
  1051 
       
  1052   conv = gst_element_factory_make ("audioconvert", "aconv");
       
  1053   if (conv == NULL)
       
  1054     goto no_audioconvert;
       
  1055   gst_bin_add (GST_BIN_CAST (element), conv);
       
  1056 
       
  1057   scale = gst_element_factory_make ("audioresample", "aresample");
       
  1058   if (scale == NULL)
       
  1059     goto no_audioresample;
       
  1060   gst_bin_add (GST_BIN_CAST (element), scale);
       
  1061 #ifndef __SYMBIAN32__
       
  1062   volume = gst_element_factory_make ("volume", "volume");
       
  1063   if (volume == NULL)
       
  1064     goto no_volume;
       
  1065   g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL);
       
  1066   play_bin->volume_element = volume;
       
  1067   gst_bin_add (GST_BIN_CAST (element), volume);
       
  1068 #endif
       
  1069 
       
  1070   res = gst_element_link_pads (conv, "src", scale, "sink");
       
  1071 #ifndef __SYMBIAN32__
       
  1072   res &= gst_element_link_pads (scale, "src", volume, "sink");
       
  1073 #else
       
  1074   res &= gst_element_link_pads (scale, "src", sink, NULL);
       
  1075 #endif
       
  1076 #ifndef __SYMBIAN32__
       
  1077   res &= gst_element_link_pads (volume, "src", sink, NULL);
       
  1078 #endif
       
  1079   if (!res)
       
  1080     goto link_failed;
       
  1081 
       
  1082   pad = gst_element_get_pad (conv, "sink");
       
  1083   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
       
  1084   gst_object_unref (pad);
       
  1085 
       
  1086   gst_element_set_state (element, GST_STATE_READY);
       
  1087 
       
  1088   /* since we're gonna add it to a bin but don't want to lose it,
       
  1089    * we keep a reference. */
       
  1090   gst_object_ref (element);
       
  1091   g_hash_table_insert (play_bin->cache, "abin", element);
       
  1092 
       
  1093   return element;
       
  1094 
       
  1095   /* ERRORS */
       
  1096 no_sinks:
       
  1097   {
       
  1098     post_missing_element_message (play_bin, "autoaudiosink");
       
  1099     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
  1100         (_("Both autoaudiosink and alsasink elements are missing.")), (NULL));
       
  1101     return NULL;
       
  1102   }
       
  1103 no_audioconvert:
       
  1104   {
       
  1105     post_missing_element_message (play_bin, "audioconvert");
       
  1106     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
  1107         (_("Missing element '%s' - check your GStreamer installation."),
       
  1108             "audioconvert"), ("possibly a liboil version mismatch?"));
       
  1109     gst_object_unref (element);
       
  1110     return NULL;
       
  1111   }
       
  1112 no_audioresample:
       
  1113   {
       
  1114     post_missing_element_message (play_bin, "audioresample");
       
  1115     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
  1116         (_("Missing element '%s' - check your GStreamer installation."),
       
  1117             "audioresample"), ("possibly a liboil version mismatch?"));
       
  1118     gst_object_unref (element);
       
  1119     return NULL;
       
  1120   }
       
  1121 no_volume:
       
  1122   {
       
  1123     post_missing_element_message (play_bin, "volume");
       
  1124     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
  1125         (_("Missing element '%s' - check your GStreamer installation."),
       
  1126             "volume"), ("possibly a liboil version mismatch?"));
       
  1127     gst_object_unref (element);
       
  1128     return NULL;
       
  1129   }
       
  1130 link_failed:
       
  1131   {
       
  1132     GST_ELEMENT_ERROR (play_bin, CORE, PAD,
       
  1133         (NULL), ("Failed to configure the audio sink."));
       
  1134     gst_object_unref (element);
       
  1135     return NULL;
       
  1136   }
       
  1137 }
       
  1138 
       
  1139 /* make the element (bin) that contains the elements needed to perform
       
  1140  * visualisation ouput.  The idea is to split the audio using tee, then
       
  1141  * sending the output to the regular audio bin and the other output to
       
  1142  * the vis plugin that transforms it into a video that is rendered with the
       
  1143  * normal video bin. The video and audio bins are run in threads to make sure
       
  1144  * they don't block eachother.
       
  1145  *
       
  1146  *  +-----------------------------------------------------------------------+
       
  1147  *  | visbin                                                                |
       
  1148  *  |      +------+   +--------+   +----------------+                       |
       
  1149  *  |      | tee  |   | aqueue |   |   abin ...     |                       |
       
  1150  *  |   +-sink   src-sink     src-sink              |                       |
       
  1151  *  |   |  |      |   +--------+   +----------------+                       |
       
  1152  *  |   |  |      |                                                         |
       
  1153  *  |   |  |      |   +------+   +------------+   +------+   +-----------+  |
       
  1154  *  |   |  |      |   |vqueue|   | audioconv  |   | vis  |   | vbin ...  |  |
       
  1155  *  |   |  |     src-sink   src-sink + samp  src-sink   src-sink         |  |
       
  1156  *  |   |  |      |   +------+   +------------+   +------+   +-----------+  |
       
  1157  *  |   |  |      |                                                         |
       
  1158  *  |   |  +------+                                                         |
       
  1159  * sink-+                                                                   |
       
  1160  *  +-----------------------------------------------------------------------+
       
  1161  */
       
  1162 static GstElement *
       
  1163 gen_vis_element (GstPlayBin * play_bin)
       
  1164 {
       
  1165   gboolean res;
       
  1166   GstElement *element;
       
  1167   GstElement *tee;
       
  1168   GstElement *asink;
       
  1169   GstElement *vsink;
       
  1170   GstElement *conv;
       
  1171   GstElement *resamp;
       
  1172   GstElement *conv2;
       
  1173   GstElement *vis;
       
  1174   GstElement *vqueue, *aqueue;
       
  1175   GstPad *pad, *rpad;
       
  1176 
       
  1177   /* errors are already posted when these fail. */
       
  1178   asink = gen_audio_element (play_bin);
       
  1179   if (!asink)
       
  1180     return NULL;
       
  1181   vsink = gen_video_element (play_bin);
       
  1182   if (!vsink) {
       
  1183     gst_object_unref (asink);
       
  1184     return NULL;
       
  1185   }
       
  1186 
       
  1187   element = gst_bin_new ("visbin");
       
  1188   tee = gst_element_factory_make ("tee", "tee");
       
  1189 
       
  1190   vqueue = gst_element_factory_make ("queue", "vqueue");
       
  1191   aqueue = gst_element_factory_make ("queue", "aqueue");
       
  1192 
       
  1193   gst_bin_add (GST_BIN_CAST (element), asink);
       
  1194   gst_bin_add (GST_BIN_CAST (element), vqueue);
       
  1195   gst_bin_add (GST_BIN_CAST (element), aqueue);
       
  1196   gst_bin_add (GST_BIN_CAST (element), vsink);
       
  1197   gst_bin_add (GST_BIN_CAST (element), tee);
       
  1198 
       
  1199   conv = gst_element_factory_make ("audioconvert", "aconv");
       
  1200   if (conv == NULL)
       
  1201     goto no_audioconvert;
       
  1202   gst_bin_add (GST_BIN_CAST (element), conv);
       
  1203 
       
  1204   resamp = gst_element_factory_make ("audioresample", "aresamp");
       
  1205   if (resamp == NULL)
       
  1206     goto no_audioresample;
       
  1207   gst_bin_add (GST_BIN_CAST (element), resamp);
       
  1208 
       
  1209   conv2 = gst_element_factory_make ("audioconvert", "aconv2");
       
  1210   if (conv2 == NULL)
       
  1211     goto no_audioconvert;
       
  1212   gst_bin_add (GST_BIN_CAST (element), conv2);
       
  1213 
       
  1214   if (play_bin->visualisation) {
       
  1215     gst_object_ref (play_bin->visualisation);
       
  1216     vis = play_bin->visualisation;
       
  1217   } else {
       
  1218     vis = gst_element_factory_make ("goom", "vis");
       
  1219     if (!vis)
       
  1220       goto no_goom;
       
  1221   }
       
  1222   gst_bin_add (GST_BIN_CAST (element), vis);
       
  1223 
       
  1224   res = gst_element_link_pads (vqueue, "src", conv, "sink");
       
  1225   res &= gst_element_link_pads (conv, "src", resamp, "sink");
       
  1226   res &= gst_element_link_pads (resamp, "src", conv2, "sink");
       
  1227   res &= gst_element_link_pads (conv2, "src", vis, "sink");
       
  1228   res &= gst_element_link_pads (vis, "src", vsink, "sink");
       
  1229   if (!res)
       
  1230     goto link_failed;
       
  1231 
       
  1232   pad = gst_element_get_pad (aqueue, "sink");
       
  1233   rpad = gst_element_get_request_pad (tee, "src%d");
       
  1234   gst_pad_link (rpad, pad);
       
  1235   gst_object_unref (rpad);
       
  1236   gst_object_unref (pad);
       
  1237   gst_element_link_pads (aqueue, "src", asink, "sink");
       
  1238 
       
  1239   pad = gst_element_get_pad (vqueue, "sink");
       
  1240   rpad = gst_element_get_request_pad (tee, "src%d");
       
  1241   gst_pad_link (rpad, pad);
       
  1242   gst_object_unref (rpad);
       
  1243   gst_object_unref (pad);
       
  1244 
       
  1245   pad = gst_element_get_pad (tee, "sink");
       
  1246   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
       
  1247   gst_object_unref (pad);
       
  1248 
       
  1249   return element;
       
  1250 
       
  1251   /* ERRORS */
       
  1252 no_audioconvert:
       
  1253   {
       
  1254     post_missing_element_message (play_bin, "audioconvert");
       
  1255     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
  1256         (_("Missing element '%s' - check your GStreamer installation."),
       
  1257             "audioconvert"), ("possibly a liboil version mismatch?"));
       
  1258     gst_object_unref (element);
       
  1259     return NULL;
       
  1260   }
       
  1261 no_audioresample:
       
  1262   {
       
  1263     post_missing_element_message (play_bin, "audioresample");
       
  1264     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
  1265         (_("Missing element '%s' - check your GStreamer installation."),
       
  1266             "audioresample"), (NULL));
       
  1267     gst_object_unref (element);
       
  1268     return NULL;
       
  1269   }
       
  1270 no_goom:
       
  1271   {
       
  1272     post_missing_element_message (play_bin, "goom");
       
  1273     GST_ELEMENT_ERROR (play_bin, CORE, MISSING_PLUGIN,
       
  1274         (_("Missing element '%s' - check your GStreamer installation."),
       
  1275             "goom"), (NULL));
       
  1276     gst_object_unref (element);
       
  1277     return NULL;
       
  1278   }
       
  1279 link_failed:
       
  1280   {
       
  1281     GST_ELEMENT_ERROR (play_bin, CORE, PAD,
       
  1282         (NULL), ("Failed to configure the visualisation element."));
       
  1283     gst_object_unref (element);
       
  1284     return NULL;
       
  1285   }
       
  1286 }
       
  1287 
       
  1288 /* get rid of all installed sinks */
       
  1289 static void
       
  1290 remove_sinks (GstPlayBin * play_bin)
       
  1291 {
       
  1292   GList *sinks;
       
  1293   GstObject *parent;
       
  1294   GstElement *element;
       
  1295   GstPad *pad, *peer;
       
  1296 
       
  1297   if (play_bin->cache == NULL)
       
  1298     return;
       
  1299 
       
  1300   GST_DEBUG ("removesinks");
       
  1301   element = g_hash_table_lookup (play_bin->cache, "abin");
       
  1302   if (element != NULL) {
       
  1303     parent = gst_element_get_parent (element);
       
  1304     if (parent != NULL) {
       
  1305       /* we remove the element from the parent so that
       
  1306        * there is no unwanted state change when the parent
       
  1307        * is disposed */
       
  1308       play_bin->sinks = g_list_remove (play_bin->sinks, element);
       
  1309       gst_element_set_state (element, GST_STATE_NULL);
       
  1310       gst_bin_remove (GST_BIN_CAST (parent), element);
       
  1311       gst_object_unref (parent);
       
  1312     }
       
  1313     pad = gst_element_get_pad (element, "sink");
       
  1314     if (pad != NULL) {
       
  1315       peer = gst_pad_get_peer (pad);
       
  1316       if (peer != NULL) {
       
  1317         gst_pad_unlink (peer, pad);
       
  1318         gst_object_unref (peer);
       
  1319       }
       
  1320       gst_object_unref (pad);
       
  1321     }
       
  1322   }
       
  1323   element = g_hash_table_lookup (play_bin->cache, "vbin");
       
  1324   if (element != NULL) {
       
  1325     parent = gst_element_get_parent (element);
       
  1326     if (parent != NULL) {
       
  1327       play_bin->sinks = g_list_remove (play_bin->sinks, element);
       
  1328       gst_element_set_state (element, GST_STATE_NULL);
       
  1329       gst_bin_remove (GST_BIN_CAST (parent), element);
       
  1330       gst_object_unref (parent);
       
  1331     }
       
  1332     pad = gst_element_get_pad (element, "sink");
       
  1333     if (pad != NULL) {
       
  1334       peer = gst_pad_get_peer (pad);
       
  1335       if (peer != NULL) {
       
  1336         gst_pad_unlink (peer, pad);
       
  1337         gst_object_unref (peer);
       
  1338       }
       
  1339       gst_object_unref (pad);
       
  1340     }
       
  1341   }
       
  1342 
       
  1343   for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) {
       
  1344     GstElement *element = GST_ELEMENT_CAST (sinks->data);
       
  1345     GstPad *pad;
       
  1346     GstPad *peer;
       
  1347 
       
  1348     pad = gst_element_get_pad (element, "sink");
       
  1349 
       
  1350     GST_LOG ("removing sink %p", element);
       
  1351 
       
  1352     peer = gst_pad_get_peer (pad);
       
  1353     if (peer) {
       
  1354       gst_pad_unlink (peer, pad);
       
  1355       gst_object_unref (peer);
       
  1356     }
       
  1357     gst_object_unref (pad);
       
  1358 
       
  1359     gst_element_set_state (element, GST_STATE_NULL);
       
  1360     gst_bin_remove (GST_BIN_CAST (play_bin), element);
       
  1361   }
       
  1362   g_list_free (play_bin->sinks);
       
  1363   play_bin->sinks = NULL;
       
  1364 
       
  1365   if (play_bin->visualisation) {
       
  1366     GstElement *vis_bin;
       
  1367 
       
  1368     vis_bin =
       
  1369         GST_ELEMENT_CAST (gst_element_get_parent (play_bin->visualisation));
       
  1370 
       
  1371     gst_element_set_state (play_bin->visualisation, GST_STATE_NULL);
       
  1372 
       
  1373     if (vis_bin) {
       
  1374       gst_bin_remove (GST_BIN_CAST (vis_bin), play_bin->visualisation);
       
  1375       gst_object_unref (vis_bin);
       
  1376     }
       
  1377   }
       
  1378 
       
  1379   if (play_bin->frame) {
       
  1380     gst_buffer_unref (play_bin->frame);
       
  1381     play_bin->frame = NULL;
       
  1382   }
       
  1383 
       
  1384   if (play_bin->textoverlay_element) {
       
  1385     gst_object_unref (play_bin->textoverlay_element);
       
  1386     play_bin->textoverlay_element = NULL;
       
  1387   }
       
  1388 }
       
  1389 
       
  1390 /* loop over the streams and set up the pipeline to play this
       
  1391  * media file. First we count the number of audio and video streams.
       
  1392  * If there is no video stream but there exists an audio stream,
       
  1393  * we install a visualisation pipeline.
       
  1394  *
       
  1395  * Also make sure to only connect the first audio and video pad. FIXME
       
  1396  * this should eventually be handled with a tuner interface so that
       
  1397  * one can switch the streams.
       
  1398  *
       
  1399  * This function takes ownership of @sink.*
       
  1400  */
       
  1401 static gboolean
       
  1402 add_sink (GstPlayBin * play_bin, GstElement * sink, GstPad * srcpad,
       
  1403     GstPad * subtitle_pad)
       
  1404 {
       
  1405   GstPad *sinkpad;
       
  1406   GstPadLinkReturn linkres;
       
  1407   GstElement *parent;
       
  1408   GstStateChangeReturn stateret;
       
  1409   GstState state;
       
  1410 
       
  1411   g_return_val_if_fail (sink != NULL, FALSE);
       
  1412 
       
  1413   state = GST_STATE_PAUSED;
       
  1414 
       
  1415   /* this is only for debugging */
       
  1416   parent = gst_pad_get_parent_element (srcpad);
       
  1417   if (parent) {
       
  1418     GST_DEBUG ("Adding sink %" GST_PTR_FORMAT
       
  1419         " with state %d (parent: %d, peer: %d)", sink,
       
  1420         GST_STATE (sink), GST_STATE (play_bin), GST_STATE (parent));
       
  1421     gst_object_unref (parent);
       
  1422   }
       
  1423   gst_bin_add (GST_BIN_CAST (play_bin), sink);
       
  1424 
       
  1425   /* bring it to the required state so we can link to the peer without
       
  1426    * breaking the flow */
       
  1427   stateret = gst_element_set_state (sink, state);
       
  1428   if (stateret == GST_STATE_CHANGE_FAILURE)
       
  1429     goto state_failed;
       
  1430 
       
  1431   /* we found a sink for this stream, now try to install it */
       
  1432   sinkpad = gst_element_get_pad (sink, "sink");
       
  1433   linkres = gst_pad_link (srcpad, sinkpad);
       
  1434   gst_object_unref (sinkpad);
       
  1435 
       
  1436   /* try to link the pad of the sink to the stream */
       
  1437   if (GST_PAD_LINK_FAILED (linkres))
       
  1438     goto link_failed;
       
  1439 
       
  1440   if (GST_IS_PAD (subtitle_pad)) {
       
  1441     sinkpad = gst_element_get_pad (sink, "text_sink");
       
  1442     linkres = gst_pad_link (subtitle_pad, sinkpad);
       
  1443     gst_object_unref (sinkpad);
       
  1444   }
       
  1445 
       
  1446   /* try to link the subtitle pad of the sink to the stream, this is not
       
  1447    * fatal. */
       
  1448   if (GST_PAD_LINK_FAILED (linkres))
       
  1449     goto subtitle_failed;
       
  1450 
       
  1451 done:
       
  1452   /* we got the sink succesfully linked, now keep the sink
       
  1453    * in our internal list */
       
  1454   play_bin->sinks = g_list_prepend (play_bin->sinks, sink);
       
  1455 
       
  1456   return TRUE;
       
  1457 
       
  1458   /* ERRORS */
       
  1459 state_failed:
       
  1460   {
       
  1461     gst_element_set_state (sink, GST_STATE_NULL);
       
  1462     gst_bin_remove (GST_BIN_CAST (play_bin), sink);
       
  1463     GST_DEBUG_OBJECT (play_bin, "state change failure when adding sink");
       
  1464     return FALSE;
       
  1465   }
       
  1466 link_failed:
       
  1467   {
       
  1468     gchar *capsstr;
       
  1469     GstCaps *caps;
       
  1470 
       
  1471     /* could not link this stream */
       
  1472     caps = gst_pad_get_caps (srcpad);
       
  1473     capsstr = gst_caps_to_string (caps);
       
  1474     g_warning ("could not link %s: %d", capsstr, linkres);
       
  1475     GST_DEBUG_OBJECT (play_bin,
       
  1476         "link failed when adding sink, caps %s, reason %d", capsstr, linkres);
       
  1477     g_free (capsstr);
       
  1478     gst_caps_unref (caps);
       
  1479 
       
  1480     gst_element_set_state (sink, GST_STATE_NULL);
       
  1481     gst_bin_remove (GST_BIN_CAST (play_bin), sink);
       
  1482     return FALSE;
       
  1483   }
       
  1484 subtitle_failed:
       
  1485   {
       
  1486     GstCaps *caps;
       
  1487 
       
  1488     /* could not link this stream */
       
  1489     caps = gst_pad_get_caps (subtitle_pad);
       
  1490     GST_WARNING_OBJECT (play_bin, "subtitle link failed when adding sink, "
       
  1491         "caps = %" GST_PTR_FORMAT ", reason %d", caps, linkres);
       
  1492     gst_caps_unref (caps);
       
  1493 
       
  1494     /* not fatal */
       
  1495     goto done;
       
  1496   }
       
  1497 }
       
  1498 
       
  1499 static void
       
  1500 dummy_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
       
  1501 {
       
  1502 }
       
  1503 
       
  1504 static gboolean
       
  1505 setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
       
  1506 {
       
  1507   GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin);
       
  1508   GList *streaminfo = NULL, *s;
       
  1509   gboolean need_vis = FALSE;
       
  1510   gboolean need_text = FALSE;
       
  1511   GstPad *textsrcpad = NULL, *pad = NULL, *origtextsrcpad = NULL;
       
  1512   GstElement *sink;
       
  1513   gboolean res = TRUE;
       
  1514 
       
  1515   /* get rid of existing sinks */
       
  1516   if (play_bin->sinks) {
       
  1517     remove_sinks (play_bin);
       
  1518   }
       
  1519   GST_DEBUG_OBJECT (play_base_bin, "setupsinks");
       
  1520 
       
  1521   /* find out what to do */
       
  1522   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0 &&
       
  1523       group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
       
  1524     need_text = TRUE;
       
  1525   } else if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0 &&
       
  1526       group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 &&
       
  1527       play_bin->visualisation != NULL) {
       
  1528     need_vis = TRUE;
       
  1529   }
       
  1530 
       
  1531   /* now actually connect everything */
       
  1532   g_object_get (G_OBJECT (play_base_bin), "stream-info", &streaminfo, NULL);
       
  1533   for (s = streaminfo; s; s = g_list_next (s)) {
       
  1534     GObject *obj = G_OBJECT (s->data);
       
  1535     gint type;
       
  1536     GstObject *object;
       
  1537 
       
  1538     g_object_get (obj, "type", &type, NULL);
       
  1539     g_object_get (obj, "object", &object, NULL);
       
  1540   }
       
  1541 
       
  1542   /* link audio */
       
  1543   if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) {
       
  1544     if (need_vis) {
       
  1545       sink = gen_vis_element (play_bin);
       
  1546     } else {
       
  1547       sink = gen_audio_element (play_bin);
       
  1548     }
       
  1549     if (!sink)
       
  1550       return FALSE;
       
  1551 
       
  1552     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll,
       
  1553         "src");
       
  1554     res = add_sink (play_bin, sink, pad, NULL);
       
  1555     gst_object_unref (pad);
       
  1556   }
       
  1557 
       
  1558   /* link video */
       
  1559   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
       
  1560     if (need_text) {
       
  1561       GstObject *parent = NULL, *grandparent = NULL;
       
  1562       GstPad *ghost = NULL;
       
  1563 
       
  1564       sink = gen_text_element (play_bin);
       
  1565       textsrcpad =
       
  1566           gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
       
  1567           "src");
       
  1568 
       
  1569       /* This pad is from subtitle-bin, we need to create a ghost pad to have
       
  1570          common grandparents */
       
  1571       parent = gst_object_get_parent (GST_OBJECT_CAST (textsrcpad));
       
  1572       if (!parent) {
       
  1573         GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no parent !");
       
  1574         gst_object_unref (textsrcpad);
       
  1575         textsrcpad = NULL;
       
  1576         goto beach;
       
  1577       }
       
  1578 
       
  1579       grandparent = gst_object_get_parent (parent);
       
  1580       if (!grandparent) {
       
  1581         GST_WARNING_OBJECT (textsrcpad, "subtitle pad has no grandparent !");
       
  1582         gst_object_unref (parent);
       
  1583         gst_object_unref (textsrcpad);
       
  1584         textsrcpad = NULL;
       
  1585         goto beach;
       
  1586       }
       
  1587 
       
  1588       /* We ghost the pad on subtitle_bin only, if the text pad is from the
       
  1589          media demuxer we keep it as it is */
       
  1590       if (!GST_IS_PLAY_BIN (grandparent)) {
       
  1591         GST_DEBUG_OBJECT (textsrcpad, "this subtitle pad is from a subtitle "
       
  1592             "file, ghosting to a suitable hierarchy");
       
  1593         /* Block the pad first, because as soon as we add a ghostpad, the queue
       
  1594          * will try and start pushing */
       
  1595         gst_pad_set_blocked_async (textsrcpad, TRUE, dummy_blocked_cb, NULL);
       
  1596         origtextsrcpad = gst_object_ref (textsrcpad);
       
  1597 
       
  1598         ghost = gst_ghost_pad_new ("text_src", textsrcpad);
       
  1599         if (!GST_IS_PAD (ghost)) {
       
  1600           GST_WARNING_OBJECT (textsrcpad, "failed creating ghost pad for "
       
  1601               "subtitle-bin");
       
  1602           gst_object_unref (parent);
       
  1603           gst_object_unref (grandparent);
       
  1604           gst_object_unref (textsrcpad);
       
  1605           textsrcpad = NULL;
       
  1606           goto beach;
       
  1607         }
       
  1608 
       
  1609         gst_pad_set_active (ghost, TRUE);
       
  1610         if (gst_element_add_pad (GST_ELEMENT_CAST (grandparent), ghost)) {
       
  1611           gst_object_unref (textsrcpad);
       
  1612           textsrcpad = gst_object_ref (ghost);
       
  1613         } else {
       
  1614           GST_WARNING_OBJECT (ghost, "failed adding ghost pad on subtitle-bin");
       
  1615           gst_pad_set_active (ghost, FALSE);
       
  1616           gst_object_unref (ghost);
       
  1617           gst_object_unref (textsrcpad);
       
  1618           textsrcpad = NULL;
       
  1619         }
       
  1620       } else {
       
  1621         GST_DEBUG_OBJECT (textsrcpad, "this subtitle pad is from the demuxer "
       
  1622             "no changes to hierarchy needed");
       
  1623       }
       
  1624 
       
  1625       gst_object_unref (parent);
       
  1626       gst_object_unref (grandparent);
       
  1627     } else {
       
  1628       sink = gen_video_element (play_bin);
       
  1629     }
       
  1630   beach:
       
  1631     if (!sink)
       
  1632       return FALSE;
       
  1633     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll,
       
  1634         "src");
       
  1635     res = add_sink (play_bin, sink, pad, textsrcpad);
       
  1636     gst_object_unref (pad);
       
  1637     if (textsrcpad)
       
  1638       gst_object_unref (textsrcpad);
       
  1639     if (origtextsrcpad) {
       
  1640       gst_pad_set_blocked_async (origtextsrcpad, FALSE, dummy_blocked_cb, NULL);
       
  1641       gst_object_unref (origtextsrcpad);
       
  1642     }
       
  1643   }
       
  1644 
       
  1645   /* remove the sinks now, pipeline get_state will now wait for the
       
  1646    * sinks to preroll */
       
  1647   if (play_bin->fakesink) {
       
  1648     gst_element_set_state (play_bin->fakesink, GST_STATE_NULL);
       
  1649     gst_bin_remove (GST_BIN_CAST (play_bin), play_bin->fakesink);
       
  1650     play_bin->fakesink = NULL;
       
  1651   }
       
  1652 
       
  1653   return res;
       
  1654 }
       
  1655 
       
  1656 static void
       
  1657 playbin_set_subtitles_visible (GstPlayBaseBin * play_base_bin, gboolean visible)
       
  1658 {
       
  1659   GstPlayBin *playbin = GST_PLAY_BIN (play_base_bin);
       
  1660 
       
  1661   /* we're ignoring the case of someone setting the 'current-text' property
       
  1662    * before textoverlay is set up (which is probably okay, since playbasebin
       
  1663    * will just select the first subtitle stream as active stream regardless) */
       
  1664   if (playbin->textoverlay_element != NULL) {
       
  1665     GST_LOG_OBJECT (playbin, "setting subtitle visibility to %d", visible);
       
  1666     g_object_set (playbin->textoverlay_element, "silent", !visible, NULL);
       
  1667   }
       
  1668 }
       
  1669 
       
  1670 /* Send an event to our sinks until one of them works; don't then send to the
       
  1671  * remaining sinks (unlike GstBin)
       
  1672  */
       
  1673 static gboolean
       
  1674 gst_play_bin_send_event_to_sink (GstPlayBin * play_bin, GstEvent * event)
       
  1675 {
       
  1676   GList *sinks = play_bin->sinks;
       
  1677   gboolean res = TRUE;
       
  1678 
       
  1679   while (sinks) {
       
  1680     GstElement *sink = GST_ELEMENT_CAST (sinks->data);
       
  1681 
       
  1682     gst_event_ref (event);
       
  1683     if ((res = gst_element_send_event (sink, event))) {
       
  1684       GST_DEBUG_OBJECT (play_bin,
       
  1685           "Sent event succesfully to sink %" GST_PTR_FORMAT, sink);
       
  1686       break;
       
  1687     }
       
  1688     GST_DEBUG_OBJECT (play_bin,
       
  1689         "Event failed when sent to sink %" GST_PTR_FORMAT, sink);
       
  1690 
       
  1691     sinks = g_list_next (sinks);
       
  1692   }
       
  1693 
       
  1694   gst_event_unref (event);
       
  1695 
       
  1696   return res;
       
  1697 }
       
  1698 
       
  1699 /* We only want to send the event to a single sink (overriding GstBin's
       
  1700  * behaviour), but we want to keep GstPipeline's behaviour - wrapping seek
       
  1701  * events appropriately. So, this is a messy duplication of code. */
       
  1702 static gboolean
       
  1703 gst_play_bin_send_event (GstElement * element, GstEvent * event)
       
  1704 {
       
  1705   gboolean res = FALSE;
       
  1706   GstEventType event_type = GST_EVENT_TYPE (event);
       
  1707 
       
  1708   switch (event_type) {
       
  1709     case GST_EVENT_SEEK:
       
  1710       GST_DEBUG_OBJECT (element, "Sending seek event to a sink");
       
  1711       res = gst_play_bin_send_event_to_sink (GST_PLAY_BIN (element), event);
       
  1712       break;
       
  1713     default:
       
  1714       res = parent_class->send_event (element, event);
       
  1715       break;
       
  1716   }
       
  1717 
       
  1718   return res;
       
  1719 }
       
  1720 
       
  1721 static void
       
  1722 value_list_append_structure_list (GValue * list_val, GstStructure ** first,
       
  1723     GList * structure_list)
       
  1724 {
       
  1725   GList *l;
       
  1726 
       
  1727   for (l = structure_list; l != NULL; l = l->next) {
       
  1728     GValue val = { 0, };
       
  1729 
       
  1730     if (*first == NULL)
       
  1731       *first = gst_structure_copy ((GstStructure *) l->data);
       
  1732 
       
  1733     g_value_init (&val, GST_TYPE_STRUCTURE);
       
  1734     g_value_take_boxed (&val, gst_structure_copy ((GstStructure *) l->data));
       
  1735     gst_value_list_append_value (list_val, &val);
       
  1736     g_value_unset (&val);
       
  1737   }
       
  1738 }
       
  1739 
       
  1740 /* if it's a redirect message with multiple redirect locations we might
       
  1741  * want to pick a different 'best' location depending on the required
       
  1742  * bitrates and the connection speed */
       
  1743 static GstMessage *
       
  1744 gst_play_bin_handle_redirect_message (GstPlayBin * playbin, GstMessage * msg)
       
  1745 {
       
  1746   const GValue *locations_list, *location_val;
       
  1747   GstMessage *new_msg;
       
  1748   GstStructure *new_structure = NULL;
       
  1749   GList *l_good = NULL, *l_neutral = NULL, *l_bad = NULL;
       
  1750   GValue new_list = { 0, };
       
  1751   guint size, i;
       
  1752   GstPlayBaseBin *playbasebin = GST_PLAY_BASE_BIN (playbin);
       
  1753   guint connection_speed = playbasebin->connection_speed;
       
  1754 
       
  1755   GST_DEBUG_OBJECT (playbin, "redirect message: %" GST_PTR_FORMAT, msg);
       
  1756   GST_DEBUG_OBJECT (playbin, "connection speed: %u", connection_speed);
       
  1757 
       
  1758   if (connection_speed == 0 || msg->structure == NULL)
       
  1759     return msg;
       
  1760 
       
  1761   locations_list = gst_structure_get_value (msg->structure, "locations");
       
  1762   if (locations_list == NULL)
       
  1763     return msg;
       
  1764 
       
  1765   size = gst_value_list_get_size (locations_list);
       
  1766   if (size < 2)
       
  1767     return msg;
       
  1768 
       
  1769   /* maintain existing order as much as possible, just sort references
       
  1770    * with too high a bitrate to the end (the assumption being that if
       
  1771    * bitrates are given they are given for all interesting streams and
       
  1772    * that the you-need-at-least-version-xyz redirect has the same bitrate
       
  1773    * as the lowest referenced redirect alternative) */
       
  1774   for (i = 0; i < size; ++i) {
       
  1775     const GstStructure *s;
       
  1776     gint bitrate = 0;
       
  1777 
       
  1778     location_val = gst_value_list_get_value (locations_list, i);
       
  1779     s = (const GstStructure *) g_value_get_boxed (location_val);
       
  1780     if (!gst_structure_get_int (s, "minimum-bitrate", &bitrate) || bitrate <= 0) {
       
  1781       GST_DEBUG_OBJECT (playbin, "no bitrate: %" GST_PTR_FORMAT, s);
       
  1782       l_neutral = g_list_append (l_neutral, (gpointer) s);
       
  1783     } else if (bitrate > connection_speed) {
       
  1784       GST_DEBUG_OBJECT (playbin, "bitrate too high: %" GST_PTR_FORMAT, s);
       
  1785       l_bad = g_list_append (l_bad, (gpointer) s);
       
  1786     } else if (bitrate <= connection_speed) {
       
  1787       GST_DEBUG_OBJECT (playbin, "bitrate OK: %" GST_PTR_FORMAT, s);
       
  1788       l_good = g_list_append (l_good, (gpointer) s);
       
  1789     }
       
  1790   }
       
  1791 
       
  1792   g_value_init (&new_list, GST_TYPE_LIST);
       
  1793   value_list_append_structure_list (&new_list, &new_structure, l_good);
       
  1794   value_list_append_structure_list (&new_list, &new_structure, l_neutral);
       
  1795   value_list_append_structure_list (&new_list, &new_structure, l_bad);
       
  1796   gst_structure_set_value (new_structure, "locations", &new_list);
       
  1797   g_value_unset (&new_list);
       
  1798 
       
  1799   g_list_free (l_good);
       
  1800   g_list_free (l_neutral);
       
  1801   g_list_free (l_bad);
       
  1802 
       
  1803   new_msg = gst_message_new_element (msg->src, new_structure);
       
  1804   gst_message_unref (msg);
       
  1805 
       
  1806   GST_DEBUG_OBJECT (playbin, "new redirect message: %" GST_PTR_FORMAT, new_msg);
       
  1807   return new_msg;
       
  1808 }
       
  1809 
       
  1810 static void
       
  1811 gst_play_bin_handle_message (GstBin * bin, GstMessage * msg)
       
  1812 {
       
  1813   if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ELEMENT && msg->structure != NULL
       
  1814       && gst_structure_has_name (msg->structure, "redirect")) {
       
  1815     msg = gst_play_bin_handle_redirect_message (GST_PLAY_BIN (bin), msg);
       
  1816   }
       
  1817 
       
  1818   GST_BIN_CLASS (parent_class)->handle_message (bin, msg);
       
  1819 }
       
  1820 
       
  1821 static GstStateChangeReturn
       
  1822 gst_play_bin_change_state (GstElement * element, GstStateChange transition)
       
  1823 {
       
  1824   GstStateChangeReturn ret;
       
  1825   GstPlayBin *play_bin;
       
  1826 
       
  1827   play_bin = GST_PLAY_BIN (element);
       
  1828 
       
  1829 
       
  1830   switch (transition) {
       
  1831     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  1832       /* this really is the easiest way to make the state change return
       
  1833        * ASYNC until we added the sinks */
       
  1834       if (!play_bin->fakesink) {
       
  1835         play_bin->fakesink = gst_element_factory_make ("fakesink", "test");
       
  1836         gst_bin_add (GST_BIN_CAST (play_bin), play_bin->fakesink);
       
  1837       }
       
  1838       break;
       
  1839     default:
       
  1840       break;
       
  1841   }
       
  1842 
       
  1843   ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition);
       
  1844   if (ret == GST_STATE_CHANGE_FAILURE)
       
  1845     return ret;
       
  1846 
       
  1847   switch (transition) {
       
  1848     case GST_STATE_CHANGE_READY_TO_PAUSED:
       
  1849       /* remember us being a live pipeline */
       
  1850       play_bin->is_live = (ret == GST_STATE_CHANGE_NO_PREROLL);
       
  1851       GST_DEBUG_OBJECT (play_bin, "is live: %d", play_bin->is_live);
       
  1852       break;
       
  1853     case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
       
  1854       /* FIXME Release audio device when we implement that */
       
  1855       break;
       
  1856     case GST_STATE_CHANGE_PAUSED_TO_READY:
       
  1857     case GST_STATE_CHANGE_READY_TO_NULL:
       
  1858       /* remove sinks we added */
       
  1859       remove_sinks (play_bin);
       
  1860       /* and there might be a fakesink we need to clean up now */
       
  1861       if (play_bin->fakesink) {
       
  1862         gst_element_set_state (play_bin->fakesink, GST_STATE_NULL);
       
  1863         gst_bin_remove (GST_BIN_CAST (play_bin), play_bin->fakesink);
       
  1864         play_bin->fakesink = NULL;
       
  1865       }
       
  1866       break;
       
  1867     default:
       
  1868       break;
       
  1869   }
       
  1870 
       
  1871   return ret;
       
  1872 }
       
  1873 #ifdef __SYMBIAN32__
       
  1874 EXPORT_C
       
  1875 #endif
       
  1876 
       
  1877 
       
  1878 gboolean
       
  1879 gst_play_bin_plugin_init (GstPlugin * plugin)
       
  1880 {
       
  1881   GST_DEBUG_CATEGORY_INIT (gst_play_bin_debug, "playbin", 0, "play bin");
       
  1882 
       
  1883   return gst_element_register (plugin, "playbin", GST_RANK_NONE,
       
  1884       GST_TYPE_PLAY_BIN);
       
  1885 }
       
  1886 
       
  1887 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
       
  1888     GST_VERSION_MINOR,
       
  1889     "playbin",
       
  1890     "plays a audio file",
       
  1891     gst_play_bin_plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
       
  1892 
       
  1893 #ifdef __SYMBIAN32__
       
  1894 EXPORT_C 
       
  1895 #endif
       
  1896 GstPluginDesc* _GST_PLUGIN_DESC()
       
  1897 {
       
  1898 	return &gst_plugin_desc;
       
  1899 }
       
  1900