Index: ext/alsa/gstalsasink.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/ext/alsa/Attic/gstalsasink.c,v
retrieving revision 1.39.2.2
diff -u -r1.39.2.2 gstalsasink.c
--- ext/alsa/gstalsasink.c	5 May 2005 19:23:06 -0000	1.39.2.2
+++ ext/alsa/gstalsasink.c	19 Jun 2005 11:20:49 -0000
@@ -255,7 +255,7 @@
       case GST_EVENT_FILLER:{
         guint64 dur = gst_event_filler_get_duration (event);
 
-        if (GST_CLOCK_TIME_IS_VALID (dur)) {
+        if (this->format && GST_CLOCK_TIME_IS_VALID (dur)) {
           gint samples;
           GstBuffer *buf;
 
Index: ext/pango/Makefile.am
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/ext/pango/Makefile.am,v
retrieving revision 1.3
diff -u -r1.3 Makefile.am
--- ext/pango/Makefile.am	9 Aug 2003 23:56:38 -0000	1.3
+++ ext/pango/Makefile.am	19 Jun 2005 11:20:52 -0000
@@ -4,14 +4,14 @@
 
 plugin_LTLIBRARIES = libgsttimeoverlay.la libgsttextoverlay.la
 
-noinst_HEADERS = gsttimeoverlay.h gsttextoverlay.h
+noinst_HEADERS = gsttimeoverlay.h gsttextoverlay.h gsttextrender.h
 
 libgsttimeoverlay_la_SOURCES = gsttimeoverlay.c
 libgsttimeoverlay_la_CFLAGS = $(GST_CFLAGS) $(PANGO_CFLAGS) -I$(top_srcdir)/gst/videofilter
 libgsttimeoverlay_la_LIBADD =
 libgsttimeoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(PANGO_LIBS) -lm
 
-libgsttextoverlay_la_SOURCES = gsttextoverlay.c
+libgsttextoverlay_la_SOURCES = gsttextoverlay.c gsttextrender.c
 libgsttextoverlay_la_CFLAGS = $(GST_CFLAGS) $(PANGO_CFLAGS)
 libgsttextoverlay_la_LIBADD =
 libgsttextoverlay_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(PANGO_LIBS)
Index: ext/pango/gsttextoverlay.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/ext/pango/gsttextoverlay.c,v
retrieving revision 1.16.2.5
diff -u -r1.16.2.5 gsttextoverlay.c
--- ext/pango/gsttextoverlay.c	5 May 2005 19:23:15 -0000	1.16.2.5
+++ ext/pango/gsttextoverlay.c	19 Jun 2005 11:20:53 -0000
@@ -22,9 +22,10 @@
 #include <config.h>
 #endif
 #include <gst/gst.h>
+#include "gsttextrender.h"
 #include "gsttextoverlay.h"
 
-GST_DEBUG_CATEGORY_STATIC (pango_debug);
+GST_DEBUG_CATEGORY (pango_debug);
 #define GST_CAT_DEFAULT pango_debug
 
 static GstElementDetails textoverlay_details = {
@@ -810,7 +811,9 @@
 plugin_init (GstPlugin * plugin)
 {
   if (!gst_element_register (plugin, "textoverlay", GST_RANK_NONE,
-          GST_TYPE_TEXTOVERLAY))
+          GST_TYPE_TEXTOVERLAY) ||
+      !gst_element_register (plugin, "textrender", GST_RANK_NONE,
+          GST_TYPE_TEXT_RENDER))
     return FALSE;
 
   /*texttestsrc_plugin_init(module, plugin); */
Index: gst/mpegstream/gstdvddemux.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/mpegstream/gstdvddemux.c,v
retrieving revision 1.18.2.7
diff -u -r1.18.2.7 gstdvddemux.c
--- gst/mpegstream/gstdvddemux.c	14 Jun 2005 22:26:13 -0000	1.18.2.7
+++ gst/mpegstream/gstdvddemux.c	19 Jun 2005 11:21:00 -0000
@@ -309,6 +309,7 @@
   }
 
   dvd_demux->langcodes = NULL;
+  dvd_demux->ignore_next_newmedia_discont = FALSE;
 }
 
 
@@ -455,9 +456,75 @@
 
     gst_event_unref (event);
   } else if (!strcmp (event_type, "dvd-lang-codes")) {
+    gint num_substreams = 0, num_audstreams = 0, n;
+    GstStructure *s;
+    gchar *t;
+
+    /* reset */
     if (dvd_demux->langcodes)
       gst_event_unref (dvd_demux->langcodes);
+    PARSE_CLASS (dvd_demux)->handle_discont (mpeg_parse,
+        gst_event_new_discontinuous (TRUE, GST_FORMAT_UNDEFINED));
+
+    /* see what kind of streams we have */
     dvd_demux->langcodes = event;
+    s = event->event_data.structure.structure;
+
+    /* now create pads for each; first video */
+    n = 2;
+    DEMUX_CLASS (dvd_demux)->get_video_stream (mpeg_demux,
+        0, GST_MPEG_DEMUX_VIDEO_MPEG, &n);
+
+    /* audio */
+    for (n = 0; ; n++) {
+      gint fmt, ifo = 0;
+
+      t = g_strdup_printf ("audio-%d-format", num_audstreams);
+      if (!gst_structure_get_int (s, t, &fmt)) {
+        g_free (t);
+        break;
+      }
+      g_free (t);
+      switch (fmt) {
+        case 0x0: /* AC-3 */
+          fmt = GST_DVD_DEMUX_AUDIO_AC3;
+          break;
+        case 0x2: case 0x3: /* MPEG */
+          fmt = GST_MPEG_DEMUX_AUDIO_MPEG;
+          break;
+        case 0x4:
+          fmt = GST_DVD_DEMUX_AUDIO_LPCM;
+          break;
+        case 0x6:
+          fmt = GST_DVD_DEMUX_AUDIO_DTS;
+          break;
+        default:
+          fmt = GST_MPEG_DEMUX_AUDIO_UNKNOWN;
+          break;
+      }
+      DEMUX_CLASS (dvd_demux)->get_audio_stream (mpeg_demux,
+          num_audstreams++, fmt, &ifo);
+    }
+
+    /* subtitle */
+    for (;;) {
+      t = g_strdup_printf ("subtitle-%d-language", num_substreams);
+      if (!gst_structure_get_value (s, t)) {
+        g_free (t);
+        break;
+      }
+      g_free (t);
+      CLASS (dvd_demux)->get_subpicture_stream (mpeg_demux,
+           num_substreams++, GST_DVD_DEMUX_SUBP_DVD, NULL);
+    }
+    GST_DEBUG_OBJECT (dvd_demux,
+        "Created 1 video stream, %d audio streams and %d subpicture streams "
+        "based on DVD lang codes event; now signalling no-more-pads",
+        num_audstreams, num_substreams);
+
+    /* we know this will be all */
+    gst_element_no_more_pads (GST_ELEMENT (dvd_demux));
+    dvd_demux->ignore_next_newmedia_discont = TRUE;
   } else {
     GST_DEBUG_OBJECT (dvd_demux, "dvddemux Forwarding DVD event %s to all pads",
         event_type);
@@ -512,7 +579,11 @@
   GstDVDDemux *dvd_demux = GST_DVD_DEMUX (mpeg_parse);
 
   if (GST_EVENT_DISCONT_NEW_MEDIA (event)) {
-    gst_dvd_demux_reset (dvd_demux);
+    /* HACK */
+    if (dvd_demux->ignore_next_newmedia_discont)
+      GST_EVENT_DISCONT_NEW_MEDIA (event) = FALSE;
+    else
+      gst_dvd_demux_reset (dvd_demux);
   }
 
   /* let parent handle and forward discont */
@@ -992,6 +1063,8 @@
     dvd_demux->discont_time = GST_CLOCK_TIME_NONE;
   }
 
