gst/playback/gstplaybin2.c: Update some docs.
authorWim Taymans <wim.taymans@gmail.com>
Mon, 24 Mar 2008 12:26:30 +0000 (12:26 +0000)
committerWim Taymans <wim.taymans@gmail.com>
Mon, 24 Mar 2008 12:26:30 +0000 (12:26 +0000)
Original commit message from CVS:
* gst/playback/gstplaybin2.c: (gst_play_bin_class_init),
(init_group), (free_group), (gst_play_bin_init),
(gst_play_bin_finalize), (gst_play_bin_set_uri),
(gst_play_bin_set_suburi), (gst_play_bin_get_video_tags),
(gst_play_bin_get_audio_tags), (gst_play_bin_get_text_tags),
(gst_play_bin_set_current_video_stream),
(gst_play_bin_set_current_audio_stream),
(gst_play_bin_set_current_text_stream),
(gst_play_bin_set_encoding), (gst_play_bin_set_property),
(gst_play_bin_get_property), (pad_added_cb), (pad_removed_cb),
(no_more_pads_cb), (perform_eos), (autoplug_select_cb),
(activate_group), (deactivate_group), (setup_next_source),
(save_current_group), (gst_play_bin_change_state):
Update some docs.
Add new locks and conds to protect pipeline creation and group
switching.
Implement the sub-uri property.
Keep track of pending uridecodebin creation and configure the output
pipeline after all streams are configured.
Propagate subtitle encoding to the uridecodebins.
Implement getting the video/audio/visualisation elements.
Use input-selector for stream switching.
If we are asked to do visualisation, prefer to autoplug raw sinks
instead of sinks that accept encoded data.

ChangeLog
gst/playback/gstplaybin2.c

index 5d6690a5993ea7db78980284e0a4fe9cb6e34395..61b8c55570b6cb325a91d0ca1136a5e8b26ba129 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,30 @@
+2008-03-24  Wim Taymans  <wim.taymans@collabora.co.uk>
+
+       * gst/playback/gstplaybin2.c: (gst_play_bin_class_init),
+       (init_group), (free_group), (gst_play_bin_init),
+       (gst_play_bin_finalize), (gst_play_bin_set_uri),
+       (gst_play_bin_set_suburi), (gst_play_bin_get_video_tags),
+       (gst_play_bin_get_audio_tags), (gst_play_bin_get_text_tags),
+       (gst_play_bin_set_current_video_stream),
+       (gst_play_bin_set_current_audio_stream),
+       (gst_play_bin_set_current_text_stream),
+       (gst_play_bin_set_encoding), (gst_play_bin_set_property),
+       (gst_play_bin_get_property), (pad_added_cb), (pad_removed_cb),
+       (no_more_pads_cb), (perform_eos), (autoplug_select_cb),
+       (activate_group), (deactivate_group), (setup_next_source),
+       (save_current_group), (gst_play_bin_change_state):
+       Update some docs.
+       Add new locks and conds to protect pipeline creation and group
+       switching.
+       Implement the sub-uri property.
+       Keep track of pending uridecodebin creation and configure the output
+       pipeline after all streams are configured.
+       Propagate subtitle encoding to the uridecodebins.
+       Implement getting the video/audio/visualisation elements.
+       Use input-selector for stream switching.
+       If we are asked to do visualisation, prefer to autoplug raw sinks
+       instead of sinks that accept encoded data.
+
 2008-03-24  Wim Taymans  <wim.taymans@collabora.co.uk>
 
        * gst/playback/gstplaysink.c: (gst_play_sink_class_init),
index 15d9e77087826a091a368ff021968af12303da00..0ac2c9a9d9aabcf85a9a3485fc80f069ab4ec404 100644 (file)
  * visualisations for audio files
  * </listitem>
  * <listitem>
- * subtitle support for video files
+ * subtitle support for video files. Subtitles can be store in external
+ * files.
  * </listitem>
  * <listitem>
- * stream selection between different audio/subtitles streams
+ * stream selection between different video/audio/subtitles streams
  * </listitem>
  * <listitem>
  * meta info (tag) extraction
@@ -51,7 +52,7 @@
  * buffering when playing streams over a network
  * </listitem>
  * <listitem>
- * volume control
+ * volume control with mute option
  * </listitem>
  * </itemizedlist>
  * </para>
@@ -284,12 +285,24 @@ struct _GstSourceSelect
   GstPad *sinkpad;              /* the sinkpad of the sink when the selector is linked */
 };
 
