tizen 2.0 init
[framework/multimedia/gst-plugins-base0.10.git] / gst / playback / gstsubtitleoverlay.c
index 98fe15d..a3e96c7 100644 (file)
@@ -45,6 +45,8 @@
 #include <gst/video/video.h>
 #include <string.h>
 
+#include "gst/glib-compat-private.h"
+
 GST_DEBUG_CATEGORY_STATIC (subtitle_overlay_debug);
 #define GST_CAT_DEFAULT subtitle_overlay_debug
 
@@ -178,6 +180,59 @@ static const gchar *_sub_pad_names[] = { "subpicture", "subpicture_sink",
   "subtitle_sink", "subtitle"
 };
 
+static inline gboolean
+_is_raw_video (GstStructure * s)
+{
+  const gchar *name;
+
+  name = gst_structure_get_name (s);
+
+  if (g_str_has_prefix (name, "video/x-raw-"))
+    return TRUE;
+  return FALSE;
+}
+
+static gboolean
+_is_video_pad (GstPad * pad, gboolean * hw_accelerated)
+{
+  GstPad *peer = gst_pad_get_peer (pad);
+  GstCaps *caps;
+  gboolean ret;
+  const gchar *name;
+
+  if (peer) {
+    caps = gst_pad_get_negotiated_caps (peer);
+    if (!caps) {
+      caps = gst_pad_get_caps_reffed (peer);
+    }
+    gst_object_unref (peer);
+  } else {
+    caps = gst_pad_get_caps_reffed (pad);
+  }
+
+
+  name = gst_structure_get_name (gst_caps_get_structure (caps, 0));
+  if (g_str_has_prefix (name, "video/x-raw-")) {
+    ret = TRUE;
+    if (hw_accelerated)
+      *hw_accelerated = FALSE;
+
+  } else if (g_str_has_prefix (name, "video/x-surface")) {
+    ret = TRUE;
+    if (hw_accelerated)
+      *hw_accelerated = TRUE;
+  } else {
+
+    ret = FALSE;
+    if (hw_accelerated)
+      *hw_accelerated = FALSE;
+  }
+
+  gst_caps_unref (caps);
+
+  return ret;
+}
+
 static GstCaps *
 _get_sub_caps (GstElementFactory * factory)
 {
@@ -262,26 +317,16 @@ _factory_filter (GstPluginFeature * feature, GstCaps ** subcaps)
   templ_caps = _get_sub_caps (factory);
 
   if (is_renderer && have_video_sink && templ_caps) {
-    GstCaps *tmp;
-
     GST_DEBUG ("Found renderer element %s (%s) with caps %" GST_PTR_FORMAT,
         gst_element_factory_get_longname (factory),
         gst_plugin_feature_get_name (feature), templ_caps);
-    tmp = gst_caps_union (*subcaps, templ_caps);
-    gst_caps_unref (templ_caps);
-    gst_caps_replace (subcaps, tmp);
-    gst_caps_unref (tmp);
+    gst_caps_merge (*subcaps, templ_caps);
     return TRUE;
   } else if (!is_renderer && !have_video_sink && templ_caps) {
-    GstCaps *tmp;
-
     GST_DEBUG ("Found parser element %s (%s) with caps %" GST_PTR_FORMAT,
         gst_element_factory_get_longname (factory),
         gst_plugin_feature_get_name (feature), templ_caps);
-    tmp = gst_caps_union (*subcaps, templ_caps);
-    gst_caps_unref (templ_caps);
-    gst_caps_replace (subcaps, tmp);
-    gst_caps_unref (tmp);
+    gst_caps_merge (*subcaps, templ_caps);
     return TRUE;
   } else {
     if (templ_caps)
@@ -347,7 +392,7 @@ gst_subtitle_overlay_create_factory_caps (void)
 }
 
 static gboolean
-_filter_factories_for_caps (GstElementFactory * factory, const GstCaps * caps)
+check_factory_for_caps (GstElementFactory * factory, const GstCaps * caps)
 {
   GstCaps *fcaps = _get_sub_caps (factory);
   gboolean ret = (fcaps) ? gst_caps_can_intersect (fcaps, caps) : FALSE;
@@ -360,6 +405,26 @@ _filter_factories_for_caps (GstElementFactory * factory, const GstCaps * caps)
   return ret;
 }
 
+static GList *
+gst_subtitle_overlay_get_factories_for_caps (const GList * list,
+    const GstCaps * caps)
+{
+  const GList *walk = list;
+  GList *result = NULL;
+
+  while (walk) {
+    GstElementFactory *factory = walk->data;
+
+    walk = g_list_next (walk);
+
+    if (check_factory_for_caps (factory, caps)) {
+      result = g_list_prepend (result, factory);
+    }
+  }
+
+  return result;
+}
+
 static gint
 _sort_by_ranks (GstPluginFeature * f1, GstPluginFeature * f2)
 {
@@ -606,13 +671,11 @@ _setup_passthrough (GstSubtitleOverlay * self)
 out:
   /* Unblock pads */
   gst_pad_set_blocked_async_full (self->video_block_pad, FALSE,
-      _pad_blocked_cb, gst_object_ref (self),
-      (GDestroyNotify) gst_object_unref);
+      _pad_blocked_cb, self, NULL);
 
   if (self->subtitle_sink_blocked)
     gst_pad_set_blocked_async_full (self->subtitle_block_pad, FALSE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
 
   return TRUE;
 }
@@ -755,13 +818,11 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
 
       /* Unblock pads */
       gst_pad_set_blocked_async_full (self->video_block_pad, FALSE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
 
       if (self->subtitle_sink_blocked)
         gst_pad_set_blocked_async_full (self->subtitle_block_pad, FALSE,
-            _pad_blocked_cb, gst_object_ref (self),
-            (GDestroyNotify) gst_object_unref);
+            _pad_blocked_cb, self, NULL);
       goto out;
     } else if (target) {
       gst_object_unref (target);
@@ -771,8 +832,7 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
   if (self->subtitle_sink_blocked && !self->video_sink_blocked) {
     GST_DEBUG_OBJECT (self, "Subtitle sink blocked but video not blocked");
     gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
     goto out;
   }
 
@@ -782,8 +842,8 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
   g_mutex_lock (self->factories_lock);
   gst_subtitle_overlay_update_factory_list (self);
   if (subcaps) {
-    factories = gst_filter_run (self->factories,
-        (GstFilterFunc) _filter_factories_for_caps, FALSE, subcaps);
+    factories =
+        gst_subtitle_overlay_get_factories_for_caps (self->factories, subcaps);
     if (!factories) {
       GstMessage *msg;
 
@@ -810,7 +870,7 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
 
   for (l = factories; l; l = l->next) {
     GstElementFactory *factory = l->data;
-    gboolean is_renderer = _is_renderer (factory);
+    gboolean is_video, is_hw, is_renderer = _is_renderer (factory);
     GstElement *element;
     GstPad *sink, *src;
 
@@ -841,6 +901,7 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
 
     element = is_renderer ? self->renderer : self->parser;
 
+    is_video = _is_video_pad (self->video_sinkpad, &is_hw);
     /* If this is a parser, create textoverlay and link video and the parser to it
      * Else link the renderer to the output colorspace */
     if (!is_renderer) {
@@ -918,84 +979,113 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
       gst_object_unref (sink);
       gst_object_unref (src);
 
-      if (G_UNLIKELY (!_create_element (self, &self->post_colorspace,
-                  "ffmpegcolorspace", NULL, "post-colorspace", FALSE))) {
-        continue;
-      }
+      /* If we are working with video/x-surface, we do not add
+       * colorspace conversion elements */
+      if (is_video && !is_hw) {
+        if (G_UNLIKELY (!_create_element (self, &self->post_colorspace,
+                    COLORSPACE, NULL, "post-colorspace", FALSE))) {
+          continue;
+        }
 
-      src = gst_element_get_static_pad (overlay, "src");
-      if (G_UNLIKELY (!src)) {
-        GST_WARNING_OBJECT (self, "Can't get src pad from overlay");
-        continue;
-      }
+        src = gst_element_get_static_pad (overlay, "src");
+        if (G_UNLIKELY (!src)) {
+          GST_WARNING_OBJECT (self, "Can't get src pad from overlay");
+          continue;
+        }
 
-      sink = gst_element_get_static_pad (self->post_colorspace, "sink");
-      if (G_UNLIKELY (!sink)) {
-        GST_WARNING_OBJECT (self, "Can't get sink pad from ffmpegcolorspace");
-        gst_object_unref (src);
-        continue;
-      }
+        sink = gst_element_get_static_pad (self->post_colorspace, "sink");
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
+          gst_object_unref (src);
+          continue;
+        }
 
-      if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
-        GST_WARNING_OBJECT (self, "Can't link overlay with ffmpegcolorspace");
+        if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
+          GST_WARNING_OBJECT (self, "Can't link overlay with " COLORSPACE);
+          gst_object_unref (src);
+          gst_object_unref (sink);
+          continue;
+        }
         gst_object_unref (src);
         gst_object_unref (sink);
-        continue;
-      }
-      gst_object_unref (src);
-      gst_object_unref (sink);
 
-      if (G_UNLIKELY (!_create_element (self, &self->pre_colorspace,
-                  "ffmpegcolorspace", NULL, "pre-colorspace", FALSE))) {
-        continue;
-      }
+        if (G_UNLIKELY (!_create_element (self, &self->pre_colorspace,
+                    "identity", NULL, "pre-colorspace", FALSE))) {
+          continue;
+        }
 
-      sink = gst_element_get_static_pad (overlay, "video_sink");
-      if (G_UNLIKELY (!sink)) {
-        GST_WARNING_OBJECT (self, "Can't get video sink from textoverlay");
-        continue;
-      }
+        sink = gst_element_get_static_pad (overlay, "video_sink");
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get video sink from textoverlay");
+          continue;
+        }
 
-      src = gst_element_get_static_pad (self->pre_colorspace, "src");
-      if (G_UNLIKELY (!src)) {
-        GST_WARNING_OBJECT (self, "Can't get srcpad from ffmpegcolorspace");
-        gst_object_unref (sink);
-        continue;
-      }
+        src = gst_element_get_static_pad (self->pre_colorspace, "src");
+        if (G_UNLIKELY (!src)) {
+          GST_WARNING_OBJECT (self, "Can't get srcpad from " COLORSPACE);
+          gst_object_unref (sink);
+          continue;
+        }
 
-      if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
-        GST_WARNING_OBJECT (self, "Can't link ffmpegcolorspace to textoverlay");
+        if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
+          GST_WARNING_OBJECT (self, "Can't link " COLORSPACE " to textoverlay");
+          gst_object_unref (src);
+          gst_object_unref (sink);
+          continue;
+        }
         gst_object_unref (src);
         gst_object_unref (sink);
-        continue;
-      }
-      gst_object_unref (src);
-      gst_object_unref (sink);
 