+  dvd_demux->ignore_next_newmedia_discont = FALSE;
+
   /* You never know what happens to a buffer when you send it.  Just
      in case, we keep a reference to the buffer during the execution
      of this function. */
@@ -1207,6 +1280,7 @@
         gst_event_unref (dvd_demux->langcodes);
         dvd_demux->langcodes = NULL;
       }
+      dvd_demux->ignore_next_newmedia_discont = FALSE;
       break;
     default:
       break;
Index: gst/mpegstream/gstdvddemux.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/mpegstream/gstdvddemux.h,v
retrieving revision 1.7.2.2
diff -u -r1.7.2.2 gstdvddemux.h
--- gst/mpegstream/gstdvddemux.h	17 Apr 2005 20:46:45 -0000	1.7.2.2
+++ gst/mpegstream/gstdvddemux.h	19 Jun 2005 11:21:00 -0000
@@ -113,6 +113,8 @@
 				/* Subpicture output streams. */
 
   GstEvent *langcodes;
+
+  gboolean ignore_next_newmedia_discont;
 };
 
 
Index: gst/playback/gstplaybasebin.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/playback/gstplaybasebin.c,v
retrieving revision 1.62.2.16
diff -u -r1.62.2.16 gstplaybasebin.c
--- gst/playback/gstplaybasebin.c	10 Jun 2005 13:29:53 -0000	1.62.2.16
+++ gst/playback/gstplaybasebin.c	19 Jun 2005 11:21:04 -0000
@@ -46,7 +46,8 @@
   ARG_SOURCE,
   ARG_VIDEO,
   ARG_AUDIO,
-  ARG_TEXT
+  ARG_TEXT,
+  ARG_SUBPICTURE
 };
 
 /* signals */
@@ -177,6 +178,10 @@
       g_param_spec_int ("current-text", "Current text",
           "Currently playing text stream (-1 = none)",
           -1, G_MAXINT, -1, G_PARAM_READWRITE));
+  g_object_class_install_property (gobject_klass, ARG_SUBPICTURE,
+      g_param_spec_int ("current-subpicture", "Current subpicture",
+          "Currently playing subpicture stream (-1 = none)",
+          -1, G_MAXINT, -1, G_PARAM_READWRITE));
 
   GST_DEBUG_CATEGORY_INIT (gst_play_base_bin_debug, "playbasebin", 0,
       "playbasebin");