+#define GST_SOURCE_GROUP_GET_LOCK(group) (((GstSourceGroup*)(group))->lock)
+#define GST_SOURCE_GROUP_GET_COND(group) (((GstSourceGroup*)(group))->cond)
+#define GST_SOURCE_GROUP_LOCK(group) (g_mutex_lock (GST_SOURCE_GROUP_GET_LOCK(group)))
+#define GST_SOURCE_GROUP_UNLOCK(group) (g_mutex_unlock (GST_SOURCE_GROUP_GET_LOCK(group)))
+#define GST_SOURCE_GROUP_WAIT(group) (g_cond_wait \
+               (GST_SOURCE_GROUP_GET_COND (group),GST_SOURCE_GROUP_GET_LOCK(group)))
+#define GST_SOURCE_GROUP_BROADCAST(group) (g_cond_broadcast \
+               (GST_SOURCE_GROUP_GET_COND (group)))
+
 /* a structure to hold the objects for decoding a uri and the subtitle uri
  */
 struct _GstSourceGroup
 {
   GstPlayBin *playbin;
 
+  GMutex *lock;
+  GCond *cond;
+
   gboolean valid;               /* the group has valid info to start playback */
   gboolean active;              /* the group is active */
 
@@ -298,7 +311,6 @@ struct _GstSourceGroup
   gchar *suburi;
   GValueArray *streaminfo;
   GstElement *source;
-  gchar *subencoding;           /* encoding to propagate to the subtitle elements */
 
   GPtrArray *video_channels;    /* links to selector pads */
   GPtrArray *audio_channels;    /* links to selector pads */
@@ -307,15 +319,22 @@ struct _GstSourceGroup
   /* uridecodebins for uri and subtitle uri */
   GstElement *uridecodebin;
   GstElement *suburidecodebin;
+  gint pending;
 
   /* selectors for different streams */
   GstSourceSelect selector[GST_PLAY_SINK_TYPE_LAST];
 };
 
+#define GST_PLAY_BIN_GET_LOCK(bin) (((GstPlayBin*)(bin))->lock)
+#define GST_PLAY_BIN_LOCK(bin) (g_mutex_lock (GST_PLAY_BIN_GET_LOCK(bin)))
+#define GST_PLAY_BIN_UNLOCK(bin) (g_mutex_unlock (GST_PLAY_BIN_GET_LOCK(bin)))
+
 struct _GstPlayBin
 {
   GstPipeline parent;
 
+  GMutex *lock;                 /* to protect group switching */
+
   /* the groups, we use a double buffer to switch between current and next */
   GstSourceGroup groups[2];     /* array with group info */
   GstSourceGroup *curr_group;   /* pointer to the currently playing group */
@@ -328,6 +347,7 @@ struct _GstPlayBin
   gint current_video;           /* the currently selected stream */
   gint current_audio;           /* the currently selected stream */
   gint current_text;            /* the currently selected stream */
+  gchar *encoding;              /* subtitle encoding */
 
   /* our play sink */
   GstPlaySink *playsink;
@@ -552,7 +572,6 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
       g_param_spec_object ("source", "Source", "Source element",
           GST_TYPE_ELEMENT, G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
 
-
   /**
    * GstPlayBin:flags
    *
@@ -639,7 +658,7 @@ gst_play_bin_class_init (GstPlayBinClass * klass)
           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
   g_object_class_install_property (gobject_klass, PROP_VIS_PLUGIN,
       g_param_spec_object ("vis-plugin", "Vis plugin",
-          "the visualization element to use (NULL = none)",
+          "the visualization element to use (NULL = default)",
           GST_TYPE_ELEMENT, G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
 
   g_object_class_install_property (gobject_klass, PROP_VOLUME,
@@ -818,6 +837,8 @@ init_group (GstPlayBin * playbin, GstSourceGroup * group)
   group->video_channels = g_ptr_array_new ();
   group->audio_channels = g_ptr_array_new ();
   group->text_channels = g_ptr_array_new ();
+  group->lock = g_mutex_new ();
+  group->cond = g_cond_new ();
   /* init selectors */
   group->playbin = playbin;
   group->selector[0].media = "audio/x-raw-";
@@ -837,11 +858,23 @@ init_group (GstPlayBin * playbin, GstSourceGroup * group)
   group->selector[4].channels = group->text_channels;
 }
 
+static void
+free_group (GstPlayBin * playbin, GstSourceGroup * group)
+{
+  g_ptr_array_free (group->video_channels, TRUE);
+  g_ptr_array_free (group->audio_channels, TRUE);
+  g_ptr_array_free (group->text_channels, TRUE);
+  g_mutex_free (group->lock);
+  g_cond_free (group->cond);
+}
+
 static void
 gst_play_bin_init (GstPlayBin * playbin)
 {
   GstFactoryListType type;
 
+  playbin->lock = g_mutex_new ();
+
   /* init groups */
   playbin->curr_group = &playbin->groups[0];
   playbin->next_group = &playbin->groups[1];
@@ -861,6 +894,7 @@ gst_play_bin_init (GstPlayBin * playbin)
   playbin->current_video = DEFAULT_CURRENT_VIDEO;
   playbin->current_audio = DEFAULT_CURRENT_AUDIO;
   playbin->current_text = DEFAULT_CURRENT_TEXT;
+  playbin->encoding = g_strdup (DEFAULT_SUBTITLE_ENCODING);
 }
 
 static void
@@ -870,7 +904,12 @@ gst_play_bin_finalize (GObject * object)
 
   playbin = GST_PLAY_BIN (object);
 
+  free_group (playbin, &playbin->groups[0]);
+  free_group (playbin, &playbin->groups[1]);
+
   g_value_array_free (playbin->elements);
+  g_free (playbin->encoding);
+  g_mutex_free (playbin->lock);
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
 }