-      /* Set src ghostpad target */
-      src = gst_element_get_static_pad (self->post_colorspace, "src");
-      if (G_UNLIKELY (!src)) {
-        GST_WARNING_OBJECT (self, "Can't get src pad from ffmpegcolorspace");
-        continue;
-      }
+        /* Set src ghostpad target */
+        src = gst_element_get_static_pad (self->post_colorspace, "src");
+        if (G_UNLIKELY (!src)) {
+          GST_WARNING_OBJECT (self, "Can't get src pad from " COLORSPACE);
+          continue;
+        }
 
-      if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
-                  (self->srcpad), src))) {
-        GST_WARNING_OBJECT (self, "Can't set srcpad target");
+        if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
+                    (self->srcpad), src))) {
+          GST_WARNING_OBJECT (self, "Can't set srcpad target");
+          gst_object_unref (src);
+          continue;
+        }
+        gst_object_unref (src);
+      } else if (is_hw) {
+        GST_DEBUG_OBJECT (self,
+            "Is Hardware, not adding colorspace converters, ");
+        /* Set src ghostpad target */
+        src = gst_element_get_static_pad (self->overlay, "src");
+        if (G_UNLIKELY (!src)) {
+          GST_WARNING_OBJECT (self, "Can't get src pad from textoverlay");
+          continue;
+        }
+
+        if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
+                    (self->srcpad), src))) {
+          GST_WARNING_OBJECT (self, "Can't set srcpad target");
+          gst_object_unref (src);
+          continue;
+        }
         gst_object_unref (src);