@@ -585,6 +590,8 @@
     prename = "text";
   else if (type == GST_STREAM_TYPE_AUDIO)
     prename = "audio";
+  else if (type == GST_STREAM_TYPE_SUBPICTURE)
+    prename = "subpicture";
   else
     g_return_if_reached ();
 
@@ -643,6 +650,10 @@
   gst_element_link (selector, preroll);
   group->type[type - 1].selector = selector;
   group->type[type - 1].preroll = preroll;
+  if (type == GST_STREAM_TYPE_SUBPICTURE) {
+    g_object_set (G_OBJECT (preroll), "block-timeout",
+        (guint64) GST_MSECOND * 20, NULL);
+  }
   if (type == GST_STREAM_TYPE_TEXT && play_base_bin->subtitle) {
     group->type[type - 1].bin = GST_BIN (play_base_bin->subtitle);
     gst_bin_add (GST_BIN (play_base_bin->subtitle), selector);
@@ -921,6 +932,33 @@
   return FALSE;
 }
 
+/*
+ */
+
+static gboolean
+has_subtitle_ancestor (GstPad * src)
+{
+  GList *list;
+  GstPad *sink = NULL;
+  gint len;
+
+  for (;;) {
+    if (strstr (GST_OBJECT_NAME (src), "subtitle") ||
+        strstr (GST_OBJECT_NAME (src), "subpicture"))
+      return TRUE;
+    list = gst_pad_get_internal_links (src);
+    if ((len = g_list_length (list)) == 1)
+      if ((sink = list->data))
+        src = GST_PAD_PEER (sink);
+    g_list_free (list);
+
+    if (len != 1 || !sink || !src)
+      break;
+  }
+
+  return FALSE;
+}
+
 /* signal fired when decodebin has found a new raw pad. We create
  * a preroll element if needed and the appropriate streaminfo.
  */
@@ -965,7 +1003,10 @@
   } else if (g_str_has_prefix (mimetype, "video/") &&
       gst_object_get_parent (GST_OBJECT (element)) !=
       GST_OBJECT (play_base_bin->subtitle)) {
-    type = GST_STREAM_TYPE_VIDEO;
+    if (has_subtitle_ancestor (pad))
+      type = GST_STREAM_TYPE_SUBPICTURE;
+    else
+      type = GST_STREAM_TYPE_VIDEO;
   } else if (g_str_has_prefix (mimetype, "text/")) {
     type = GST_STREAM_TYPE_TEXT;
   } else {
@@ -979,16 +1020,18 @@
       GST_DEBUG ("play base: pad needs new preroll");
       gen_preroll_element (play_base_bin, group, type, pad, info);
     }
-  }
 
-  /* add to stream selector */
-  sinkpad = gst_element_get_pad (group->type[type - 1].selector, "sink%d");
-  /* make sure we catch unlink signals */
-  sig = g_signal_connect (G_OBJECT (sinkpad), "unlinked",
-      G_CALLBACK (preroll_unlinked), play_base_bin);
-  /* keep a ref to the signal id so that we can disconnect the signal callback */
-  g_object_set_data (G_OBJECT (sinkpad), "unlinked_id", GINT_TO_POINTER (sig));
-  gst_pad_link (pad, sinkpad);
+    /* add to stream selector */
+    sinkpad = gst_element_get_pad (group->type[type - 1].selector, "sink%d");
+    /* make sure we catch unlink signals */
+    sig = g_signal_connect (G_OBJECT (sinkpad), "unlinked",
+        G_CALLBACK (preroll_unlinked), play_base_bin);
+    /* keep a ref to the signal id so that we can disconnect the
+     * signal callback */
+    g_object_set_data (G_OBJECT (sinkpad), "unlinked_id",
+        GINT_TO_POINTER (sig));
+    gst_pad_link (pad, sinkpad);
+  }
 
   /* add the stream to the list */
   gst_caps_free (caps);
@@ -1652,6 +1695,10 @@
       set_active_source (play_base_bin,
           GST_STREAM_TYPE_TEXT, g_value_get_int (value));
       break;
+    case ARG_SUBPICTURE:
+      set_active_source (play_base_bin,
+          GST_STREAM_TYPE_SUBPICTURE, g_value_get_int (value));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
@@ -1717,6 +1764,10 @@
       g_value_set_int (value, get_active_source (play_base_bin,
               GST_STREAM_TYPE_TEXT));
       break;
+    case ARG_SUBPICTURE:
+      g_value_set_int (value, get_active_source (play_base_bin,
+              GST_STREAM_TYPE_SUBPICTURE));
+      break;
     default:
       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
       break;
Index: gst/playback/gstplaybasebin.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/playback/gstplaybasebin.h,v
retrieving revision 1.12.2.3
diff -u -r1.12.2.3 gstplaybasebin.h
--- gst/playback/gstplaybasebin.h	1 Apr 2005 11:15:22 -0000	1.12.2.3
+++ gst/playback/gstplaybasebin.h	19 Jun 2005 11:21:04 -0000
@@ -58,7 +58,7 @@
     GstElement	*preroll;
     GstElement	*selector;
     gboolean	 done;