@@ -885,17 +924,19 @@ gst_play_bin_set_uri (GstPlayBin * playbin, const gchar * uri)
     return;
   }
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = playbin->next_group;
 
+  GST_SOURCE_GROUP_LOCK (group);
   /* if we have no previous uri, or the new uri is different from the
    * old one, replug */
   g_free (group->uri);
   group->uri = g_strdup (uri);
   group->valid = TRUE;
+  GST_SOURCE_GROUP_UNLOCK (group);
 
   GST_DEBUG ("setting new uri to %s", uri);
-  GST_OBJECT_UNLOCK (playbin);
+  GST_PLAY_BIN_UNLOCK (playbin);
 }
 
 static void
@@ -903,9 +944,10 @@ gst_play_bin_set_suburi (GstPlayBin * playbin, const gchar * suburi)
 {
   GstSourceGroup *group;
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = playbin->next_group;
 
+  GST_SOURCE_GROUP_LOCK (group);
   if ((!suburi && !group->suburi) ||
       (suburi && group->suburi && !strcmp (group->suburi, suburi)))
     goto done;
@@ -916,11 +958,12 @@ gst_play_bin_set_suburi (GstPlayBin * playbin, const gchar * suburi)
   GST_DEBUG ("setting new .sub uri to %s", suburi);
 
 done:
-  GST_OBJECT_UNLOCK (playbin);
+  GST_SOURCE_GROUP_UNLOCK (group);
+  GST_PLAY_BIN_UNLOCK (playbin);
 }
 
 /* get the currently playing group or if nothing is playing, the next
- * group. Must be called with the LOCK. */
+ * group. Must be called with the PLAY_BIN_LOCK. */
 static GstSourceGroup *
 get_group (GstPlayBin * playbin)
 {
@@ -953,10 +996,10 @@ gst_play_bin_get_video_tags (GstPlayBin * playbin, gint stream)
   GstTagList *result;
   GstSourceGroup *group;
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = get_group (playbin);
   result = get_tags (playbin, group->video_channels, stream);
-  GST_OBJECT_UNLOCK (playbin);
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   return result;
 }
@@ -967,10 +1010,10 @@ gst_play_bin_get_audio_tags (GstPlayBin * playbin, gint stream)
   GstTagList *result;
   GstSourceGroup *group;
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = get_group (playbin);
   result = get_tags (playbin, group->audio_channels, stream);
-  GST_OBJECT_UNLOCK (playbin);
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   return result;
 }
@@ -981,10 +1024,10 @@ gst_play_bin_get_text_tags (GstPlayBin * playbin, gint stream)
   GstTagList *result;
   GstSourceGroup *group;
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = get_group (playbin);
   result = get_tags (playbin, group->text_channels, stream);
-  GST_OBJECT_UNLOCK (playbin);
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   return result;
 }
@@ -1012,7 +1055,7 @@ gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
   GPtrArray *channels;
   GstPad *sinkpad;
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = get_group (playbin);
   if (!(channels = group->video_channels))
     goto no_channels;
@@ -1026,7 +1069,7 @@ gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
 
   if (sinkpad)
     gst_object_ref (sinkpad);