-        continue;
       }
-      gst_object_unref (src);
 
       /* Send segments to the parser/overlay if necessary. These are not sent
        * outside this element because of the proxy pad event function */
       if (self->video_segment.format != GST_FORMAT_UNDEFINED) {
         GstEvent *event1, *event2;
 
-        sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
-        if (G_UNLIKELY (!sink)) {
-          GST_WARNING_OBJECT (self, "Can't get sink pad from ffmpegcolorspace");
-          continue;
+        if (is_video) {
+          sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
+          if (G_UNLIKELY (!sink)) {
+            GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
+            continue;
+          }
+        } else {
+          sink = gst_element_get_static_pad (self->overlay, "video_sink");
+          if (G_UNLIKELY (!sink)) {
+            GST_WARNING_OBJECT (self, "Can't get sink pad from textoverlay");
+            continue;
+          }
         }
 
         _generate_update_newsegment_event (&self->video_segment, &event1,
@@ -1036,10 +1126,19 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
       }
 
       /* Set the sink ghostpad targets */
-      sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
-      if (G_UNLIKELY (!sink)) {
-        GST_WARNING_OBJECT (self, "Can't get sink pad from ffmpegcolorspace");
-        continue;
+      if (is_video && !is_hw) {
+        sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
+          continue;
+        }
+      } else if (is_video && is_hw) {
+        GST_DEBUG_OBJECT (self, "Setting ghostpad to overlay video sink");
+        sink = gst_element_get_static_pad (self->overlay, "video_sink");
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get sink pad from overlay");
+          continue;
+        }
       }
 
       if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
@@ -1086,66 +1185,137 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
           g_object_set (self->renderer, "font-desc", self->font_desc, NULL);
       }
 
-      /* First link everything internally */
-      if (G_UNLIKELY (!_create_element (self, &self->post_colorspace,
-                  "ffmpegcolorspace", NULL, "post-colorspace", FALSE))) {
-        continue;
-      }
+      if (is_video) {
+        gboolean render_is_hw;
 
-      src = gst_element_get_static_pad (element, "src");
-      if (G_UNLIKELY (!src)) {
-        GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
-        continue;
-      }
-
-      sink = gst_element_get_static_pad (self->post_colorspace, "sink");
-      if (G_UNLIKELY (!sink)) {
-        GST_WARNING_OBJECT (self, "Can't get sink pad from ffmpegcolorspace");
-        gst_object_unref (src);
-        continue;
-      }
+        /* First check that renderer also supports the video format */
+        sink = _get_video_pad (element);
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
+          continue;
+        }
 