-#define NUM_TYPES 3
+#define NUM_TYPES 4
   } type[NUM_TYPES]; /* AUDIO, VIDEO, TEXT */
 } GstPlayBaseGroup;
 
Index: gst/playback/gstplaybin.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/playback/gstplaybin.c,v
retrieving revision 1.36.2.3
diff -u -r1.36.2.3 gstplaybin.c
--- gst/playback/gstplaybin.c	9 May 2005 09:11:16 -0000	1.36.2.3
+++ gst/playback/gstplaybin.c	19 Jun 2005 11:21:05 -0000
@@ -469,21 +469,22 @@
  */
 
 static GstElement *
-gen_text_element (GstPlayBin * play_bin)
+gen_subtitle_element (GstPlayBin * play_bin, gboolean need_text_render)
 {
-  GstElement *element, *csp, *overlay, *vbin;
+  GstElement *render = NULL, *element, *csp, *overlay, *vbin;
 
-  overlay = gst_element_factory_make ("textoverlay", "overlay");
-  g_object_set (G_OBJECT (overlay),
-      "halign", "center", "valign", "bottom", NULL);
-  play_bin->textoverlay_element = overlay;
-  if (play_bin->font_desc) {
-    g_object_set (G_OBJECT (play_bin->textoverlay_element),
-        "font-desc", play_bin->font_desc, NULL);
+  overlay = gst_element_factory_make ("imagemixer", "blender");
+  if (need_text_render) {
+    render = gst_element_factory_make ("textrender", "render");
+    play_bin->textoverlay_element = render;
+    if (play_bin->font_desc) {
+      g_object_set (G_OBJECT (play_bin->textoverlay_element),
+          "font-desc", play_bin->font_desc, NULL);
+    }
   }
   vbin = gen_video_element (play_bin);
-  if (!overlay) {
-    g_warning ("No overlay (pango) element, subtitles disabled");
+  if (!overlay || (need_text_render && !render)) {
+    g_warning ("No overlay rendering element, subtitles disabled");
     return vbin;
   }
   csp = gst_element_factory_make ("ffmpegcolorspace", "subtitlecsp");
@@ -491,11 +492,22 @@
   gst_element_link_many (csp, overlay, vbin, NULL);
   gst_bin_add_many (GST_BIN (element), csp, overlay, vbin, NULL);
 
-  gst_element_add_ghost_pad (element,
-      gst_element_get_pad (overlay, "text_sink"), "text_sink");
+  if (need_text_render) {
+    gst_pad_link (gst_element_get_pad (render, "src"),
+        gst_element_get_pad (overlay, "subpicture_sink_0"));
+    gst_bin_add (GST_BIN (element), render);
+    gst_element_add_ghost_pad (element,
+        gst_element_get_pad (render, "sink"), "subtitle_sink");
+  } else {
+    gst_element_add_ghost_pad (element,
+        gst_element_get_pad (overlay, "subpicture_sink_0"), "subtitle_sink");
+  }
   gst_element_add_ghost_pad (element,
       gst_element_get_pad (csp, "sink"), "sink");
 
+  gst_element_set_state (element, GST_STATE_READY);
+  gst_bin_sync_children_state (GST_BIN (element));
+
   return element;
 }
 
@@ -751,7 +763,7 @@
   GstPlayBaseGroup *group;
   GList *streaminfo = NULL, *s;
   gboolean need_vis = FALSE;
-  gboolean need_text = FALSE;
+  gboolean need_text = FALSE, need_subpicture = FALSE;
   GstPad *textsrcpad = NULL, *textsinkpad = NULL;
   GstElement *sink;
 
@@ -763,9 +775,12 @@
 
   /* find out what to do */
   group = play_base_bin->queued_groups->data;
-  if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0 &&
-      group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
-    need_text = TRUE;
+  if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
+    if (group->type[GST_STREAM_TYPE_SUBPICTURE - 1].npads > 0) {
+      need_subpicture = TRUE;
+    } else if (group->type[GST_STREAM_TYPE_TEXT - 1].npads > 0) {
+      need_text = TRUE;
+    }
   } else if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads == 0 &&
       group->type[GST_STREAM_TYPE_AUDIO - 1].npads > 0 &&
       play_bin->visualisation != NULL) {
@@ -783,7 +798,7 @@
     g_object_get (obj, "object", &object, NULL);
 
     /* use the sink elements as seek entry point */
-    if (type == 4) {
+    if (type == 5) {
       play_bin->seekables = g_list_prepend (play_bin->seekables, object);
     }
   }
@@ -802,13 +817,19 @@
 
   /* link video */
   if (group->type[GST_STREAM_TYPE_VIDEO - 1].npads > 0) {
-    if (need_text) {
-      sink = gen_text_element (play_bin);
+    if (need_text || need_subpicture) {
+      sink = gen_subtitle_element (play_bin, !need_subpicture);
 
-      textsinkpad = gst_element_get_pad (sink, "text_sink");
-      textsrcpad =
-          gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
-          "src");
+      textsinkpad = gst_element_get_pad (sink, "subtitle_sink");
+      if (need_subpicture) {
+        textsrcpad =
+            gst_element_get_pad (group->type[GST_STREAM_TYPE_SUBPICTURE - 1].preroll,
+            "src");
+      } else {
+        textsrcpad =
+            gst_element_get_pad (group->type[GST_STREAM_TYPE_TEXT - 1].preroll,
+            "src");
+      }
       if (textsinkpad && textsrcpad) {
         gst_pad_link (textsrcpad, textsinkpad);
       }
Index: gst/playback/gststreaminfo.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/playback/gststreaminfo.c,v
retrieving revision 1.11.2.1
diff -u -r1.11.2.1 gststreaminfo.c
--- gst/playback/gststreaminfo.c	17 Apr 2005 20:46:45 -0000	1.11.2.1
+++ gst/playback/gststreaminfo.c	19 Jun 2005 11:21:05 -0000
@@ -60,6 +60,7 @@
     {GST_STREAM_TYPE_AUDIO, "GST_STREAM_TYPE_AUDIO", "Audio stream"},
     {GST_STREAM_TYPE_VIDEO, "GST_STREAM_TYPE_VIDEO", "Video stream"},
     {GST_STREAM_TYPE_TEXT, "GST_STREAM_TYPE_TEXT", "Text stream"},
+    {GST_STREAM_TYPE_SUBPICTURE, "GST_STREAM_TYPE_SUBPICTURE", "Subpicture stream"},
     {GST_STREAM_TYPE_ELEMENT, "GST_STREAM_TYPE_ELEMENT",
         "Stream handled by element"},
     {0, NULL, NULL},
Index: gst/playback/gststreaminfo.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins/gst/playback/gststreaminfo.h,v
retrieving revision 1.7.4.1
diff -u -r1.7.4.1 gststreaminfo.h
--- gst/playback/gststreaminfo.h	17 Apr 2005 20:46:45 -0000	1.7.4.1
+++ gst/playback/gststreaminfo.h	19 Jun 2005 11:21:05 -0000
@@ -35,11 +35,12 @@
 typedef struct _GstStreamInfoClass GstStreamInfoClass;
 
 typedef enum {
-  GST_STREAM_TYPE_UNKNOWN = 0,
-  GST_STREAM_TYPE_AUDIO   = 1,	/* an audio stream */
-  GST_STREAM_TYPE_VIDEO   = 2,	/* a video stream */
-  GST_STREAM_TYPE_TEXT    = 3,	/* a subtitle/text stream */
-  GST_STREAM_TYPE_ELEMENT = 4,	/* stream handled by an element */
+  GST_STREAM_TYPE_UNKNOWN    = 0,
+  GST_STREAM_TYPE_AUDIO      = 1,	/* an audio stream */
+  GST_STREAM_TYPE_VIDEO      = 2,	/* a video stream */
+  GST_STREAM_TYPE_TEXT       = 3,	/* a subtitle/text stream */
+  GST_STREAM_TYPE_SUBPICTURE = 4,	/* a subtitle in picture-form */
+  GST_STREAM_TYPE_ELEMENT    = 5,	/* stream handled by an element */
 } GstStreamType;
 
 struct _GstStreamInfo {
--- /dev/null	2005-06-19 12:17:19.307478024 +0200
+++ ext/dvdnav/gst-dvd-2	2005-06-19 12:32:42.000000000 +0200
@@ -0,0 +1 @@
+/home/rbultje/Projects/GStreamer/0.8/gstreamer/tools/gst-launch-0.8 dvdnavsrc ! dvddemux name=demux .current_video ! mpeg2dec ! queue max-size-bytes=25000000 ! { imagemixer name=i ! ffmpegcolorspace ! videoscale ! xvimagesink } demux.current_subpicture ! dvdsubdec ! queue max-size-buffers=0 max-size-bytes=0 max-size-time=0 block-timeout=10000000 ! i.subpicture_sink_%d #demux.current_audio ! a52dec ! { queue ! audioconvert ! audioscale ! alsasink }
--- /dev/null	2005-06-19 12:17:19.307478024 +0200
+++ ext/pango/gsttextrender.c	2005-06-11 16:29:28.000000000 +0200
@@ -0,0 +1,420 @@
+/* GStreamer
+ * Copyright (C) <1999> Erik Walthinsen <omega@cse.ogi.edu>
+ * Copyright (C) <2003> David Schleef <ds@schleef.org>
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library; if not, write to the
+ * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+ * Boston, MA 02111-1307, USA.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+#include <gst/gst.h>
+#include "gsttextrender.h"
+
+GST_DEBUG_CATEGORY_EXTERN (pango_debug);
+#define GST_CAT_DEFAULT pango_debug
+
+static GstElementDetails text_render_details = {
+  "Text Render",
+  "Filter/Editor/Video",
+  "Renders a text string to a image bitmap",
+  "David Schleef <ds@schleef.org>, "
+  "Ronald S. Bultje <rbultje@ronald.bitfreak.net>"
+};
+
+enum
+{
+  ARG_0,
+  ARG_FONT_DESC
+};
+
+
+static GstStaticPadTemplate src_template_factory =
+GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("video/x-raw-yuv, "
+        "format = (fourcc) AYUV, "
+        "width = (int) [ 1, MAX ], "
+        "height = (int) [ 1, MAX ], framerate = (double) 1")
+    );
+
+static GstStaticPadTemplate sink_template_factory =
+    GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("text/x-pango-markup; text/plain")
+    );
+
+static void gst_text_render_base_init (gpointer g_class);
+static void gst_text_render_class_init (GstTextRenderClass * klass);
+static void gst_text_render_init (GstTextRender * overlay);
+static void gst_text_render_set_property (GObject * object,
+    guint prop_id, const GValue * value, GParamSpec * pspec);
+static void gst_text_render_get_property (GObject * object,
+    guint prop_id, GValue * value, GParamSpec * pspec);
+static void gst_text_render_finalize (GObject * object);
+
+static GstElementClass *parent_class = NULL;
+
+/*static guint gst_text_render_signals[LAST_SIGNAL] = { 0 }; */
+
+GType
+gst_text_render_get_type (void)
+{
+  static GType text_render_type = 0;
+
+  if (!text_render_type) {
+    static const GTypeInfo text_render_info = {
+      sizeof (GstTextRenderClass),
+      gst_text_render_base_init,
+      NULL,
+      (GClassInitFunc) gst_text_render_class_init,
+      NULL,
+      NULL,
+      sizeof (GstTextRender),
+      0,
+      (GInstanceInitFunc) gst_text_render_init,
+    };
+
+    text_render_type =
+        g_type_register_static (GST_TYPE_ELEMENT, "GstTextRender",
+        &text_render_info, 0);
+  }
+  return text_render_type;
+}
+
+static void
+gst_text_render_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_template_factory));
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_template_factory));
+
+  gst_element_class_set_details (element_class, &text_render_details);
+}
+
+static void
+gst_text_render_class_init (GstTextRenderClass * klass)
+{
+  GObjectClass *gobject_class;
+  GstElementClass *gstelement_class;
+
+  gobject_class = (GObjectClass *) klass;
+  gstelement_class = (GstElementClass *) klass;
+
+  parent_class = g_type_class_peek_parent (klass);
+
+  gobject_class->finalize = gst_text_render_finalize;
+  gobject_class->set_property = gst_text_render_set_property;
+  gobject_class->get_property = gst_text_render_get_property;
+
+  klass->pango_context = pango_ft2_get_context (72, 72);
+  g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_FONT_DESC,
+      g_param_spec_string ("font-desc", "font description",
+          "Pango font description of font "
+          "to be used for rendering. "
+          "See documentation of "
+          "pango_font_description_from_string"
+          " for syntax.", "", G_PARAM_WRITABLE));
+}
+
+
+static void
+resize_bitmap (GstTextRender * overlay, int width, int height)
+{
+  FT_Bitmap *bitmap = &overlay->bitmap;
+  int pitch = (width | 3) + 1;
+  int size = pitch * height;
+
+  /* no need to keep reallocating; just keep the maximum size so far */
+  if (size <= overlay->bitmap_buffer_size) {
+    bitmap->rows = height;
+    bitmap->width = width;
+    bitmap->pitch = pitch;
+    memset (bitmap->buffer, 0, overlay->bitmap_buffer_size);
+    return;
+  }
+  if (!bitmap->buffer) {
+    /* initialize */
+    bitmap->pixel_mode = ft_pixel_mode_grays;
+    bitmap->num_grays = 256;
+  }
+  if (bitmap->buffer)
+    bitmap->buffer = g_realloc (bitmap->buffer, size);
+  else
+    bitmap->buffer = g_malloc (size);
+  bitmap->rows = height;
+  bitmap->width = width;
+  bitmap->pitch = pitch;
+  memset (bitmap->buffer, 0, size);
+  overlay->bitmap_buffer_size = size;
+}
+
+static void
+render_text (GstTextRender * overlay)
+{
+  PangoRectangle ink_rect, logical_rect;
+
+  pango_layout_get_pixel_extents (overlay->layout, &ink_rect, &logical_rect);
+  resize_bitmap (overlay, ink_rect.width, ink_rect.height + ink_rect.y);
+  pango_ft2_render_layout (&overlay->bitmap, overlay->layout, -ink_rect.x, 0);
+  overlay->baseline_y = ink_rect.y;
+}
+
+static GstPadLinkReturn
+gst_text_render_link (GstPad * pad, const GstCaps * caps)
+{
+  GstTextRender *overlay = GST_TEXT_RENDER (gst_pad_get_parent (pad));
+  GstStructure *structure;
+
+  structure = gst_caps_get_structure (caps, 0);
+  overlay->width = overlay->height = 0;
+  gst_structure_get_int (structure, "width", &overlay->width);
+  gst_structure_get_int (structure, "height", &overlay->height);
+
+  return GST_PAD_LINK_OK;
+}
+
+static GstCaps *
+gst_text_render_fixate (GstPad * pad, const GstCaps * caps)
+{
+  GstTextRender *overlay = GST_TEXT_RENDER (gst_pad_get_parent (pad));
+  GstCaps *copy = gst_caps_copy (caps);
+  GstStructure *s = gst_caps_get_structure (copy, 0);
+
+  if (gst_caps_structure_fixate_field_nearest_int (s, "width",
+          overlay->bitmap.width) ||
+      gst_caps_structure_fixate_field_nearest_int (s, "height",
+          overlay->bitmap.rows))
+    return copy;
+
+  gst_caps_free (copy);
+
+  return NULL;
+}
+
+static void
+gst_text_overlay_blit_yuv420 (GstTextRender * overlay, FT_Bitmap * bitmap,
+    guchar * pixbuf, int x0, int y0)
+{
+  int y;                        /* text bitmap coordinates */
+  int x1, y1;                   /* video buffer coordinates */
+  int rowinc, bit_rowinc;
+  guchar *p, *bitp;
+  int video_width = overlay->width, video_height = overlay->height;
+  int bitmap_x0 = 0;            //x0 < 1 ? -(x0 - 1) : 1;       /* 1 pixel border */
+  int bitmap_y0 = y0 < 1 ? -(y0 - 1) : 1;       /* 1 pixel border */
+  int bitmap_width = bitmap->width - bitmap_x0;
+  int bitmap_height = bitmap->rows - bitmap_y0;
+  int skip_y, skip_x;
+  guchar v;
+
+  if (x0 + bitmap_x0 + bitmap_width > video_width - 1)  /* 1 pixel border */
+    bitmap_width -= x0 + bitmap_x0 + bitmap_width - video_width + 1;
+  if (y0 + bitmap_y0 + bitmap_height > video_height - 1)        /* 1 pixel border */
+    bitmap_height -= y0 + bitmap_y0 + bitmap_height - video_height + 1;
+
+  rowinc = video_width - bitmap_width;
+  bit_rowinc = bitmap->pitch - bitmap_width;
+
+  y1 = y0 + bitmap_y0;
+  x1 = x0 + bitmap_x0;
+  p = pixbuf + 1 + (video_width * y1 + x1) * 4;
+  bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
+  for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
+    int n;
+
+    for (n = bitmap_width; n > 0; --n) {
+      v = *bitp;
+      if (v) {
+        p[-4] = CLAMP (p[-4] - v, 0, 255);
+        p[4] = CLAMP (p[4] - v, 0, 255);
+        p[-4 * video_width] = CLAMP (p[-4 * video_width] - v, 0, 255);
+        p[4 * video_width] = CLAMP (p[4 * video_width] - v, 0, 255);
+      }
+      p += 4;
+      bitp++;
+    }
+    p += rowinc * 4;
+    bitp += bit_rowinc;
+  }
+
+  y1 = y0 + bitmap_y0;
+  x1 = x0 + bitmap_x0;
+  bitp = bitmap->buffer + bitmap->pitch * bitmap_y0 + bitmap_x0;
+  p = pixbuf + (video_width * y1 + x1) * 4;
+  skip_y = 0;
+  skip_x = 0;
+
+  for (y = bitmap_y0; y < bitmap_y0 + bitmap_height; y++) {
+    int n;
+
+    x1 = x0 + bitmap_x0;
+    skip_x = 0;
+    for (n = bitmap_width; n > 0; --n) {
+      v = *bitp;
+      if (v) {
+        p[0] = v;
+        p[1] = 255;
+        p[2] = p[3] = 0x80;
+      }
+      p += 4;
+      bitp++;
+    }
+    /*if (!skip_x && !skip_y) u_p--; */
+    p += rowinc * 4;
+    bitp += bit_rowinc;
+  }
+}
+
+
+static void
+gst_text_render_chain (GstPad * pad, GstData * _data)
+{
+  GstBuffer *buf = GST_BUFFER (_data), *out;
+  GstTextRender *overlay = GST_TEXT_RENDER (gst_pad_get_parent (pad));
+  guint size = GST_BUFFER_SIZE (buf);
+  guint8 *data = GST_BUFFER_DATA (buf);
+
+  /* somehow pango barfs over "\0" buffers... */
+  while (size > 0 &&
+      (data[size - 1] == '\r' ||
+          data[size - 1] == '\n' || data[size - 1] == '\0')) {
+    size--;
+  }
+
+  /* render text */
+  GST_DEBUG ("rendering '%*s'", size, data);
+  pango_layout_set_markup (overlay->layout, (gchar *) data, size);
+  render_text (overlay);
+
+  if (GST_PAD_LINK_FAILED (gst_pad_renegotiate (overlay->srcpad))) {
+    GST_ELEMENT_ERROR (overlay, CORE, NEGOTIATION, (NULL), (NULL));
+    return;
+  }
+
+  /* put in a buffer */
+  out = gst_buffer_new_and_alloc (overlay->width * overlay->height * 4);
+  gst_buffer_stamp (out, GST_BUFFER (buf));
+//  gst_buffer_stamp (out, buf);
+  data = GST_BUFFER_DATA (out);
+gint n;
+for (n = 0; n < overlay->width * overlay->height; n++){
+data[n*4] = 0;
+data[n*4+1] = 0;
+data[n*4+2] = data[n*4+3] = 128;
+}
+  if (overlay->bitmap.buffer) {
+    gst_text_overlay_blit_yuv420 (overlay, &overlay->bitmap, data, 0, 0);
+  }
+  gst_data_unref (_data);
+
+  gst_pad_push (overlay->srcpad, GST_DATA (out));
+}
+
+static void
+gst_text_render_finalize (GObject * object)
+{
+  GstTextRender *overlay = GST_TEXT_RENDER (object);
+
+  if (overlay->layout) {
+    g_object_unref (overlay->layout);
+    overlay->layout = NULL;
+  }
+  if (overlay->bitmap.buffer) {
+    g_free (overlay->bitmap.buffer);
+    overlay->bitmap.buffer = NULL;
+  }
+
+  G_OBJECT_CLASS (parent_class)->finalize (object);
+}
+
+static void
+gst_text_render_init (GstTextRender * overlay)
+{
+  /* sink */
+  overlay->sinkpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&sink_template_factory), "sink");
+  gst_pad_set_chain_function (overlay->sinkpad, gst_text_render_chain);
+  gst_element_add_pad (GST_ELEMENT (overlay), overlay->sinkpad);
+
+  /* source */
+  overlay->srcpad =
+      gst_pad_new_from_template (gst_static_pad_template_get
+      (&src_template_factory), "src");
+  gst_pad_set_link_function (overlay->srcpad, gst_text_render_link);
+  gst_pad_set_fixate_function (overlay->srcpad, gst_text_render_fixate);
+  gst_element_add_pad (GST_ELEMENT (overlay), overlay->srcpad);
+
+  overlay->layout =
+      pango_layout_new (GST_TEXT_RENDER_GET_CLASS (overlay)->pango_context);
+  memset (&overlay->bitmap, 0, sizeof (overlay->bitmap));
+}
+
+
+static void
+gst_text_render_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstTextRender *overlay;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TEXT_RENDER (object));
+  overlay = GST_TEXT_RENDER (object);
+
+  switch (prop_id) {
+    case ARG_FONT_DESC:
+    {
+      PangoFontDescription *desc;
+
+      desc = pango_font_description_from_string (g_value_get_string (value));
+      if (desc) {
+        GST_LOG ("font description set: %s", g_value_get_string (value));
+        pango_layout_set_font_description (overlay->layout, desc);
+        pango_font_description_free (desc);
+        render_text (overlay);
+      } else
+        GST_WARNING ("font description parse failed: %s",
+            g_value_get_string (value));
+      break;
+    }
+
+    default:
+      break;
+  }
+}
+
+static void
+gst_text_render_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstTextRender *overlay;
+
+  /* it's not null if we got it, but it might not be ours */
+  g_return_if_fail (GST_IS_TEXT_RENDER (object));
+  overlay = GST_TEXT_RENDER (object);
+
+  switch (prop_id) {
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
--- /dev/null	2005-06-19 12:17:19.307478024 +0200
+++ ext/pango/gsttextrender.h	2005-06-05 15:15:34.000000000 +0200
@@ -0,0 +1,47 @@
+/* -*- Mode: C; c-file-style: "stroustrup" -*- */
+#ifndef __GST_TEXT_RENDER_H__
+#define __GST_TEXT_RENDER_H__
+
+#include <gst/gst.h>
+#include <pango/pangoft2.h>
+
+G_BEGIN_DECLS
+
+#define GST_TYPE_TEXT_RENDER           (gst_text_render_get_type())
+#define GST_TEXT_RENDER(obj)           (G_TYPE_CHECK_INSTANCE_CAST((obj),\
+                                        GST_TYPE_TEXT_RENDER, GstTextRender))
+#define GST_TEXT_RENDER_CLASS(klass)   (G_TYPE_CHECK_CLASS_CAST((klass),\
+                                        GST_TYPE_ULAW, GstTextRender))
+#define GST_TEXT_RENDER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),\
+                                        GST_TYPE_TEXT_RENDER, GstTextRenderClass))
+#define GST_IS_TEXT_RENDER(obj)        (G_TYPE_CHECK_INSTANCE_TYPE((obj),\
+                                        GST_TYPE_TEXT_RENDER))
+#define GST_IS_TEXT_RENDER_CLASS(obj)  (G_TYPE_CHECK_CLASS_TYPE((klass),\
+                                        GST_TYPE_TEXT_RENDER))
+
+typedef struct _GstTextRender      GstTextRender;
+typedef struct _GstTextRenderClass GstTextRenderClass;
+
+struct _GstTextRender {
+    GstElement            element;
+
+    GstPad               *sinkpad, *srcpad;
+    gint                  width;
+    gint                  height;
+    PangoLayout          *layout;
+    FT_Bitmap             bitmap;
+    gint                  bitmap_buffer_size;
+    gint                  baseline_y;
+};
+
+struct _GstTextRenderClass {
+    GstElementClass parent_class;
+
+    PangoContext *pango_context;
+};
+
+GType gst_text_render_get_type(void) G_GNUC_CONST;
+
+G_END_DECLS
+
+#endif /* __GST_TEXT_RENDER_H */