-  GST_OBJECT_UNLOCK (playbin);
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   if (sinkpad) {
     GstObject *selector;
@@ -1042,7 +1085,7 @@ gst_play_bin_set_current_video_stream (GstPlayBin * playbin, gint stream)
 
 no_channels:
   {
-    GST_OBJECT_UNLOCK (playbin);
+    GST_PLAY_BIN_UNLOCK (playbin);
     return FALSE;
   }
 }
@@ -1054,7 +1097,7 @@ gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
   GPtrArray *channels;
   GstPad *sinkpad;
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = get_group (playbin);
   if (!(channels = group->audio_channels))
     goto no_channels;
@@ -1068,7 +1111,7 @@ gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
 
   if (sinkpad)
     gst_object_ref (sinkpad);
-  GST_OBJECT_UNLOCK (playbin);
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   if (sinkpad) {
     GstObject *selector;
@@ -1084,7 +1127,7 @@ gst_play_bin_set_current_audio_stream (GstPlayBin * playbin, gint stream)
 
 no_channels:
   {
-    GST_OBJECT_UNLOCK (playbin);
+    GST_PLAY_BIN_UNLOCK (playbin);
     return FALSE;
   }
 }
@@ -1096,7 +1139,7 @@ gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
   GPtrArray *channels;
   GstPad *sinkpad;
 
-  GST_OBJECT_LOCK (playbin);
+  GST_PLAY_BIN_LOCK (playbin);
   group = get_group (playbin);
   if (!(channels = group->text_channels))
     goto no_channels;
@@ -1110,7 +1153,7 @@ gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
 
   if (sinkpad)
     gst_object_ref (sinkpad);
-  GST_OBJECT_UNLOCK (playbin);
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   if (sinkpad) {
     GstObject *selector;
@@ -1126,11 +1169,32 @@ gst_play_bin_set_current_text_stream (GstPlayBin * playbin, gint stream)
 
 no_channels:
   {
-    GST_OBJECT_UNLOCK (playbin);
+    GST_PLAY_BIN_UNLOCK (playbin);
     return FALSE;
   }
 }
 
+static void
+gst_play_bin_set_encoding (GstPlayBin * playbin, const gchar * encoding)
+{
+  GstElement *elem;
+
+  GST_PLAY_BIN_LOCK (playbin);
+  g_free (playbin->encoding);
+  playbin->encoding = g_strdup (encoding);
+
+  /* set subtitles on all current and next decodebins. */
+  if ((elem = playbin->groups[0].uridecodebin))
+    g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
+  if ((elem = playbin->groups[0].suburidecodebin))
+    g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
+  if ((elem = playbin->groups[1].uridecodebin))
+    g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
+  if ((elem = playbin->groups[1].suburidecodebin))
+    g_object_set (G_OBJECT (elem), "subtitle-encoding", encoding, NULL);
+  GST_PLAY_BIN_UNLOCK (playbin);
+}
+
 static void
 gst_play_bin_set_property (GObject * object, guint prop_id,
     const GValue * value, GParamSpec * pspec)
@@ -1148,6 +1212,7 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
       break;
     case PROP_FLAGS:
       gst_play_sink_set_flags (playbin->playsink, g_value_get_flags (value));
+      gst_play_sink_reconfigure (playbin->playsink);
       break;
     case PROP_CURRENT_VIDEO:
       gst_play_bin_set_current_video_stream (playbin, g_value_get_int (value));
@@ -1159,10 +1224,15 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
       gst_play_bin_set_current_text_stream (playbin, g_value_get_int (value));
       break;
     case PROP_SUBTITLE_ENCODING:
+      gst_play_bin_set_encoding (playbin, g_value_get_string (value));
       break;
     case PROP_VIDEO_SINK:
+      gst_play_sink_set_video_sink (playbin->playsink,
+          g_value_get_object (value));
       break;
     case PROP_AUDIO_SINK:
+      gst_play_sink_set_audio_sink (playbin->playsink,
+          g_value_get_object (value));
       break;
     case PROP_VIS_PLUGIN:
       gst_play_sink_set_vis_plugin (playbin->playsink,
@@ -1175,11 +1245,13 @@ gst_play_bin_set_property (GObject * object, guint prop_id,
       gst_play_sink_set_mute (playbin->playsink, g_value_get_boolean (value));
       break;
     case PROP_FONT_DESC:
+      gst_play_sink_set_font_desc (playbin->playsink,
+          g_value_get_string (value));
       break;
     case PROP_CONNECTION_SPEED:
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       playbin->connection_speed = g_value_get_uint (value) * 1000;
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1200,20 +1272,20 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
     {
       GstSourceGroup *group;
 
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       group = get_group (playbin);
       g_value_set_string (value, group->uri);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     }
     case PROP_SUBURI:
     {
       GstSourceGroup *group;
 
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       group = get_group (playbin);
       g_value_set_string (value, group->suburi);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     }
     case PROP_SOURCE:
@@ -1226,59 +1298,68 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
       GstSourceGroup *group;
       gint n_video;
 
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       group = get_group (playbin);
       n_video = (group->video_channels ? group->video_channels->len : 0);
       g_value_set_int (value, n_video);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     }
     case PROP_CURRENT_VIDEO:
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       g_value_set_int (value, playbin->current_video);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     case PROP_N_AUDIO:
     {
       GstSourceGroup *group;
       gint n_audio;
 
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       group = get_group (playbin);
       n_audio = (group->audio_channels ? group->audio_channels->len : 0);
       g_value_set_int (value, n_audio);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     }
     case PROP_CURRENT_AUDIO:
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       g_value_set_int (value, playbin->current_audio);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     case PROP_N_TEXT:
     {
       GstSourceGroup *group;
       gint n_text;
 
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       group = get_group (playbin);
       n_text = (group->text_channels ? group->text_channels->len : 0);
       g_value_set_int (value, n_text);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     }
     case PROP_CURRENT_TEXT:
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       g_value_set_int (value, playbin->current_text);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     case PROP_SUBTITLE_ENCODING:
+      GST_PLAY_BIN_LOCK (playbin);
+      g_value_set_string (value, playbin->encoding);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     case PROP_VIDEO_SINK:
+      g_value_set_object (value,
+          gst_play_sink_get_video_sink (playbin->playsink));
       break;
     case PROP_AUDIO_SINK:
+      g_value_set_object (value,
+          gst_play_sink_get_audio_sink (playbin->playsink));
       break;
     case PROP_VIS_PLUGIN:
+      g_value_set_object (value,
+          gst_play_sink_get_vis_plugin (playbin->playsink));
       break;
     case PROP_VOLUME:
       g_value_set_double (value, gst_play_sink_get_volume (playbin->playsink));
@@ -1290,11 +1371,13 @@ gst_play_bin_get_property (GObject * object, guint prop_id, GValue * value,
       gst_value_take_buffer (value, gst_play_bin_convert_frame (playbin, NULL));
       break;
     case PROP_FONT_DESC:
+      g_value_take_string (value,
+          gst_play_sink_get_font_desc (playbin->playsink));
       break;
     case PROP_CONNECTION_SPEED:
-      GST_OBJECT_LOCK (playbin);
+      GST_PLAY_BIN_LOCK (playbin);
       g_value_set_uint (value, playbin->connection_speed / 1000);
-      GST_OBJECT_UNLOCK (playbin);
+      GST_PLAY_BIN_UNLOCK (playbin);
       break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -1366,10 +1449,11 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
   if (select == NULL)
     goto unknown_type;
 
+  GST_SOURCE_GROUP_LOCK (group);
   if (select->selector == NULL) {
     /* no selector, create one */
     GST_DEBUG_OBJECT (playbin, "creating new selector");
-    select->selector = g_object_new (GST_TYPE_STREAM_SELECTOR, NULL);
+    select->selector = gst_element_factory_make ("input-selector", NULL);
     if (select->selector == NULL)
       goto no_selector;
 
@@ -1402,6 +1486,7 @@ pad_added_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
   }
   GST_DEBUG_OBJECT (playbin, "linked pad %s:%s to selector %p",
       GST_DEBUG_PAD_NAME (pad), select->selector);
+  GST_SOURCE_GROUP_UNLOCK (group);
 
   return;
 
@@ -1416,6 +1501,7 @@ no_selector:
   {
     GST_ERROR_OBJECT (playbin, "could not create selector for pad %s:%s",
         GST_DEBUG_PAD_NAME (pad));
+    GST_SOURCE_GROUP_UNLOCK (group);
     return;
   }
 link_failed:
@@ -1423,6 +1509,7 @@ link_failed:
     GST_ERROR_OBJECT (playbin,
         "failed to link pad %s:%s to selector, reason %d",
         GST_DEBUG_PAD_NAME (pad), res);
+    GST_SOURCE_GROUP_UNLOCK (group);
     return;
   }
 }
@@ -1442,6 +1529,7 @@ pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
   GST_DEBUG_OBJECT (playbin,
       "pad %s:%s removed from group %p", GST_DEBUG_PAD_NAME (pad), group);
 