-      if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
-        GST_WARNING_OBJECT (self, "Can't link renderer with ffmpegcolorspace");
-        gst_object_unref (src);
+        if (is_video != _is_video_pad (sink, &render_is_hw) ||
+            is_hw != render_is_hw) {
+          GST_DEBUG_OBJECT (self, "Renderer doesn't support %s video",
+              is_hw ? "surface" : "raw");
+          gst_object_unref (sink);
+          continue;
+        }
         gst_object_unref (sink);
-        continue;
-      }
-      gst_object_unref (src);
-      gst_object_unref (sink);
 
-      if (G_UNLIKELY (!_create_element (self, &self->pre_colorspace,
-                  "ffmpegcolorspace", NULL, "pre-colorspace", FALSE))) {
-        continue;
-      }
+        if (!is_hw) {
+          /* First link everything internally */
+          if (G_UNLIKELY (!_create_element (self, &self->post_colorspace,
+                      COLORSPACE, NULL, "post-colorspace", FALSE))) {
+            continue;
+          }
+          src = gst_element_get_static_pad (element, "src");
+          if (G_UNLIKELY (!src)) {
+            GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
+            continue;
+          }
 
-      sink = _get_video_pad (element);
-      if (G_UNLIKELY (!sink)) {
-        GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
-        continue;
-      }
+          sink = gst_element_get_static_pad (self->post_colorspace, "sink");
+          if (G_UNLIKELY (!sink)) {
+            GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
+            gst_object_unref (src);
+            continue;
+          }
 
-      src = gst_element_get_static_pad (self->pre_colorspace, "src");
-      if (G_UNLIKELY (!src)) {
-        GST_WARNING_OBJECT (self, "Can't get srcpad from ffmpegcolorspace");
-        gst_object_unref (sink);
-        continue;
-      }
+          if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
+            GST_WARNING_OBJECT (self, "Can't link renderer with " COLORSPACE);
+            gst_object_unref (src);
+            gst_object_unref (sink);
+            continue;
+          }
+          gst_object_unref (src);
+          gst_object_unref (sink);
 
-      if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
-        GST_WARNING_OBJECT (self, "Can't link ffmpegcolorspace to renderer");
-        gst_object_unref (src);
+          if (G_UNLIKELY (!_create_element (self, &self->pre_colorspace,
+                      COLORSPACE, NULL, "pre-colorspace", FALSE))) {
+            continue;
+          }
+
+          sink = _get_video_pad (element);
+          if (G_UNLIKELY (!sink)) {
+            GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
+            continue;
+          }
+
+          src = gst_element_get_static_pad (self->pre_colorspace, "src");
+          if (G_UNLIKELY (!src)) {
+            GST_WARNING_OBJECT (self, "Can't get srcpad from " COLORSPACE);
+            gst_object_unref (sink);
+            continue;
+          }
+
+          if (G_UNLIKELY (gst_pad_link (src, sink) != GST_PAD_LINK_OK)) {
+            GST_WARNING_OBJECT (self, "Can't link " COLORSPACE " to renderer");
+            gst_object_unref (src);
+            gst_object_unref (sink);
+            continue;
+          }
+          gst_object_unref (src);
+          gst_object_unref (sink);
+
+          /* Set src ghostpad target */
+          src = gst_element_get_static_pad (self->post_colorspace, "src");
+          if (G_UNLIKELY (!src)) {
+            GST_WARNING_OBJECT (self, "Can't get src pad from " COLORSPACE);
+            continue;
+          }
+        } else {
+          /* Set src ghostpad target in the harware accelerated case */
+
+          src = gst_element_get_static_pad (self->renderer, "src");
+          if (G_UNLIKELY (!src)) {
+            GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
+            continue;
+          }
+        }
+      } else {                  /* No video pad */
+        GstCaps *allowed_caps, *video_caps = NULL;
+        GstPad *video_peer;
+        gboolean can_intersect = FALSE;
+
+        video_peer = gst_pad_get_peer (self->video_sinkpad);
+        if (video_peer) {
+          video_caps = gst_pad_get_negotiated_caps (video_peer);
+          if (!video_caps) {
+            video_caps = gst_pad_get_caps_reffed (video_peer);
+          }
+          gst_object_unref (video_peer);
+        }
+
+        sink = _get_video_pad (element);
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get video sink from renderer");
+          continue;
+        }
+        allowed_caps = gst_pad_get_caps_reffed (sink);
         gst_object_unref (sink);
-        continue;
-      }
-      gst_object_unref (src);
-      gst_object_unref (sink);
 
-      /* Set src ghostpad target */
-      src = gst_element_get_static_pad (self->post_colorspace, "src");
-      if (G_UNLIKELY (!src)) {
-        GST_WARNING_OBJECT (self, "Can't get src pad from ffmpegcolorspace");
-        continue;
+        if (allowed_caps && video_caps)
+          can_intersect = gst_caps_can_intersect (allowed_caps, video_caps);
+
+        if (allowed_caps)
+          gst_caps_unref (allowed_caps);
+
+        if (video_caps)
+          gst_caps_unref (video_caps);
+
+        if (G_UNLIKELY (!can_intersect)) {
+          GST_WARNING_OBJECT (self, "Renderer with custom caps is not "
+              "compatible with video stream");
+          continue;
+        }
+
+        src = gst_element_get_static_pad (element, "src");
+        if (G_UNLIKELY (!src)) {
+          GST_WARNING_OBJECT (self, "Can't get src pad from renderer");
+          continue;
+        }
       }
 
       if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
