gst_plugins_base/gst/playback/gstplaybin.c
branchRCL_3
changeset 30 7e817e7e631c
parent 29 567bb019e3e3
equal deleted inserted replaced
29:567bb019e3e3 30:7e817e7e631c
    18  */
    18  */
    19 
    19 
    20 /**
    20 /**
    21  * SECTION:element-playbin
    21  * SECTION:element-playbin
    22  *
    22  *
       
    23  * <refsect2>
       
    24  * <para>
    23  * Playbin provides a stand-alone everything-in-one abstraction for an
    25  * Playbin provides a stand-alone everything-in-one abstraction for an
    24  * audio and/or video player.
    26  * audio and/or video player.
    25  *
    27  * </para>
       
    28  * <para>
    26  * It can handle both audio and video files and features
    29  * It can handle both audio and video files and features
    27  * <itemizedlist>
    30  * <itemizedlist>
    28  * <listitem>
    31  * <listitem>
    29  * automatic file type recognition and based on that automatic
    32  * automatic file type recognition and based on that automatic
    30  * selection and usage of the right audio/video/subtitle demuxers/decoders
    33  * selection and usage of the right audio/video/subtitle demuxers/decoders
    49  * </listitem>
    52  * </listitem>
    50  * <listitem>
    53  * <listitem>
    51  * volume control
    54  * volume control
    52  * </listitem>
    55  * </listitem>
    53  * </itemizedlist>
    56  * </itemizedlist>
    54  *
    57  * </para>
    55  * <refsect2>
       
    56  * <title>Usage</title>
    58  * <title>Usage</title>
    57  * <para>
    59  * <para>
    58  * A playbin element can be created just like any other element using
    60  * A playbin element can be created just like any other element using
    59  * gst_element_factory_make(). The file/URI to play should be set via the #GstPlayBin:uri
    61  * gst_element_factory_make(). The file/URI to play should be set via the "uri"
    60  * property. This must be an absolute URI, relative file paths are not allowed.
    62  * property. This must be an absolute URI, relative file paths are not allowed.
    61  * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
    63  * Example URIs are file:///home/joe/movie.avi or http://www.joedoe.com/foo.ogg
    62  *
    64  * </para>
       
    65  * <para>
    63  * Playbin is a #GstPipeline. It will notify the application of everything
    66  * Playbin is a #GstPipeline. It will notify the application of everything
    64  * that's happening (errors, end of stream, tags found, state changes, etc.)
    67  * that's happening (errors, end of stream, tags found, state changes, etc.)
    65  * by posting messages on its #GstBus. The application needs to watch the
    68  * by posting messages on its #GstBus. The application needs to watch the
    66  * bus.
    69  * bus.
    67  *
    70  * </para>
       
    71  * <para>
    68  * Playback can be initiated by setting the element to PLAYING state using
    72  * Playback can be initiated by setting the element to PLAYING state using
    69  * gst_element_set_state(). Note that the state change will take place in
    73  * gst_element_set_state(). Note that the state change will take place in
    70  * the background in a separate thread, when the function returns playback
    74  * the background in a separate thread, when the function returns playback
    71  * is probably not happening yet and any errors might not have occured yet.
    75  * is probably not happening yet and any errors might not have occured yet.
    72  * Applications using playbin should ideally be written to deal with things
    76  * Applications using playbin should ideally be written to deal with things
    73  * completely asynchroneous.
    77  * completely asynchroneous.
    74  *
    78  * </para>
       
    79  * <para>
    75  * When playback has finished (an EOS message has been received on the bus)
    80  * When playback has finished (an EOS message has been received on the bus)
    76  * or an error has occured (an ERROR message has been received on the bus) or
    81  * or an error has occured (an ERROR message has been received on the bus) or
    77  * the user wants to play a different track, playbin should be set back to
    82  * the user wants to play a different track, playbin should be set back to
    78  * READY or NULL state, then the #GstPlayBin:uri property should be set to the
    83  * READY or NULL state, then the "uri" property should be set to the new
    79  * new location and then playbin be set to PLAYING state again.
    84  * location and then playbin be set to PLAYING state again.
    80  *
    85  * </para>
       
    86  * <para>
    81  * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
    87  * Seeking can be done using gst_element_seek_simple() or gst_element_seek()
    82  * on the playbin element. Again, the seek will not be executed
    88  * on the playbin element. Again, the seek will not be executed
    83  * instantaneously, but will be done in a background thread. When the seek
    89  * instantaneously, but will be done in a background thread. When the seek
    84  * call returns the seek will most likely still be in process. An application
    90  * call returns the seek will most likely still be in process. An application
    85  * may wait for the seek to finish (or fail) using gst_element_get_state() with
    91  * may wait for the seek to finish (or fail) using gst_element_get_state() with
    86  * -1 as the timeout, but this will block the user interface and is not
    92  * -1 as the timeout, but this will block the user interface and is not
    87  * recommended at all.
    93  * recommended at all.
    88  *
    94  * </para>
       
    95  * <para>
    89  * Applications may query the current position and duration of the stream
    96  * Applications may query the current position and duration of the stream
    90  * via gst_element_query_position() and gst_element_query_duration() and
    97  * via gst_element_query_position() and gst_element_query_duration() and
    91  * setting the format passed to GST_FORMAT_TIME. If the query was successful,
    98  * setting the format passed to GST_FORMAT_TIME. If the query was successful,
    92  * the duration or position will have been returned in units of nanoseconds.
    99  * the duration or position will have been returned in units of nanoseconds.
    93  * </para>
   100  * </para>
    94  * </refsect2>
       
    95  * <refsect2>
       
    96  * <title>Advanced Usage: specifying the audio and video sink</title>
   101  * <title>Advanced Usage: specifying the audio and video sink</title>
    97  * <para>
   102  * <para>
    98  * By default, if no audio sink or video sink has been specified via the
   103  * By default, if no audio sink or video sink has been specified via the
    99  * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property, playbin will use
   104  * "audio-sink" or "video-sink" property, playbin will use the autoaudiosink
   100  * the autoaudiosink and autovideosink elements to find the first-best
   105  * and autovideosink elements to find the first-best available output method.
   101  * available output method.
       
   102  * This should work in most cases, but is not always desirable. Often either
   106  * This should work in most cases, but is not always desirable. Often either
   103  * the user or application might want to specify more explicitly what to use
   107  * the user or application might want to specify more explicitly what to use
   104  * for audio and video output.
   108  * for audio and video output.
   105  *
   109  * </para>
       
   110  * <para>
   106  * If the application wants more control over how audio or video should be
   111  * If the application wants more control over how audio or video should be
   107  * output, it may create the audio/video sink elements itself (for example
   112  * output, it may create the audio/video sink elements itself (for example
   108  * using gst_element_factory_make()) and provide them to playbin using the
   113  * using gst_element_factory_make()) and provide them to playbin using the
   109  * #GstPlayBin:audio-sink or #GstPlayBin:video-sink property.
   114  * "audio-sink" or "video-sink" property.
   110  *
   115  * </para>
       
   116  * <para>
   111  * GNOME-based applications, for example, will usually want to create
   117  * GNOME-based applications, for example, will usually want to create
   112  * gconfaudiosink and gconfvideosink elements and make playbin use those,
   118  * gconfaudiosink and gconfvideosink elements and make playbin use those,
   113  * so that output happens to whatever the user has configured in the GNOME
   119  * so that output happens to whatever the user has configured in the GNOME
   114  * Multimedia System Selector confinguration dialog.
   120  * Multimedia System Selector confinguration dialog.
   115  *
   121  * </para>
       
   122  * <para>
   116  * The sink elements do not necessarily need to be ready-made sinks. It is
   123  * The sink elements do not necessarily need to be ready-made sinks. It is
   117  * possible to create container elements that look like a sink to playbin,
   124  * possible to create container elements that look like a sink to playbin,
   118  * but in reality contain a number of custom elements linked together. This
   125  * but in reality contain a number of custom elements linked together. This
   119  * can be achieved by creating a #GstBin and putting elements in there and
   126  * can be achieved by creating a #GstBin and putting elements in there and
   120  * linking them, and then creating a sink #GstGhostPad for the bin and pointing
   127  * linking them, and then creating a sink #GstGhostPad for the bin and pointing
   121  * it to the sink pad of the first element within the bin. This can be used
   128  * it to the sink pad of the first element within the bin. This can be used
   122  * for a number of purposes, for example to force output to a particular
   129  * for a number of purposes, for example to force output to a particular
   123  * format or to modify or observe the data before it is output.
   130  * format or to modify or observe the data before it is output.
   124  *
   131  * </para>
       
   132  * <para>
   125  * It is also possible to 'suppress' audio and/or video output by using
   133  * It is also possible to 'suppress' audio and/or video output by using
   126  * 'fakesink' elements (or capture it from there using the fakesink element's
   134  * 'fakesink' elements (or capture it from there using the fakesink element's
   127  * "handoff" signal, which, nota bene, is fired from the streaming thread!).
   135  * "handoff" signal, which, nota bene, is fired from the streaming thread!).
   128  * </para>
   136  * </para>
   129  * </refsect2>
       
   130  * <refsect2>
       
   131  * <title>Retrieving Tags and Other Meta Data</title>
   137  * <title>Retrieving Tags and Other Meta Data</title>
   132  * <para>
   138  * <para>
   133  * Most of the common meta data (artist, title, etc.) can be retrieved by
   139  * Most of the common meta data (artist, title, etc.) can be retrieved by
   134  * watching for TAG messages on the pipeline's bus (see above).
   140  * watching for TAG messages on the pipeline's bus (see above).
   135  *
   141  * </para>
       
   142  * <para>
   136  * Other more specific meta information like width/height/framerate of video
   143  * Other more specific meta information like width/height/framerate of video
   137  * streams or samplerate/number of channels of audio streams can be obtained
   144  * streams or samplerate/number of channels of audio streams can be obtained
   138  * using the  #GstPlayBaseBin:stream-info property, which will return a GList of
   145  * using the "stream-info" property, which will return a GList of stream info
   139  * stream info objects, one for each stream. These are opaque objects that can
   146  * objects, one for each stream. These are opaque objects that can only be
   140  * only be accessed via the standard GObject property interface, ie. g_object_get().
   147  * accessed via the standard GObject property interface, ie. g_object_get().
   141  * Each stream info object has the following properties:
   148  * Each stream info object has the following properties:
   142  * <itemizedlist>
   149  * <itemizedlist>
   143  * <listitem>"object" (GstObject) (the decoder source pad usually)</listitem>
   150  * <listitem>"object" (GstObject) (the decoder source pad usually)</listitem>
   144  * <listitem>"type" (enum) (if this is an audio/video/subtitle stream)</listitem>
   151  * <listitem>"type" (enum) (if this is an audio/video/subtitle stream)</listitem>
   145  * <listitem>"decoder" (string) (name of decoder used to decode this stream)</listitem>
   152  * <listitem>"decoder" (string) (name of decoder used to decode this stream)</listitem>
   146  * <listitem>"mute" (boolean) (to mute or unmute this stream)</listitem>
   153  * <listitem>"mute" (boolean) (to mute or unmute this stream)</listitem>
   147  * <listitem>"caps" (GstCaps) (caps of the decoded stream)</listitem>
   154  * <listitem>"caps" (GstCaps) (caps of the decoded stream)</listitem>
   148  * <listitem>"language-code" (string) (ISO-639 language code for this stream, mostly used for audio/subtitle streams)</listitem>
   155  * <listitem>"language-code" (string) (ISO-639 language code for this stream, mostly used for audio/subtitle streams)</listitem>
   149  * <listitem>"codec" (string) (format this stream was encoded in)</listitem>
   156  * <listitem>"codec" (string) (format this stream was encoded in)</listitem>
   150  * </itemizedlist>
   157  * </itemizedlist>
   151  * Stream information from the #GstPlayBaseBin:stream-info property is best queried once
   158  * Stream information from the stream-info properties is best queried once
   152  * playbin has changed into PAUSED or PLAYING state (which can be detected
   159  * playbin has changed into PAUSED or PLAYING state (which can be detected
   153  * via a state-changed message on the #GstBus where old_state=READY and
   160  * via a state-changed message on the bus where old_state=READY and
   154  * new_state=PAUSED), since before that the list might not be complete yet or
   161  * new_state=PAUSED), since before that the list might not be complete yet or
   155  * not contain all available information (like language-codes).
   162  * not contain all available information (like language-codes).
   156  * </para>
   163  * </para>
   157  * </refsect2>
       
   158  * <refsect2>
       
   159  * <title>Buffering</title>
   164  * <title>Buffering</title>
       
   165  * <para>
   160  * Playbin handles buffering automatically for the most part, but applications
   166  * Playbin handles buffering automatically for the most part, but applications
   161  * need to handle parts of the buffering process as well. Whenever playbin is
   167  * need to handle parts of the buffering process as well. Whenever playbin is
   162  * buffering, it will post BUFFERING messages on the bus with a percentage
   168  * buffering, it will post BUFFERING messages on the bus with a percentage
   163  * value that shows the progress of the buffering process. Applications need
   169  * value that shows the progress of the buffering process. Applications need
   164  * to set playbin to PLAYING or PAUSED state in response to these messages.
   170  * to set playbin to PLAYING or PAUSED state in response to these messages.
   165  * They may also want to convey the buffering progress to the user in some
   171  * They may also want to convey the buffering progress to the user in some
   166  * way. Here is how to extract the percentage information from the message
   172  * way. Here is how to extract the percentage information from the message
   167  * (requires GStreamer >= 0.10.11):
   173  * (requires GStreamer >= 0.10.11):
   168  * |[
   174  * </para>
       
   175  * <para>
       
   176  * <programlisting>
   169  * switch (GST_MESSAGE_TYPE (msg)) {
   177  * switch (GST_MESSAGE_TYPE (msg)) {
   170  *   case GST_MESSAGE_BUFFERING: {
   178  *   case GST_MESSAGE_BUFFERING: {
   171  *     gint percent = 0;
   179  *     gint percent = 0;
   172  *     gst_message_parse_buffering (msg, &amp;percent);
   180  *     gst_message_parse_buffering (msg, &amp;percent);
   173  *     g_print ("Buffering (%%u percent done)", percent);
   181  *     g_print ("Buffering (%%u percent done)", percent);
   174  *     break;
   182  *     break;
   175  *   }
   183  *   }
   176  *   ...
   184  *   ...
   177  * }
   185  * }
   178  * ]|
   186  * </programlisting>
   179  * Note that applications should keep/set the pipeline in the PAUSED state when
   187  * Note that applications should keep/set the pipeline in the PAUSED state when
   180  * a BUFFERING message is received with a buffer percent value < 100 and set
   188  * a BUFFERING message is received with a buffer percent value < 100 and set
   181  * the pipeline back to PLAYING state when a BUFFERING message with a value
   189  * the pipeline back to PLAYING state when a BUFFERING message with a value
   182  * of 100 percent is received (if PLAYING is the desired state, that is).
   190  * of 100 percent is received (if PLAYING is the desired state, that is).
   183  * </refsect2>
   191  * </para>
   184  * <refsect2>
       
   185  * <title>Embedding the video window in your application</title>
   192  * <title>Embedding the video window in your application</title>
       
   193  * <para>
   186  * By default, playbin (or rather the video sinks used) will create their own
   194  * By default, playbin (or rather the video sinks used) will create their own
   187  * window. Applications will usually want to force output to a window of their
   195  * window. Applications will usually want to force output to a window of their
   188  * own, however. This can be done using the #GstXOverlay interface, which most
   196  * own, however. This can be done using the GstXOverlay interface, which most
   189  * video sinks implement. See the documentation there for more details.
   197  * video sinks implement. See the documentation there for more details.
   190  * </refsect2>
   198  * </para>
   191  * <refsect2>
       
   192  * <title>Specifying which CD/DVD device to use</title>
   199  * <title>Specifying which CD/DVD device to use</title>
       
   200  * <para>
   193  * The device to use for CDs/DVDs needs to be set on the source element
   201  * The device to use for CDs/DVDs needs to be set on the source element
   194  * playbin creates before it is opened. The only way to do this at the moment
   202  * playbin creates before it is opened. The only way to do this at the moment
   195  * is to connect to playbin's "notify::source" signal, which will be emitted
   203  * is to connect to playbin's "notify::source" signal, which will be emitted
   196  * by playbin when it has created the source element for a particular URI.
   204  * by playbin when it has created the source element for a particular URI.
   197  * In the signal callback you can check if the source element has a "device"
   205  * In the signal callback you can check if the source element has a "device"
   198  * property and set it appropriately. In future ways might be added to specify
   206  * property and set it appropriately. In future ways might be added to specify
   199  * the device as part of the URI, but at the time of writing this is not
   207  * the device as part of the URI, but at the time of writing this is not
   200  * possible yet.
   208  * possible yet.
   201  * </refsect2>
   209  * </para>
   202  * <refsect2>
       
   203  * <title>Examples</title>
   210  * <title>Examples</title>
   204  * |[
   211  * <para>
       
   212  * Here is a simple pipeline to play back a video or audio file:
       
   213  * <programlisting>
   205  * gst-launch -v playbin uri=file:///path/to/somefile.avi
   214  * gst-launch -v playbin uri=file:///path/to/somefile.avi
   206  * ]| This will play back the given AVI video file, given that the video and
   215  * </programlisting>
       
   216  * This will play back the given AVI video file, given that the video and
   207  * audio decoders required to decode the content are installed. Since no
   217  * audio decoders required to decode the content are installed. Since no
   208  * special audio sink or video sink is supplied (not possible via gst-launch),
   218  * special audio sink or video sink is supplied (not possible via gst-launch),
   209  * playbin will try to find a suitable audio and video sink automatically
   219  * playbin will try to find a suitable audio and video sink automatically
   210  * using the autoaudiosink and autovideosink elements.
   220  * using the autoaudiosink and autovideosink elements.
   211  * |[
   221  * </para>
       
   222  * <para>
       
   223  * Here is a another pipeline to play track 4 of an audio CD:
       
   224  * <programlisting>
   212  * gst-launch -v playbin uri=cdda://4
   225  * gst-launch -v playbin uri=cdda://4
   213  * ]| This will play back track 4 on an audio CD in your disc drive (assuming
   226  * </programlisting>
       
   227  * This will play back track 4 on an audio CD in your disc drive (assuming
   214  * the drive is detected automatically by the plugin).
   228  * the drive is detected automatically by the plugin).
   215  * |[
   229  * </para>
       
   230  * <para>
       
   231  * Here is a another pipeline to play title 1 of a DVD:
       
   232  * <programlisting>
   216  * gst-launch -v playbin uri=dvd://1
   233  * gst-launch -v playbin uri=dvd://1
   217  * ]| This will play back title 1 of a DVD in your disc drive (assuming
   234  * </programlisting>
       
   235  * This will play back title 1 of a DVD in your disc drive (assuming
   218  * the drive is detected automatically by the plugin).
   236  * the drive is detected automatically by the plugin).
       
   237  * </para>
   219  * </refsect2>
   238  * </refsect2>
   220  */
   239  */
   221 
   240 
   222 #ifdef HAVE_CONFIG_H
   241 #ifdef HAVE_CONFIG_H
   223 #include "config.h"
   242 #include "config.h"
   228 
   247 
   229 #include <gst/gst-i18n-plugin.h>
   248 #include <gst/gst-i18n-plugin.h>
   230 #include <gst/pbutils/pbutils.h>
   249 #include <gst/pbutils/pbutils.h>
   231 
   250 
   232 #include "gstplaybasebin.h"
   251 #include "gstplaybasebin.h"
       
   252 
       
   253 #ifdef __SYMBIAN32__
       
   254 #include <glib_global.h>
       
   255 #endif
   233 
   256 
   234 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
   257 GST_DEBUG_CATEGORY_STATIC (gst_play_bin_debug);
   235 #define GST_CAT_DEFAULT gst_play_bin_debug
   258 #define GST_CAT_DEFAULT gst_play_bin_debug
   236 
   259 
   237 #define GST_TYPE_PLAY_BIN               (gst_play_bin_get_type())
   260 #define GST_TYPE_PLAY_BIN               (gst_play_bin_get_type())
   243 #define VOLUME_MAX_DOUBLE 10.0
   266 #define VOLUME_MAX_DOUBLE 10.0
   244 
   267 
   245 typedef struct _GstPlayBin GstPlayBin;
   268 typedef struct _GstPlayBin GstPlayBin;
   246 typedef struct _GstPlayBinClass GstPlayBinClass;
   269 typedef struct _GstPlayBinClass GstPlayBinClass;
   247 
   270 
   248 /**
       
   249  * GstPlayBin:
       
   250  *
       
   251  * High-level player element
       
   252  */
       
   253 struct _GstPlayBin
   271 struct _GstPlayBin
   254 {
   272 {
   255   GstPlayBaseBin parent;
   273   GstPlayBaseBin parent;
   256 
   274 
   257   /* the configurable elements */
   275   /* the configurable elements */
   260   GstElement *video_sink;
   278   GstElement *video_sink;
   261   GstElement *visualisation;
   279   GstElement *visualisation;
   262   GstElement *pending_visualisation;
   280   GstElement *pending_visualisation;
   263   GstElement *volume_element;
   281   GstElement *volume_element;
   264   GstElement *textoverlay_element;
   282   GstElement *textoverlay_element;
   265   GstElement *spu_element;
       
   266   gfloat volume;
   283   gfloat volume;
   267 
   284 
   268   /* these are the currently active sinks */
   285   /* these are the currently active sinks */
   269   GList *sinks;
   286   GList *sinks;
   270 
   287 
   311 static gboolean setup_sinks (GstPlayBaseBin * play_base_bin,
   328 static gboolean setup_sinks (GstPlayBaseBin * play_base_bin,
   312     GstPlayBaseGroup * group);
   329     GstPlayBaseGroup * group);
   313 static void remove_sinks (GstPlayBin * play_bin);
   330 static void remove_sinks (GstPlayBin * play_bin);
   314 static void playbin_set_subtitles_visible (GstPlayBaseBin * play_base_bin,
   331 static void playbin_set_subtitles_visible (GstPlayBaseBin * play_base_bin,
   315     gboolean visible);
   332     gboolean visible);
   316 static void playbin_set_audio_mute (GstPlayBaseBin * play_base_bin,
       
   317     gboolean mute);
       
   318 
   333 
   319 static void gst_play_bin_set_property (GObject * object, guint prop_id,
   334 static void gst_play_bin_set_property (GObject * object, guint prop_id,
   320     const GValue * value, GParamSpec * spec);
   335     const GValue * value, GParamSpec * spec);
   321 static void gst_play_bin_get_property (GObject * object, guint prop_id,
   336 static void gst_play_bin_get_property (GObject * object, guint prop_id,
   322     GValue * value, GParamSpec * spec);
   337     GValue * value, GParamSpec * spec);
   383   gobject_klass->get_property = gst_play_bin_get_property;
   398   gobject_klass->get_property = gst_play_bin_get_property;
   384 
   399 
   385   g_object_class_install_property (gobject_klass, ARG_VIDEO_SINK,
   400   g_object_class_install_property (gobject_klass, ARG_VIDEO_SINK,
   386       g_param_spec_object ("video-sink", "Video Sink",
   401       g_param_spec_object ("video-sink", "Video Sink",
   387           "the video output element to use (NULL = default sink)",
   402           "the video output element to use (NULL = default sink)",
   388           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   403           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
   389   g_object_class_install_property (gobject_klass, ARG_AUDIO_SINK,
   404   g_object_class_install_property (gobject_klass, ARG_AUDIO_SINK,
   390       g_param_spec_object ("audio-sink", "Audio Sink",
   405       g_param_spec_object ("audio-sink", "Audio Sink",
   391           "the audio output element to use (NULL = default sink)",
   406           "the audio output element to use (NULL = default sink)",
   392           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   407           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
   393   g_object_class_install_property (gobject_klass, ARG_VIS_PLUGIN,
   408   g_object_class_install_property (gobject_klass, ARG_VIS_PLUGIN,
   394       g_param_spec_object ("vis-plugin", "Vis plugin",
   409       g_param_spec_object ("vis-plugin", "Vis plugin",
   395           "the visualization element to use (NULL = none)",
   410           "the visualization element to use (NULL = none)",
   396           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   411           GST_TYPE_ELEMENT, G_PARAM_READWRITE));
   397   g_object_class_install_property (gobject_klass, ARG_VOLUME,
   412   g_object_class_install_property (gobject_klass, ARG_VOLUME,
   398       g_param_spec_double ("volume", "volume", "volume",
   413       g_param_spec_double ("volume", "volume", "volume",
   399           0.0, VOLUME_MAX_DOUBLE, 1.0,
   414           0.0, VOLUME_MAX_DOUBLE, 1.0, G_PARAM_READWRITE));
   400           G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
       
   401   g_object_class_install_property (gobject_klass, ARG_FRAME,
   415   g_object_class_install_property (gobject_klass, ARG_FRAME,
   402       gst_param_spec_mini_object ("frame", "Frame",
   416       gst_param_spec_mini_object ("frame", "Frame",
   403           "The last frame (NULL = no video available)", GST_TYPE_BUFFER,
   417           "The last frame (NULL = no video available)",
   404           G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
   418           GST_TYPE_BUFFER, G_PARAM_READABLE));
   405   g_object_class_install_property (gobject_klass, ARG_FONT_DESC,
   419   g_object_class_install_property (gobject_klass, ARG_FONT_DESC,
   406       g_param_spec_string ("subtitle-font-desc", "Subtitle font description",
   420       g_param_spec_string ("subtitle-font-desc",
   407           "Pango font description of font " "to be used for subtitle rendering",
   421           "Subtitle font description",
   408           NULL, G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
   422           "Pango font description of font "
       
   423           "to be used for subtitle rendering", NULL, G_PARAM_WRITABLE));
   409 
   424 
   410   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose);
   425   gobject_klass->dispose = GST_DEBUG_FUNCPTR (gst_play_bin_dispose);
   411 
   426 
   412   gst_element_class_set_details (gstelement_klass, &gst_play_bin_details);
   427   gst_element_class_set_details (gstelement_klass, &gst_play_bin_details);
   413 
   428 
   418   gstbin_klass->handle_message =
   433   gstbin_klass->handle_message =
   419       GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
   434       GST_DEBUG_FUNCPTR (gst_play_bin_handle_message);
   420 
   435 
   421   playbasebin_klass->setup_output_pads = setup_sinks;
   436   playbasebin_klass->setup_output_pads = setup_sinks;
   422   playbasebin_klass->set_subtitles_visible = playbin_set_subtitles_visible;
   437   playbasebin_klass->set_subtitles_visible = playbin_set_subtitles_visible;
   423   playbasebin_klass->set_audio_mute = playbin_set_audio_mute;
       
   424 }
   438 }
   425 
   439 
   426 static void
   440 static void
   427 gst_play_bin_init (GstPlayBin * play_bin)
   441 gst_play_bin_init (GstPlayBin * play_bin)
   428 {
   442 {
   430   play_bin->audio_sink = NULL;
   444   play_bin->audio_sink = NULL;
   431   play_bin->visualisation = NULL;
   445   play_bin->visualisation = NULL;
   432   play_bin->pending_visualisation = NULL;
   446   play_bin->pending_visualisation = NULL;
   433   play_bin->volume_element = NULL;
   447   play_bin->volume_element = NULL;
   434   play_bin->textoverlay_element = NULL;
   448   play_bin->textoverlay_element = NULL;
   435   play_bin->spu_element = NULL;
       
   436   play_bin->volume = 1.0;
   449   play_bin->volume = 1.0;
   437   play_bin->sinks = NULL;
   450   play_bin->sinks = NULL;
   438   play_bin->frame = NULL;
   451   play_bin->frame = NULL;
   439   play_bin->font_desc = NULL;
   452   play_bin->font_desc = NULL;
   440   play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal,
   453   play_bin->cache = g_hash_table_new_full (g_str_hash, g_str_equal,
   476   }
   489   }
   477   if (play_bin->textoverlay_element != NULL) {
   490   if (play_bin->textoverlay_element != NULL) {
   478     gst_object_unref (play_bin->textoverlay_element);
   491     gst_object_unref (play_bin->textoverlay_element);
   479     play_bin->textoverlay_element = NULL;
   492     play_bin->textoverlay_element = NULL;
   480   }
   493   }
   481   if (play_bin->spu_element != NULL) {
       
   482     gst_object_unref (play_bin->spu_element);
       
   483     play_bin->spu_element = NULL;
       
   484   }
       
   485   g_free (play_bin->font_desc);
   494   g_free (play_bin->font_desc);
   486   play_bin->font_desc = NULL;
   495   play_bin->font_desc = NULL;
   487 
   496 
   488   G_OBJECT_CLASS (parent_class)->dispose (object);
   497   G_OBJECT_CLASS (parent_class)->dispose (object);
   489 }
   498 }
   527 
   536 
   528   if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) {
   537   if (!GST_IS_BIN (vis_bin) || !GST_IS_PAD (tee_pad)) {
   529     goto beach;
   538     goto beach;
   530   }
   539   }
   531 
   540 
   532   vis_src_pad = gst_element_get_static_pad (play_bin->visualisation, "src");
   541   vis_src_pad = gst_element_get_pad (play_bin->visualisation, "src");
   533   vis_sink_pad = gst_pad_get_peer (tee_pad);
   542   vis_sink_pad = gst_pad_get_peer (tee_pad);
   534 
   543 
   535   /* Can be fakesink */
   544   /* Can be fakesink */
   536   if (GST_IS_PAD (vis_src_pad)) {
   545   if (GST_IS_PAD (vis_src_pad)) {
   537     vqueue_pad = gst_pad_get_peer (vis_src_pad);
   546     vqueue_pad = gst_pad_get_peer (vis_src_pad);
   570     /* Add the new one */
   579     /* Add the new one */
   571     gst_bin_add (vis_bin, pending_visualisation);
   580     gst_bin_add (vis_bin, pending_visualisation);
   572     /* Synchronizing state */
   581     /* Synchronizing state */
   573     gst_element_set_state (pending_visualisation, bin_state);
   582     gst_element_set_state (pending_visualisation, bin_state);
   574 
   583 
   575     vis_sink_pad = gst_element_get_static_pad (pending_visualisation, "sink");
   584     vis_sink_pad = gst_element_get_pad (pending_visualisation, "sink");
   576     vis_src_pad = gst_element_get_static_pad (pending_visualisation, "src");
   585     vis_src_pad = gst_element_get_pad (pending_visualisation, "src");
   577 
   586 
   578     if (!GST_IS_PAD (vis_sink_pad) || !GST_IS_PAD (vis_src_pad)) {
   587     if (!GST_IS_PAD (vis_sink_pad) || !GST_IS_PAD (vis_src_pad)) {
   579       goto beach;
   588       goto beach;
   580     }
   589     }
   581 
   590 
   671           /* Check if the visualisation is already in a bin */
   680           /* Check if the visualisation is already in a bin */
   672           if (GST_IS_BIN (vis_bin)) {
   681           if (GST_IS_BIN (vis_bin)) {
   673             GstPad *vis_sink_pad = NULL, *tee_pad = NULL;
   682             GstPad *vis_sink_pad = NULL, *tee_pad = NULL;
   674 
   683 
   675             /* Now get tee pad and block it async */
   684             /* Now get tee pad and block it async */
   676             vis_sink_pad = gst_element_get_static_pad (play_bin->visualisation,
   685             vis_sink_pad = gst_element_get_pad (play_bin->visualisation,
   677                 "sink");
   686                 "sink");
   678             if (!GST_IS_PAD (vis_sink_pad)) {
   687             if (!GST_IS_PAD (vis_sink_pad)) {
   679               goto beach;
   688               goto beach;
   680             }
   689             }
   681             tee_pad = gst_pad_get_peer (vis_sink_pad);
   690             tee_pad = gst_pad_get_peer (vis_sink_pad);
   770   /* applications need to know the buffer caps,
   779   /* applications need to know the buffer caps,
   771    * make sure they are always set on the frame */
   780    * make sure they are always set on the frame */
   772   if (GST_BUFFER_CAPS (frame) == NULL) {
   781   if (GST_BUFFER_CAPS (frame) == NULL) {
   773     GstPad *pad;
   782     GstPad *pad;
   774 
   783 
   775     if ((pad = gst_element_get_static_pad (identity, "sink"))) {
   784     if ((pad = gst_element_get_pad (identity, "sink"))) {
   776       gst_buffer_set_caps (frame, GST_PAD_CAPS (pad));
   785       gst_buffer_set_caps (frame, GST_PAD_CAPS (pad));
   777       gst_object_unref (pad);
   786       gst_object_unref (pad);
   778     }
   787     }
   779   }
   788   }
   780 
   789 
   859   /* be more careful with the pad from the custom sink element, it might not
   868   /* be more careful with the pad from the custom sink element, it might not
   860    * be named 'sink' */
   869    * be named 'sink' */
   861   if (!gst_element_link_pads (scale, "src", sink, NULL))
   870   if (!gst_element_link_pads (scale, "src", sink, NULL))
   862     goto link_failed;
   871     goto link_failed;
   863 
   872 
   864   pad = gst_element_get_static_pad (identity, "sink");
   873   pad = gst_element_get_pad (identity, "sink");
   865   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
   874   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
   866   gst_object_unref (pad);
   875   gst_object_unref (pad);
   867 
   876 
   868   gst_element_set_state (element, GST_STATE_READY);
   877   gst_element_set_state (element, GST_STATE_READY);
   869 
   878 
   915  *
   924  *
   916  *  +--------------------------------------------------+
   925  *  +--------------------------------------------------+
   917  *  | tbin                  +-------------+            |
   926  *  | tbin                  +-------------+            |
   918  *  |          +-----+      | textoverlay |   +------+ |
   927  *  |          +-----+      | textoverlay |   +------+ |
   919  *  |          | csp | +--video_sink      |   | vbin | |
   928  *  |          | csp | +--video_sink      |   | vbin | |
   920  * video_sink-sink  src+ +-text_sink    src---sink   | |
   929  * video_sink-sink  src+ +-text_sink     src-sink    | |
   921  *  |          +-----+   |  +-------------+   +------+ |
   930  *  |          +-----+   |  +-------------+   +------+ |
   922  * text_sink-------------+                             |
   931  * text_sink-------------+                             |
   923  *  +--------------------------------------------------+
   932  *  +--------------------------------------------------+
   924  *
   933  *
   925  *  If there is no subtitle renderer this function will simply return the
   934  *  If there is no subtitle renderer this function will simply return the
   926  *  videosink without the text_sink pad.
   935  *  videosink without the text_sink pad.
   927  */
   936  */
   928 static GstElement *
   937 static GstElement *
   929 add_text_element (GstPlayBin * play_bin, GstElement * vbin)
   938 gen_text_element (GstPlayBin * play_bin)
   930 {
   939 {
   931   GstElement *element, *csp, *overlay;
   940   GstElement *element, *csp, *overlay, *vbin;
   932   GstPad *pad;
   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;
   933 
   947 
   934   /* Text overlay */
   948   /* Text overlay */
   935   overlay = gst_element_factory_make ("textoverlay", "overlay");
   949   overlay = gst_element_factory_make ("textoverlay", "overlay");
   936 
   950 
   937   /* If no overlay return the video bin without subtitle support. */
   951   /* If no overlay return the video bin without subtitle support. */
   960   /* Link */
   974   /* Link */
   961   gst_element_link_pads (csp, "src", overlay, "video_sink");
   975   gst_element_link_pads (csp, "src", overlay, "video_sink");
   962   gst_element_link_pads (overlay, "src", vbin, "sink");
   976   gst_element_link_pads (overlay, "src", vbin, "sink");
   963 
   977 
   964   /* Add ghost pads on the subtitle bin */
   978   /* Add ghost pads on the subtitle bin */
   965   pad = gst_element_get_static_pad (overlay, "text_sink");
   979   pad = gst_element_get_pad (overlay, "text_sink");
   966   gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad));
   980   gst_element_add_pad (element, gst_ghost_pad_new ("text_sink", pad));
   967   gst_object_unref (pad);
   981   gst_object_unref (pad);
   968 
   982 
   969   pad = gst_element_get_static_pad (csp, "sink");
   983   pad = gst_element_get_pad (csp, "sink");
   970   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
   984   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
   971   gst_object_unref (pad);
   985   gst_object_unref (pad);
   972 
       
   973   /* If the vbin provides a subpicture sink pad, ghost it too */
       
   974   pad = gst_element_get_static_pad (vbin, "subpicture_sink");
       
   975   if (pad) {
       
   976     gst_element_add_pad (element, gst_ghost_pad_new ("subpicture_sink", pad));
       
   977     gst_object_unref (pad);
       
   978   }
       
   979 
   986 
   980   /* Set state to READY */
   987   /* Set state to READY */
   981   gst_element_set_state (element, GST_STATE_READY);
   988   gst_element_set_state (element, GST_STATE_READY);
   982 
   989 
   983   return element;
   990   return element;
   986 no_overlay:
   993 no_overlay:
   987   {
   994   {
   988     post_missing_element_message (play_bin, "textoverlay");
   995     post_missing_element_message (play_bin, "textoverlay");
   989     GST_WARNING_OBJECT (play_bin,
   996     GST_WARNING_OBJECT (play_bin,
   990         "No overlay (pango) element, subtitles disabled");
   997         "No overlay (pango) element, subtitles disabled");
   991     return vbin;
       
   992   }
       
   993 }
       
   994 
       
   995 /* make an element for rendering DVD subpictures onto output video
       
   996  *
       
   997  *  +---------------------------------------------+
       
   998  *  | tbin                   +--------+           |
       
   999  *  |          +-----+       |        |  +------+ |
       
  1000  *  |          | csp | src-videosink  |  | vbin | |
       
  1001  * video_sink-sink  src+     |       src-sink   | |
       
  1002  *  |          +-----+   +subpicture  |  +------+ |
       
  1003  * subpicture_pad--------+   +--------+           |
       
  1004  *  +---------- ----------------------------------+
       
  1005  *
       
  1006  */
       
  1007 static GstElement *
       
  1008 add_spu_element (GstPlayBin * play_bin, GstElement * vbin)
       
  1009 {
       
  1010   GstElement *element, *csp, *overlay;
       
  1011   GstPad *pad;
       
  1012 
       
  1013   /* DVD spu overlay */
       
  1014   GST_DEBUG_OBJECT (play_bin, "Attempting to insert DVD SPU element");
       
  1015 
       
  1016   overlay = gst_element_factory_make ("dvdspu", "overlay");
       
  1017 
       
  1018   /* If no overlay return the video bin without subpicture support. */
       
  1019   if (!overlay)
       
  1020     goto no_overlay;
       
  1021 
       
  1022   /* Create our bin */
       
  1023   element = gst_bin_new ("spubin");
       
  1024 
       
  1025   /* Take a ref */
       
  1026   play_bin->spu_element = GST_ELEMENT_CAST (gst_object_ref (overlay));
       
  1027 
       
  1028   /* we know this will succeed, as the video bin already created one before */
       
  1029   csp = gst_element_factory_make ("ffmpegcolorspace", "spucsp");
       
  1030 
       
  1031   /* Add our elements */
       
  1032   gst_bin_add_many (GST_BIN_CAST (element), csp, overlay, vbin, NULL);
       
  1033 
       
  1034   /* Link */
       
  1035   gst_element_link_pads (csp, "src", overlay, "video");
       
  1036   gst_element_link_pads (overlay, "src", vbin, "sink");
       
  1037 
       
  1038   /* Add ghost pad on the subpicture bin so it looks like vbin */
       
  1039   pad = gst_element_get_static_pad (csp, "sink");
       
  1040   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
       
  1041   gst_object_unref (pad);
       
  1042 
       
  1043   pad = gst_element_get_static_pad (overlay, "subpicture");
       
  1044   gst_element_add_pad (element, gst_ghost_pad_new ("subpicture_sink", pad));
       
  1045   gst_object_unref (pad);
       
  1046 
       
  1047   /* Set state to READY */
       
  1048   gst_element_set_state (element, GST_STATE_READY);
       
  1049 
       
  1050   return element;
       
  1051 
       
  1052   /* ERRORS */
       
  1053 no_overlay:
       
  1054   {
       
  1055     post_missing_element_message (play_bin, "dvdspu");
       
  1056     GST_WARNING_OBJECT (play_bin,
       
  1057         "No DVD overlay (dvdspu) element. "
       
  1058         "menu highlight/subtitles unavailable");
       
  1059     return vbin;
   998     return vbin;
  1060   }
   999   }
  1061 }
  1000 }
  1062 
  1001 
  1063 /* make the element (bin) that contains the elements needed to perform
  1002 /* make the element (bin) that contains the elements needed to perform
  1117 
  1056 
  1118   scale = gst_element_factory_make ("audioresample", "aresample");
  1057   scale = gst_element_factory_make ("audioresample", "aresample");
  1119   if (scale == NULL)
  1058   if (scale == NULL)
  1120     goto no_audioresample;
  1059     goto no_audioresample;
  1121   gst_bin_add (GST_BIN_CAST (element), scale);
  1060   gst_bin_add (GST_BIN_CAST (element), scale);
  1122 
  1061 #ifndef __SYMBIAN32__
  1123   volume = gst_element_factory_make ("volume", "volume");
  1062   volume = gst_element_factory_make ("volume", "volume");
  1124   if (volume == NULL)
  1063   if (volume == NULL)
  1125     goto no_volume;
  1064     goto no_volume;
  1126   g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL);
  1065   g_object_set (G_OBJECT (volume), "volume", play_bin->volume, NULL);
  1127   play_bin->volume_element = volume;
  1066   play_bin->volume_element = volume;
  1128   gst_bin_add (GST_BIN_CAST (element), volume);
  1067   gst_bin_add (GST_BIN_CAST (element), volume);
       
  1068 #endif
  1129 
  1069 
  1130   res = gst_element_link_pads (conv, "src", scale, "sink");
  1070   res = gst_element_link_pads (conv, "src", scale, "sink");
       
  1071 #ifndef __SYMBIAN32__
  1131   res &= gst_element_link_pads (scale, "src", volume, "sink");
  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__
  1132   res &= gst_element_link_pads (volume, "src", sink, NULL);
  1077   res &= gst_element_link_pads (volume, "src", sink, NULL);
       
  1078 #endif
  1133   if (!res)
  1079   if (!res)
  1134     goto link_failed;
  1080     goto link_failed;
  1135 
  1081 
  1136   pad = gst_element_get_static_pad (conv, "sink");
  1082   pad = gst_element_get_pad (conv, "sink");
  1137   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
  1083   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
  1138   gst_object_unref (pad);
  1084   gst_object_unref (pad);
  1139 
  1085 
  1140   gst_element_set_state (element, GST_STATE_READY);
  1086   gst_element_set_state (element, GST_STATE_READY);
  1141 
  1087 
  1281   res &= gst_element_link_pads (conv2, "src", vis, "sink");
  1227   res &= gst_element_link_pads (conv2, "src", vis, "sink");
  1282   res &= gst_element_link_pads (vis, "src", vsink, "sink");
  1228   res &= gst_element_link_pads (vis, "src", vsink, "sink");
  1283   if (!res)
  1229   if (!res)
  1284     goto link_failed;
  1230     goto link_failed;
  1285 
  1231 
  1286   pad = gst_element_get_static_pad (aqueue, "sink");
  1232   pad = gst_element_get_pad (aqueue, "sink");
  1287   rpad = gst_element_get_request_pad (tee, "src%d");
  1233   rpad = gst_element_get_request_pad (tee, "src%d");
  1288   gst_pad_link (rpad, pad);
  1234   gst_pad_link (rpad, pad);
  1289   gst_object_unref (rpad);
  1235   gst_object_unref (rpad);
  1290   gst_object_unref (pad);
  1236   gst_object_unref (pad);
  1291   gst_element_link_pads (aqueue, "src", asink, "sink");
  1237   gst_element_link_pads (aqueue, "src", asink, "sink");
  1292 
  1238 
  1293   pad = gst_element_get_static_pad (vqueue, "sink");
  1239   pad = gst_element_get_pad (vqueue, "sink");
  1294   rpad = gst_element_get_request_pad (tee, "src%d");
  1240   rpad = gst_element_get_request_pad (tee, "src%d");
  1295   gst_pad_link (rpad, pad);
  1241   gst_pad_link (rpad, pad);
  1296   gst_object_unref (rpad);
  1242   gst_object_unref (rpad);
  1297   gst_object_unref (pad);
  1243   gst_object_unref (pad);
  1298 
  1244 
  1299   pad = gst_element_get_static_pad (tee, "sink");
  1245   pad = gst_element_get_pad (tee, "sink");
  1300   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
  1246   gst_element_add_pad (element, gst_ghost_pad_new ("sink", pad));
  1301   gst_object_unref (pad);
  1247   gst_object_unref (pad);
  1302 
  1248 
  1303   return element;
  1249   return element;
  1304 
  1250 
  1362       play_bin->sinks = g_list_remove (play_bin->sinks, element);
  1308       play_bin->sinks = g_list_remove (play_bin->sinks, element);
  1363       gst_element_set_state (element, GST_STATE_NULL);
  1309       gst_element_set_state (element, GST_STATE_NULL);
  1364       gst_bin_remove (GST_BIN_CAST (parent), element);
  1310       gst_bin_remove (GST_BIN_CAST (parent), element);
  1365       gst_object_unref (parent);
  1311       gst_object_unref (parent);
  1366     }
  1312     }
  1367     pad = gst_element_get_static_pad (element, "sink");
  1313     pad = gst_element_get_pad (element, "sink");
  1368     if (pad != NULL) {
  1314     if (pad != NULL) {
  1369       peer = gst_pad_get_peer (pad);
  1315       peer = gst_pad_get_peer (pad);
  1370       if (peer != NULL) {
  1316       if (peer != NULL) {
  1371         gst_pad_unlink (peer, pad);
  1317         gst_pad_unlink (peer, pad);
  1372         gst_object_unref (peer);
  1318         gst_object_unref (peer);
  1381       play_bin->sinks = g_list_remove (play_bin->sinks, element);
  1327       play_bin->sinks = g_list_remove (play_bin->sinks, element);
  1382       gst_element_set_state (element, GST_STATE_NULL);
  1328       gst_element_set_state (element, GST_STATE_NULL);
  1383       gst_bin_remove (GST_BIN_CAST (parent), element);
  1329       gst_bin_remove (GST_BIN_CAST (parent), element);
  1384       gst_object_unref (parent);
  1330       gst_object_unref (parent);
  1385     }
  1331     }
  1386     pad = gst_element_get_static_pad (element, "sink");
  1332     pad = gst_element_get_pad (element, "sink");
  1387     if (pad != NULL) {
  1333     if (pad != NULL) {
  1388       peer = gst_pad_get_peer (pad);
  1334       peer = gst_pad_get_peer (pad);
  1389       if (peer != NULL) {
  1335       if (peer != NULL) {
  1390         gst_pad_unlink (peer, pad);
  1336         gst_pad_unlink (peer, pad);
  1391         gst_object_unref (peer);
  1337         gst_object_unref (peer);
  1397   for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) {
  1343   for (sinks = play_bin->sinks; sinks; sinks = g_list_next (sinks)) {
  1398     GstElement *element = GST_ELEMENT_CAST (sinks->data);
  1344     GstElement *element = GST_ELEMENT_CAST (sinks->data);
  1399     GstPad *pad;
  1345     GstPad *pad;
  1400     GstPad *peer;
  1346     GstPad *peer;
  1401 
  1347 
  1402     pad = gst_element_get_static_pad (element, "sink");
  1348     pad = gst_element_get_pad (element, "sink");
  1403 
  1349 
  1404     GST_LOG ("removing sink %p", element);
  1350     GST_LOG ("removing sink %p", element);
  1405 
  1351 
  1406     peer = gst_pad_get_peer (pad);
  1352     peer = gst_pad_get_peer (pad);
  1407     if (peer) {
  1353     if (peer) {
  1481   stateret = gst_element_set_state (sink, state);
  1427   stateret = gst_element_set_state (sink, state);
  1482   if (stateret == GST_STATE_CHANGE_FAILURE)
  1428   if (stateret == GST_STATE_CHANGE_FAILURE)
  1483     goto state_failed;
  1429     goto state_failed;
  1484 
  1430 
  1485   /* we found a sink for this stream, now try to install it */
  1431   /* we found a sink for this stream, now try to install it */
  1486   sinkpad = gst_element_get_static_pad (sink, "sink");
  1432   sinkpad = gst_element_get_pad (sink, "sink");
  1487   linkres = gst_pad_link (srcpad, sinkpad);
  1433   linkres = gst_pad_link (srcpad, sinkpad);
  1488   gst_object_unref (sinkpad);
  1434   gst_object_unref (sinkpad);
  1489 
  1435 
  1490   /* try to link the pad of the sink to the stream */
  1436   /* try to link the pad of the sink to the stream */
  1491   if (GST_PAD_LINK_FAILED (linkres))
  1437   if (GST_PAD_LINK_FAILED (linkres))
  1492     goto link_failed;
  1438     goto link_failed;
  1493 
  1439 
  1494   if (GST_IS_PAD (subtitle_pad)) {
  1440   if (GST_IS_PAD (subtitle_pad)) {
  1495     sinkpad = gst_element_get_static_pad (sink, "text_sink");
  1441     sinkpad = gst_element_get_pad (sink, "text_sink");
  1496     linkres = gst_pad_link (subtitle_pad, sinkpad);
  1442     linkres = gst_pad_link (subtitle_pad, sinkpad);
  1497     gst_object_unref (sinkpad);
  1443     gst_object_unref (sinkpad);
  1498   }
  1444   }
  1499 
  1445 
  1500   /* try to link the subtitle pad of the sink to the stream, this is not
  1446   /* try to link the subtitle pad of the sink to the stream, this is not
  1557 
  1503 
  1558 static gboolean
  1504 static gboolean
  1559 setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
  1505 setup_sinks (GstPlayBaseBin * play_base_bin, GstPlayBaseGroup * group)
  1560 {
  1506 {
  1561   GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin);
  1507   GstPlayBin *play_bin = GST_PLAY_BIN (play_base_bin);
  1562   gboolean have_video = FALSE;
  1508   GList *streaminfo = NULL, *s;
  1563   gboolean need_vis = FALSE;
  1509   gboolean need_vis = FALSE;
  1564   gboolean need_text = FALSE;
  1510   gboolean need_text = FALSE;
  1565   gboolean need_spu = FALSE;
       
  1566   GstPad *textsrcpad = NULL, *pad = NULL, *origtextsrcpad = NULL;
  1511   GstPad *textsrcpad = NULL, *pad = NULL, *origtextsrcpad = NULL;
  1567   GstElement *sink;
  1512   GstElement *sink;
  1568   gboolean res = TRUE;
  1513   gboolean res = TRUE;
  1569 
  1514 
  1570   /* get rid of existing sinks */
  1515   /* get rid of existing sinks */
  1572     remove_sinks (play_bin);
  1517     remove_sinks (play_bin);
  1573   }
  1518   }
  1574   GST_DEBUG_OBJECT (play_base_bin, "setupsinks");
  1519   GST_DEBUG_OBJECT (play_base_bin, "setupsinks");
  1575 
  1520 
  1576   /* find out what to do */
  1521   /* find out what to do */
  1577   have_video = (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0);
  1522   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0 &&
  1578   need_spu = (group->type[GST_STREAM_TYPE_SUBPICTURE - 1].npads != 0);
  1523       group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
  1579 
       
  1580   if (have_video && group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
       
  1581     need_text = TRUE;
  1524     need_text = TRUE;
  1582   } else if (!have_video &&
  1525   } else if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0 &&
  1583       group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 &&
  1526       group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 &&
  1584       play_bin->visualisation != NULL) {
  1527       play_bin->visualisation != NULL) {
  1585     need_vis = TRUE;
  1528     need_vis = TRUE;
  1586   }
  1529   }
  1587 
  1530 
  1588   /* now actually connect everything */
  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   }
  1589 
  1541 
  1590   /* link audio */
  1542   /* link audio */
  1591   if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) {
  1543   if (group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0) {
  1592     if (need_vis) {
  1544     if (need_vis) {
  1593       sink = gen_vis_element (play_bin);
  1545       sink = gen_vis_element (play_bin);
  1595       sink = gen_audio_element (play_bin);
  1547       sink = gen_audio_element (play_bin);
  1596     }
  1548     }
  1597     if (!sink)
  1549     if (!sink)
  1598       return FALSE;
  1550       return FALSE;
  1599 
  1551 
  1600     pad =
  1552     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_AUDIO - 1].preroll,
  1601         gst_element_get_static_pad (group->type[GST_STREAM_TYPE_AUDIO -
  1553         "src");
  1602             1].preroll, "src");
       
  1603     res = add_sink (play_bin, sink, pad, NULL);
  1554     res = add_sink (play_bin, sink, pad, NULL);
  1604     gst_object_unref (pad);
  1555     gst_object_unref (pad);
  1605   }
  1556   }
  1606 
  1557 
  1607   /* link video */
  1558   /* link video */
  1608   if (have_video) {
  1559   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
  1609     /* Create the video rendering bin, error is posted when this fails. */
       
  1610     sink = gen_video_element (play_bin);
       
  1611     if (!sink)
       
  1612       return FALSE;
       
  1613     if (need_spu) {
       
  1614       sink = add_spu_element (play_bin, sink);
       
  1615     }
       
  1616 
       
  1617     if (need_text) {
  1560     if (need_text) {
  1618       GstObject *parent = NULL, *grandparent = NULL;
  1561       GstObject *parent = NULL, *grandparent = NULL;
  1619       GstPad *ghost = NULL;
  1562       GstPad *ghost = NULL;
  1620 
  1563 
  1621       /* Add the subtitle overlay element into the video sink */
  1564       sink = gen_text_element (play_bin);
  1622       sink = add_text_element (play_bin, sink);
       
  1623 
       
  1624       /* Link the incoming subtitle stream into the output bin */
       
  1625       textsrcpad =
  1565       textsrcpad =
  1626           gst_element_get_static_pad (group->type[GST_STREAM_TYPE_TEXT -
  1566           gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
  1627               1].preroll, "src");
  1567           "src");
  1628 
  1568 
  1629       /* This pad is from subtitle-bin, we need to create a ghost pad to have
  1569       /* This pad is from subtitle-bin, we need to create a ghost pad to have
  1630          common grandparents */
  1570          common grandparents */
  1631       parent = gst_object_get_parent (GST_OBJECT_CAST (textsrcpad));
  1571       parent = gst_object_get_parent (GST_OBJECT_CAST (textsrcpad));
  1632       if (!parent) {
  1572       if (!parent) {
  1682             "no changes to hierarchy needed");
  1622             "no changes to hierarchy needed");
  1683       }
  1623       }
  1684 
  1624 
  1685       gst_object_unref (parent);
  1625       gst_object_unref (parent);
  1686       gst_object_unref (grandparent);
  1626       gst_object_unref (grandparent);
       
  1627     } else {
       
  1628       sink = gen_video_element (play_bin);
  1687     }
  1629     }
  1688   beach:
  1630   beach:
  1689     if (!sink)
  1631     if (!sink)
  1690       return FALSE;
  1632       return FALSE;
  1691     pad =
  1633     pad = gst_element_get_pad (group->type[GST_STREAM_TYPE_VIDEO - 1].preroll,
  1692         gst_element_get_static_pad (group->type[GST_STREAM_TYPE_VIDEO -
  1634         "src");
  1693             1].preroll, "src");
       
  1694     res = add_sink (play_bin, sink, pad, textsrcpad);
  1635     res = add_sink (play_bin, sink, pad, textsrcpad);
  1695     gst_object_unref (pad);
  1636     gst_object_unref (pad);
  1696     if (textsrcpad)
  1637     if (textsrcpad)
  1697       gst_object_unref (textsrcpad);
  1638       gst_object_unref (textsrcpad);
  1698     if (origtextsrcpad) {
  1639     if (origtextsrcpad) {
  1699       gst_pad_set_blocked_async (origtextsrcpad, FALSE, dummy_blocked_cb, NULL);
  1640       gst_pad_set_blocked_async (origtextsrcpad, FALSE, dummy_blocked_cb, NULL);
  1700       gst_object_unref (origtextsrcpad);
  1641       gst_object_unref (origtextsrcpad);
  1701     }
  1642     }
  1702 
       
  1703     /* If we have a DVD subpicture stream, link it to the SPU now */
       
  1704     if (need_spu) {
       
  1705       GstPad *subpic_pad;
       
  1706       GstPad *spu_sink_pad;
       
  1707 
       
  1708       subpic_pad =
       
  1709           gst_element_get_static_pad (group->type[GST_STREAM_TYPE_SUBPICTURE
       
  1710               - 1].preroll, "src");
       
  1711       spu_sink_pad = gst_element_get_static_pad (sink, "subpicture_sink");
       
  1712       if (subpic_pad && spu_sink_pad) {
       
  1713         GST_LOG_OBJECT (play_bin, "Linking DVD subpicture stream onto SPU");
       
  1714         gst_pad_set_blocked_async (subpic_pad, TRUE, dummy_blocked_cb, NULL);
       
  1715         if (gst_pad_link (subpic_pad, spu_sink_pad) != GST_PAD_LINK_OK) {
       
  1716           GST_WARNING_OBJECT (play_bin,
       
  1717               "Failed to link DVD subpicture stream onto SPU");
       
  1718         }
       
  1719         gst_pad_set_blocked_async (subpic_pad, FALSE, dummy_blocked_cb, NULL);
       
  1720       }
       
  1721       if (subpic_pad)
       
  1722         gst_object_unref (subpic_pad);
       
  1723       if (spu_sink_pad)
       
  1724         gst_object_unref (spu_sink_pad);
       
  1725     }
       
  1726   }
  1643   }
  1727 
  1644 
  1728   /* remove the sinks now, pipeline get_state will now wait for the
  1645   /* remove the sinks now, pipeline get_state will now wait for the
  1729    * sinks to preroll */
  1646    * sinks to preroll */
  1730   if (play_bin->fakesink) {
  1647   if (play_bin->fakesink) {
  1745    * before textoverlay is set up (which is probably okay, since playbasebin
  1662    * before textoverlay is set up (which is probably okay, since playbasebin
  1746    * will just select the first subtitle stream as active stream regardless) */
  1663    * will just select the first subtitle stream as active stream regardless) */
  1747   if (playbin->textoverlay_element != NULL) {
  1664   if (playbin->textoverlay_element != NULL) {
  1748     GST_LOG_OBJECT (playbin, "setting subtitle visibility to %d", visible);
  1665     GST_LOG_OBJECT (playbin, "setting subtitle visibility to %d", visible);
  1749     g_object_set (playbin->textoverlay_element, "silent", !visible, NULL);
  1666     g_object_set (playbin->textoverlay_element, "silent", !visible, NULL);
  1750   }
       
  1751 }
       
  1752 
       
  1753 static void
       
  1754 playbin_set_audio_mute (GstPlayBaseBin * play_base_bin, gboolean mute)
       
  1755 {
       
  1756   GstPlayBin *playbin = GST_PLAY_BIN (play_base_bin);
       
  1757 
       
  1758   if (playbin->volume_element) {
       
  1759     g_object_set (G_OBJECT (playbin->volume_element), "mute", mute, NULL);
       
  1760   }
  1667   }
  1761 }
  1668 }
  1762 
  1669 
  1763 /* Send an event to our sinks until one of them works; don't then send to the
  1670 /* Send an event to our sinks until one of them works; don't then send to the
  1764  * remaining sinks (unlike GstBin)
  1671  * remaining sinks (unlike GstBin)