+  GST_SOURCE_GROUP_LOCK (group);
   /* get the selector sinkpad */
   if (!(peer = g_object_get_data (G_OBJECT (pad), "playbin2.sinkpad")))
     goto not_linked;
@@ -1466,6 +1554,7 @@ pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
   gst_element_release_request_pad (selector, peer);
 
   gst_object_unref (selector);
+  GST_SOURCE_GROUP_UNLOCK (group);
 
   return;
 
@@ -1473,11 +1562,13 @@ pad_removed_cb (GstElement * decodebin, GstPad * pad, GstSourceGroup * group)
 not_linked:
   {
     GST_DEBUG_OBJECT (playbin, "pad not linked");
+    GST_SOURCE_GROUP_UNLOCK (group);
     return;
   }
 no_selector:
   {
     GST_DEBUG_OBJECT (playbin, "selector not found");
+    GST_SOURCE_GROUP_UNLOCK (group);
     return;
   }
 }
@@ -1497,15 +1588,21 @@ no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
   GstPlayBin *playbin;
   GstPadLinkReturn res;
   gint i;
+  gboolean configure;
 
   playbin = group->playbin;
 
   GST_DEBUG_OBJECT (playbin, "no more pads in group %p", group);
 
+  GST_SOURCE_GROUP_LOCK (group);
   for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
     GstSourceSelect *select = &group->selector[i];
 
-    if (select->selector) {
+    /* check if the specific media type was detected and thus has a selector
+     * created for it. If there is the media type, get a sinkpad from the sink
+     * and link it. We only do this if we have not yet requested the sinkpad
+     * before. */
+    if (select->selector && select->sinkpad == NULL) {
       select->sinkpad =
           gst_play_sink_request_pad (playbin->playsink, select->type);
       res = gst_pad_link (select->srcpad, select->sinkpad);
@@ -1513,8 +1610,33 @@ no_more_pads_cb (GstElement * decodebin, GstSourceGroup * group)
           res);
     }
   }
-  /* configure the modes now */
-  gst_play_sink_reconfigure (playbin->playsink);
+  group->pending--;
+  if (group->pending == 0) {
+    /* we are the last group to complete, we will configure the output and then
+     * signal the other waiters. */
+    configure = TRUE;
+  } else {
+    configure = FALSE;
+    /* check if there are more decodebins to wait for */
+    while (group->pending) {
+      GST_DEBUG_OBJECT (playbin, "%d pending in group %p, waiting",
+          group->pending, group);
+      /* FIXME, unlock when shutting down */
+      GST_SOURCE_GROUP_WAIT (group);
+    }
+  }
+  GST_SOURCE_GROUP_UNLOCK (group);
+
+  if (configure) {
+    /* we configure the modes if we were the last decodebin to complete. */
+    gst_play_sink_reconfigure (playbin->playsink);
+
+    /* signal the other decodebins that they can continue now. */
+    GST_SOURCE_GROUP_LOCK (group);
+    GST_DEBUG_OBJECT (playbin, "signal other decodebins");
+    GST_SOURCE_GROUP_BROADCAST (group);
+    GST_SOURCE_GROUP_UNLOCK (group);
+  }
 }
 
 /* send an EOS event to all of the selectors */
@@ -1527,6 +1649,8 @@ perform_eos (GstPlayBin * playbin, GstSourceGroup * group)
   GST_DEBUG_OBJECT (playbin, "doing EOS in group %p", group);
 
   event = gst_event_new_eos ();
+
+  GST_SOURCE_GROUP_LOCK (group);
   for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
     GstSourceSelect *select = &group->selector[i];
 
@@ -1536,6 +1660,8 @@ perform_eos (GstPlayBin * playbin, GstSourceGroup * group)
       gst_pad_push_event (select->srcpad, event);
     }
   }
+  GST_SOURCE_GROUP_UNLOCK (group);
+
   gst_event_unref (event);
 }
 