@@ -1163,7 +1333,7 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
 
         sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
         if (G_UNLIKELY (!sink)) {
-          GST_WARNING_OBJECT (self, "Can't get sink pad from ffmpegcolorspace");
+          GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
           continue;
         }
 
@@ -1203,10 +1373,19 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
       }
 
       /* Set the sink ghostpad targets */
-      sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
-      if (G_UNLIKELY (!sink)) {
-        GST_WARNING_OBJECT (self, "Can't get sink pad from ffmpegcolorspace");
-        continue;
+      if (self->pre_colorspace) {
+        sink = gst_element_get_static_pad (self->pre_colorspace, "sink");
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get sink pad from " COLORSPACE);
+          continue;
+        }
+      } else {
+        sink = _get_video_pad (element);
+        if (G_UNLIKELY (!sink)) {
+          GST_WARNING_OBJECT (self, "Can't get sink pad from %" GST_PTR_FORMAT,
+              element);
+          continue;
+        }
       }
 
       if (G_UNLIKELY (!gst_ghost_pad_set_target (GST_GHOST_PAD_CAST
@@ -1244,11 +1423,9 @@ _pad_blocked_cb (GstPad * pad, gboolean blocked, gpointer user_data)
   } else {
     GST_DEBUG_OBJECT (self, "Everything worked, unblocking pads");
     gst_pad_set_blocked_async_full (self->video_block_pad, FALSE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
     gst_pad_set_blocked_async_full (self->subtitle_block_pad, FALSE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
     do_async_done (self);
   }
 
@@ -1278,11 +1455,9 @@ gst_subtitle_overlay_change_state (GstElement * element,
       GST_SUBTITLE_OVERLAY_LOCK (self);
       /* Set the internal pads to blocking */
       gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
       gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
       GST_SUBTITLE_OVERLAY_UNLOCK (self);
       break;
     case GST_STATE_CHANGE_READY_TO_PAUSED:
@@ -1328,6 +1503,15 @@ gst_subtitle_overlay_change_state (GstElement * element,
       break;
     case GST_STATE_CHANGE_PAUSED_TO_READY:
       GST_DEBUG_OBJECT (self, "State change PAUSED->READY");
+
+      /* Set the pads back to blocking state */
+      GST_SUBTITLE_OVERLAY_LOCK (self);
+      gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
+          _pad_blocked_cb, self, NULL);
+      gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
+          _pad_blocked_cb, self, NULL);
+      GST_SUBTITLE_OVERLAY_UNLOCK (self);
+
       do_async_done (self);
 
       break;
@@ -1349,13 +1533,13 @@ gst_subtitle_overlay_change_state (GstElement * element,
       if (self->video_block_pad) {
         pad = self->video_block_pad;
         gst_pad_set_blocked_async_full (pad, FALSE, _pad_blocked_cb,
-            gst_object_ref (self), (GDestroyNotify) gst_object_unref);
+            self, NULL);
       }
 
       if (self->subtitle_block_pad) {
         pad = self->subtitle_block_pad;
         gst_pad_set_blocked_async_full (pad, FALSE, _pad_blocked_cb,
-            gst_object_ref (self), (GDestroyNotify) gst_object_unref);
+            self, NULL);
       }
 
       /* Remove elements */
@@ -1415,12 +1599,10 @@ gst_subtitle_overlay_handle_message (GstBin * bin, GstMessage * message)
       self->subtitle_error = TRUE;
 
       gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
 
       gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
       GST_SUBTITLE_OVERLAY_UNLOCK (self);
     }
   }
@@ -1476,12 +1658,10 @@ gst_subtitle_overlay_set_property (GObject * object, guint prop_id,
           g_object_set (self->renderer, self->silent_property, silent, NULL);
       } else {
         gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-            _pad_blocked_cb, gst_object_ref (self),
-            (GDestroyNotify) gst_object_unref);
+            _pad_blocked_cb, self, NULL);
 
         gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-            _pad_blocked_cb, gst_object_ref (self),
-            (GDestroyNotify) gst_object_unref);
+            _pad_blocked_cb, self, NULL);
       }
       GST_SUBTITLE_OVERLAY_UNLOCK (self);
       break;
@@ -1517,13 +1697,12 @@ gst_subtitle_overlay_base_init (gpointer g_class)
 {
   GstElementClass *gstelement_class = GST_ELEMENT_CLASS (g_class);
 
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&srctemplate));
+  gst_element_class_add_static_pad_template (gstelement_class, &srctemplate);
 
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&video_sinktemplate));
-  gst_element_class_add_pad_template (gstelement_class,
-      gst_static_pad_template_get (&subtitle_sinktemplate));
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &video_sinktemplate);
+  gst_element_class_add_static_pad_template (gstelement_class,
+      &subtitle_sinktemplate);
 
   gst_element_class_set_details_simple (gstelement_class, "Subtitle Overlay",
       "Video/Overlay/Subtitle",
@@ -1589,7 +1768,7 @@ gst_subtitle_overlay_src_proxy_chain (GstPad * proxypad, GstBuffer * buffer)
     return GST_FLOW_ERROR;
   }
 
-  ret = self->src_proxy_chain (proxypad, buffer);
+  ret = gst_proxy_pad_chain_default (proxypad, buffer);
 
   if (IS_VIDEO_CHAIN_IGNORE_ERROR (ret)) {
     GST_ERROR_OBJECT (self, "Downstream chain error: %s",
@@ -1626,7 +1805,7 @@ gst_subtitle_overlay_src_proxy_event (GstPad * proxypad, GstEvent * event)
     event = NULL;
     ret = TRUE;
   } else {
-    ret = self->src_proxy_event (proxypad, event);
+    ret = gst_proxy_pad_event_default (proxypad, event);
     event = NULL;
   }
 
@@ -1644,18 +1823,33 @@ static gboolean
 gst_subtitle_overlay_video_sink_setcaps (GstPad * pad, GstCaps * caps)
 {
   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (gst_pad_get_parent (pad));
+  GstPad *target;
   gboolean ret = TRUE;
   gint fps_n, fps_d;
 
   GST_DEBUG_OBJECT (pad, "Setting caps: %" GST_PTR_FORMAT, caps);
 
+  target = gst_ghost_pad_get_target (GST_GHOST_PAD_CAST (self->video_sinkpad));
+
+  GST_SUBTITLE_OVERLAY_LOCK (self);
+
+  if (!target || !gst_pad_accept_caps (target, caps)) {
+    GST_DEBUG_OBJECT (pad, "Target did not accept caps -- reconfiguring");
+
+    gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
+        _pad_blocked_cb, self, NULL);
+
+    gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
+        _pad_blocked_cb, self, NULL);
+  }
+
   if (!gst_video_parse_caps_framerate (caps, &fps_n, &fps_d)) {
     GST_ERROR_OBJECT (pad, "Failed to parse framerate from caps");
     ret = FALSE;
+    GST_SUBTITLE_OVERLAY_UNLOCK (self);
     goto out;
   }
 
-  GST_SUBTITLE_OVERLAY_LOCK (self);
   if (self->fps_n != fps_n || self->fps_d != fps_d) {
     GST_DEBUG_OBJECT (self, "New video fps: %d/%d", fps_n, fps_d);
     self->fps_n = fps_n;
@@ -1664,9 +1858,11 @@ gst_subtitle_overlay_video_sink_setcaps (GstPad * pad, GstCaps * caps)
   }
   GST_SUBTITLE_OVERLAY_UNLOCK (self);
 
-  ret = self->video_sink_setcaps (pad, caps);
+  ret = gst_ghost_pad_setcaps_default (pad, caps);
 
 out:
+  if (target)
+    gst_object_unref (target);
   gst_object_unref (self);
   return ret;
 }
@@ -1684,7 +1880,7 @@ gst_subtitle_overlay_video_sink_event (GstPad * pad, GstEvent * event)
     self->fps_n = self->fps_d = 0;
   }
 
-  ret = self->video_sink_event (pad, gst_event_ref (event));
+  ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
 
   if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
     gboolean update;
@@ -1700,7 +1896,7 @@ gst_subtitle_overlay_video_sink_event (GstPad * pad, GstEvent * event)
     if (format != GST_FORMAT_TIME) {
       GST_ERROR_OBJECT (pad, "Newsegment event in non-time format: %s",
           gst_format_get_name (format));
-      gst_object_unref (event);
+      gst_event_unref (event);
       gst_object_unref (self);
       return FALSE;
     }