@@ -1592,8 +1718,8 @@ autoplug_factories_cb (GstElement * decodebin, GstPad * pad,
 
 /* We are asked to select an element. See if the next element to check
  * is a sink. If this is the case, we see if the sink works by setting it to
- * READY. If the sink works, we return -2 to make decodebin expose the raw pad
- * so that we can setup the mixers. */
+ * READY. If the sink works, we return SELECT_EXPOSE to make decodebin
+ * expose the raw pad so that we can setup the mixers. */
 static GstAutoplugSelectResult
 autoplug_select_cb (GstElement * decodebin, GstPad * pad,
     GstCaps * caps, GstElementFactory * factory, GstSourceGroup * group)
@@ -1617,6 +1743,17 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
   /* it's a sink, see if an instance of it actually works */
   GST_DEBUG_OBJECT (playbin, "we found a sink");
 
+  klass = gst_element_factory_get_klass (factory);
+
+  /* if we are asked to do visualisations and it's an audio sink, skip the
+   * element. We can only do visualisations with raw sinks */
+  if (gst_play_sink_get_flags (playbin->playsink) & GST_PLAY_FLAG_VIS) {
+    if (strstr (klass, "Audio")) {
+      GST_DEBUG_OBJECT (playbin, "skip audio sink because of vis");
+      return GST_AUTOPLUG_SELECT_SKIP;
+    }
+  }
+
   if ((element = gst_element_factory_create (factory, NULL)) == NULL) {
     GST_WARNING_OBJECT (playbin, "Could not create an element from %s",
         gst_plugin_feature_get_name (GST_PLUGIN_FEATURE (factory)));
@@ -1634,16 +1771,15 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
     return GST_AUTOPLUG_SELECT_SKIP;
   }
 
-  /* at this point, we have the sink working, configure it in playsink */
-  klass = gst_element_factory_get_klass (factory);
-
   /* get klass to figure out if it's audio or video */
   if (strstr (klass, "Audio")) {
     GST_DEBUG_OBJECT (playbin, "configure audio sink");
     gst_play_sink_set_audio_sink (playbin->playsink, element);
+    g_object_notify (G_OBJECT (playbin), "audio-sink");
   } else if (strstr (klass, "Video")) {
     GST_DEBUG_OBJECT (playbin, "configure video sink");
     gst_play_sink_set_video_sink (playbin->playsink, element);
+    g_object_notify (G_OBJECT (playbin), "video-sink");
   } else {
     GST_WARNING_OBJECT (playbin, "unknown sink klass %s found", klass);
   }
@@ -1655,14 +1791,17 @@ autoplug_select_cb (GstElement * decodebin, GstPad * pad,
   return GST_AUTOPLUG_SELECT_EXPOSE;
 }
 
+/* must be called with PLAY_BIN_LOCK */
 static gboolean
 activate_group (GstPlayBin * playbin, GstSourceGroup * group)
 {
   GstElement *uridecodebin;
+  GstElement *suburidecodebin;
 
   g_return_val_if_fail (group->valid, FALSE);
   g_return_val_if_fail (!group->active, FALSE);
 
+  GST_SOURCE_GROUP_LOCK (group);
   if (group->uridecodebin) {
     gst_element_set_state (group->uridecodebin, GST_STATE_NULL);
     gst_bin_remove (GST_BIN_CAST (playbin), group->uridecodebin);
@@ -1676,6 +1815,8 @@ activate_group (GstPlayBin * playbin, GstSourceGroup * group)
   /* configure connection speed */
   g_object_set (uridecodebin, "connection-speed", playbin->connection_speed,
       NULL);
+  /* configure subtitle encoding */
+  g_object_set (uridecodebin, "subtitle-encoding", playbin->encoding, NULL);
   /* configure uri */
   g_object_set (uridecodebin, "uri", group->uri, NULL);
 
@@ -1686,6 +1827,9 @@ activate_group (GstPlayBin * playbin, GstSourceGroup * group)
       group);
   g_signal_connect (uridecodebin, "no-more-pads", G_CALLBACK (no_more_pads_cb),
       group);
+  /* we have 1 pending no-more-pads */
+  group->pending = 1;
+
   /* is called when the uridecodebin is out of data and we can switch to the
    * next uri */
   g_signal_connect (uridecodebin, "drained", G_CALLBACK (drained_cb), group);
@@ -1702,20 +1846,60 @@ activate_group (GstPlayBin * playbin, GstSourceGroup * group)
   gst_bin_add (GST_BIN_CAST (playbin), uridecodebin);
   group->uridecodebin = uridecodebin;
 
+  if (group->suburi) {
+    /* subtitles */
+    if (group->suburidecodebin) {
+      gst_element_set_state (group->suburidecodebin, GST_STATE_NULL);
+      gst_bin_remove (GST_BIN_CAST (playbin), group->suburidecodebin);
+      group->suburidecodebin = NULL;
+    }
+
+    suburidecodebin = gst_element_factory_make ("uridecodebin", NULL);
+    if (!suburidecodebin)
+      goto no_decodebin;
+
+    /* configure connection speed */
+    g_object_set (suburidecodebin, "connection-speed",
+        playbin->connection_speed, NULL);
+    /* configure subtitle encoding */
+    g_object_set (suburidecodebin, "subtitle-encoding", playbin->encoding,
+        NULL);
+    /* configure uri */
+    g_object_set (suburidecodebin, "uri", group->suburi, NULL);
+
+    /*  */
+    gst_bin_add (GST_BIN_CAST (playbin), suburidecodebin);
+    group->suburidecodebin = suburidecodebin;
+
+    /* connect pads and other things */
+    g_signal_connect (suburidecodebin, "pad-added", G_CALLBACK (pad_added_cb),
+        group);
+    g_signal_connect (suburidecodebin, "pad-removed",
+        G_CALLBACK (pad_removed_cb), group);
+    g_signal_connect (suburidecodebin, "no-more-pads",
+        G_CALLBACK (no_more_pads_cb), group);
+    /* we have 2 pending no-more-pads */
+    group->pending = 2;
+
+    gst_element_set_state (suburidecodebin, GST_STATE_PAUSED);
+  }
   gst_element_set_state (uridecodebin, GST_STATE_PAUSED);
 
   group->active = TRUE;
+  GST_SOURCE_GROUP_UNLOCK (group);
 
   return TRUE;
 
   /* ERRORS */
 no_decodebin:
   {
+    GST_SOURCE_GROUP_UNLOCK (group);
     return FALSE;
   }
 }
 
-/* unlink a group of uridecodebins from the sink */
+/* unlink a group of uridecodebins from the sink.
+ * must be called with PLAY_BIN_LOCK */
 static gboolean
 deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
 {
@@ -1726,6 +1910,8 @@ deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
 
   GST_DEBUG_OBJECT (playbin, "unlinking group %p", group);
 
+  GST_SOURCE_GROUP_LOCK (group);
+  group->active = FALSE;
   for (i = 0; i < GST_PLAY_SINK_TYPE_LAST; i++) {
     GstSourceSelect *select = &group->selector[i];
 
@@ -1746,7 +1932,7 @@ deactivate_group (GstPlayBin * playbin, GstSourceGroup * group)
     gst_bin_remove (GST_BIN_CAST (playbin), select->selector);
     select->selector = NULL;
   }
-  group->active = FALSE;
+  GST_SOURCE_GROUP_UNLOCK (group);
 
   return TRUE;
 }
@@ -1762,6 +1948,7 @@ setup_next_source (GstPlayBin * playbin)
   GST_DEBUG_OBJECT (playbin, "setup sources");
 
   /* see if there is a next group */
+  GST_PLAY_BIN_LOCK (playbin);
   new_group = playbin->next_group;
   if (!new_group || !new_group->valid)
     goto no_next_group;
@@ -1781,6 +1968,7 @@ setup_next_source (GstPlayBin * playbin)
   /* swap old and new */
   playbin->curr_group = new_group;
   playbin->next_group = old_group;
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   return TRUE;
 
@@ -1788,11 +1976,13 @@ setup_next_source (GstPlayBin * playbin)
 no_next_group:
   {
     GST_DEBUG_OBJECT (playbin, "no next group");
+    GST_PLAY_BIN_UNLOCK (playbin);
     return FALSE;
   }
 activate_failed:
   {
     GST_DEBUG_OBJECT (playbin, "activate failed");
+    GST_PLAY_BIN_UNLOCK (playbin);
     return FALSE;
   }
 }
@@ -1808,6 +1998,7 @@ save_current_group (GstPlayBin * playbin)
   GST_DEBUG_OBJECT (playbin, "save current group");
 
   /* see if there is a current group */
+  GST_PLAY_BIN_LOCK (playbin);
   curr_group = playbin->curr_group;
   if (curr_group && curr_group->valid) {
     /* unlink our pads with the sink */
@@ -1816,6 +2007,7 @@ save_current_group (GstPlayBin * playbin)
   /* swap old and new */
   playbin->curr_group = playbin->next_group;
   playbin->next_group = curr_group;
+  GST_PLAY_BIN_UNLOCK (playbin);
 
   return TRUE;
 }
@@ -1833,6 +2025,9 @@ gst_play_bin_change_state (GstElement * element, GstStateChange transition)
       if (!setup_next_source (playbin))
         goto source_failed;
       break;
+    case GST_STATE_CHANGE_PAUSED_TO_READY:
+      /* FIXME unlock our waiting groups */
+      break;
     default:
       break;
   }