@@ -1722,7 +1918,7 @@ static GstFlowReturn
 gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstBuffer * buffer)
 {
   GstSubtitleOverlay *self = GST_SUBTITLE_OVERLAY (GST_PAD_PARENT (pad));
-  GstFlowReturn ret = self->video_sink_chain (pad, buffer);
+  GstFlowReturn ret = gst_proxy_pad_chain_default (pad, buffer);
 
   if (G_UNLIKELY (self->downstream_chain_error) || self->passthrough_identity) {
     return ret;
@@ -1732,12 +1928,10 @@ gst_subtitle_overlay_video_sink_chain (GstPad * pad, GstBuffer * buffer)
     GST_SUBTITLE_OVERLAY_LOCK (self);
     self->subtitle_error = TRUE;
     gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
 
     gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
     GST_SUBTITLE_OVERLAY_UNLOCK (self);
 
     return GST_FLOW_OK;
@@ -1755,7 +1949,7 @@ gst_subtitle_overlay_subtitle_sink_chain (GstPad * pad, GstBuffer * buffer)
     gst_buffer_unref (buffer);
     return GST_FLOW_OK;
   } else {
-    GstFlowReturn ret = self->subtitle_sink_chain (pad, buffer);
+    GstFlowReturn ret = gst_proxy_pad_chain_default (pad, buffer);
 
     if (IS_SUBTITLE_CHAIN_IGNORE_ERROR (ret)) {
       GST_DEBUG_OBJECT (self, "Subtitle chain error: %s",
@@ -1763,12 +1957,10 @@ gst_subtitle_overlay_subtitle_sink_chain (GstPad * pad, GstBuffer * buffer)
       GST_SUBTITLE_OVERLAY_LOCK (self);
       self->subtitle_error = TRUE;
       gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
 
       gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
       GST_SUBTITLE_OVERLAY_UNLOCK (self);
 
       return GST_FLOW_OK;
@@ -1802,7 +1994,7 @@ static gboolean
 gst_subtitle_overlay_subtitle_sink_acceptcaps (GstPad * pad, GstCaps * caps)
 {
   GstCaps *othercaps = gst_subtitle_overlay_subtitle_sink_getcaps (pad);
-  gboolean ret = gst_caps_can_intersect (caps, othercaps);
+  gboolean ret = gst_caps_is_subset (caps, othercaps);
 
   gst_caps_unref (othercaps);
 
@@ -1826,7 +2018,7 @@ gst_subtitle_overlay_subtitle_sink_setcaps (GstPad * pad, GstCaps * caps)
 
   if (target && gst_pad_accept_caps (target, caps)) {
     GST_DEBUG_OBJECT (pad, "Target accepts caps");
-    ret = self->subtitle_sink_setcaps (pad, caps);
+    ret = gst_ghost_pad_setcaps_default (pad, caps);
     GST_SUBTITLE_OVERLAY_UNLOCK (self);
     goto out;
   }
@@ -1836,12 +2028,10 @@ gst_subtitle_overlay_subtitle_sink_setcaps (GstPad * pad, GstCaps * caps)
   self->subtitle_error = FALSE;
 
   gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-      _pad_blocked_cb, gst_object_ref (self),
-      (GDestroyNotify) gst_object_unref);
+      _pad_blocked_cb, self, NULL);
 
   gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-      _pad_blocked_cb, gst_object_ref (self),
-      (GDestroyNotify) gst_object_unref);
+      _pad_blocked_cb, self, NULL);
   GST_SUBTITLE_OVERLAY_UNLOCK (self);
 
 out:
@@ -1877,17 +2067,15 @@ gst_subtitle_overlay_subtitle_sink_link (GstPad * pad, GstPad * peer)
     self->subtitle_error = FALSE;
 
     gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
 
     gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
     GST_SUBTITLE_OVERLAY_UNLOCK (self);
     gst_caps_unref (caps);
   }
 
-  ret = self->subtitle_sink_link (pad, peer);
+  ret = gst_ghost_pad_link_default (pad, peer);
 
   gst_object_unref (self);
   return ret;
@@ -1906,20 +2094,18 @@ gst_subtitle_overlay_subtitle_sink_unlink (GstPad * pad)
   GST_DEBUG_OBJECT (pad, "Pad unlinking");
   gst_caps_replace (&self->subcaps, NULL);
 
-  self->subtitle_sink_unlink (pad);
+  gst_ghost_pad_unlink_default (pad);
 
   GST_SUBTITLE_OVERLAY_LOCK (self);
   self->subtitle_error = FALSE;
 
   if (self->subtitle_block_pad)
     gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
 
   if (self->video_block_pad)
     gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-        _pad_blocked_cb, gst_object_ref (self),
-        (GDestroyNotify) gst_object_unref);
+        _pad_blocked_cb, self, NULL);
   GST_SUBTITLE_OVERLAY_UNLOCK (self);
 
   gst_object_unref (self);
@@ -1942,12 +2128,10 @@ gst_subtitle_overlay_subtitle_sink_event (GstPad * pad, GstEvent * event)
     self->subtitle_error = FALSE;
     if (self->subtitle_block_pad)
       gst_pad_set_blocked_async_full (self->subtitle_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
     if (self->video_block_pad)
       gst_pad_set_blocked_async_full (self->video_block_pad, TRUE,
-          _pad_blocked_cb, gst_object_ref (self),
-          (GDestroyNotify) gst_object_unref);
+          _pad_blocked_cb, self, NULL);
     GST_SUBTITLE_OVERLAY_UNLOCK (self);
 
     gst_event_unref (event);
@@ -1993,7 +2177,7 @@ gst_subtitle_overlay_subtitle_sink_event (GstPad * pad, GstEvent * event)
       break;
   }
 
-  ret = self->subtitle_sink_event (pad, gst_event_ref (event));
+  ret = gst_proxy_pad_event_default (pad, gst_event_ref (event));
 
   if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) {
     gboolean update;
@@ -2031,7 +2215,6 @@ gst_subtitle_overlay_init (GstSubtitleOverlay * self,
     GstSubtitleOverlayClass * klass)
 {
   GstPadTemplate *templ;
-  GstIterator *it;
   GstPad *proxypad = NULL;
 
   self->lock = g_mutex_new ();
@@ -2039,67 +2222,48 @@ gst_subtitle_overlay_init (GstSubtitleOverlay * self,
 
   templ = gst_static_pad_template_get (&srctemplate);
   self->srcpad = gst_ghost_pad_new_no_target_from_template ("src", templ);
-  it = gst_pad_iterate_internal_links (self->srcpad);
-  if (G_UNLIKELY (!it
-          || gst_iterator_next (it, (gpointer) & proxypad) != GST_ITERATOR_OK
-          || proxypad == NULL)) {
-    GST_ERROR_OBJECT (self, "Failed to get proxypad of srcpad");
-  } else {
-    self->src_proxy_event = GST_PAD_EVENTFUNC (proxypad);
-    gst_pad_set_event_function (proxypad,
-        GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_event));
-    self->src_proxy_chain = GST_PAD_CHAINFUNC (proxypad);
-    gst_pad_set_chain_function (proxypad,
-        GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_chain));
-    gst_object_unref (proxypad);
-  }
-  if (it)
-    gst_iterator_free (it);
+  gst_object_unref (templ);
+
+  proxypad =
+      GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD (self->srcpad)));
+  gst_pad_set_event_function (proxypad,
+      GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_event));
+  gst_pad_set_chain_function (proxypad,
+      GST_DEBUG_FUNCPTR (gst_subtitle_overlay_src_proxy_chain));
+  gst_object_unref (proxypad);
 
   gst_element_add_pad (GST_ELEMENT_CAST (self), self->srcpad);
 
   templ = gst_static_pad_template_get (&video_sinktemplate);
   self->video_sinkpad =
       gst_ghost_pad_new_no_target_from_template ("video_sink", templ);
-  self->video_sink_event = GST_PAD_EVENTFUNC (self->video_sinkpad);
+  gst_object_unref (templ);
   gst_pad_set_event_function (self->video_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_event));
-  self->video_sink_setcaps = GST_PAD_SETCAPSFUNC (self->video_sinkpad);
   gst_pad_set_setcaps_function (self->video_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_setcaps));
-  self->video_sink_chain = GST_PAD_CHAINFUNC (self->video_sinkpad);
   gst_pad_set_chain_function (self->video_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_video_sink_chain));
 
-  proxypad = NULL;
-  it = gst_pad_iterate_internal_links (self->video_sinkpad);
-  if (G_UNLIKELY (!it
-          || gst_iterator_next (it, (gpointer) & proxypad) != GST_ITERATOR_OK
-          || proxypad == NULL)) {
-    GST_ERROR_OBJECT (self,
-        "Failed to get internally linked pad from video sinkpad");
-  }
-  if (it)
-    gst_iterator_free (it);
+  proxypad =
+      GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
+          (self->video_sinkpad)));
   self->video_block_pad = proxypad;
+  gst_object_unref (proxypad);
   gst_element_add_pad (GST_ELEMENT_CAST (self), self->video_sinkpad);
 
   templ = gst_static_pad_template_get (&subtitle_sinktemplate);
   self->subtitle_sinkpad =
       gst_ghost_pad_new_no_target_from_template ("subtitle_sink", templ);
-  self->subtitle_sink_link = GST_PAD_LINKFUNC (self->subtitle_sinkpad);
+  gst_object_unref (templ);
   gst_pad_set_link_function (self->subtitle_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_link));
-  self->subtitle_sink_unlink = GST_PAD_UNLINKFUNC (self->subtitle_sinkpad);
   gst_pad_set_unlink_function (self->subtitle_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_unlink));
-  self->subtitle_sink_event = GST_PAD_EVENTFUNC (self->subtitle_sinkpad);
   gst_pad_set_event_function (self->subtitle_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_event));
-  self->subtitle_sink_setcaps = GST_PAD_SETCAPSFUNC (self->subtitle_sinkpad);
   gst_pad_set_setcaps_function (self->subtitle_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_setcaps));
-  self->subtitle_sink_chain = GST_PAD_CHAINFUNC (self->subtitle_sinkpad);
   gst_pad_set_chain_function (self->subtitle_sinkpad,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_chain));
   gst_pad_set_getcaps_function (self->subtitle_sinkpad,
@@ -2108,17 +2272,11 @@ gst_subtitle_overlay_init (GstSubtitleOverlay * self,
       GST_DEBUG_FUNCPTR (gst_subtitle_overlay_subtitle_sink_acceptcaps));
   gst_pad_set_bufferalloc_function (self->subtitle_sinkpad, NULL);
 
-  proxypad = NULL;
-  it = gst_pad_iterate_internal_links (self->subtitle_sinkpad);
-  if (G_UNLIKELY (!it
-          || gst_iterator_next (it, (gpointer) & proxypad) != GST_ITERATOR_OK
-          || proxypad == NULL)) {
-    GST_ERROR_OBJECT (self,
-        "Failed to get internally linked pad from subtitle sinkpad");
-  }
-  if (it)
-    gst_iterator_free (it);
+  proxypad =
+      GST_PAD_CAST (gst_proxy_pad_get_internal (GST_PROXY_PAD
+          (self->subtitle_sinkpad)));
   self->subtitle_block_pad = proxypad;
+  gst_object_unref (proxypad);
 
   gst_element_add_pad (GST_ELEMENT_CAST (self), self->subtitle_sinkpad);