Index: gst/subparse/gstsubparse.c
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/gst/subparse/gstsubparse.c,v
retrieving revision 1.28
diff -u -r1.28 gstsubparse.c
--- gst/subparse/gstsubparse.c	8 Apr 2006 21:02:51 -0000	1.28
+++ gst/subparse/gstsubparse.c	23 Apr 2006 12:06:05 -0000
@@ -26,6 +26,7 @@
 #include <stdlib.h>
 #include <sys/types.h>
 #include <regex.h>
+#include <libxml/HTMLparser.h>
 
 #include "gstsubparse.h"
 #include "gstssaparse.h"
@@ -64,6 +65,8 @@
 
 static GstFlowReturn gst_sub_parse_chain (GstPad * sinkpad, GstBuffer * buf);
 
+static void sami_context_clean(ParserState * state);
+
 static GstElementClass *parent_class = NULL;
 
 GType
@@ -114,6 +117,7 @@
     gst_segment_free (subparse->segment);
     subparse->segment = NULL;
   }
+  sami_context_clean(&subparse->state);
 
   GST_CALL_PARENT (G_OBJECT_CLASS, dispose, (object));
 }
@@ -545,6 +549,323 @@
   }
 }
 
+#define MAX_STATE 100
+typedef struct
+{
+	gchar *buf, *resultbuf;
+	gchar *rubybuf;
+	gboolean in_title;
+	gchar *state;
+	guint buflen;
+	guint state_len;
+	guint64 time1, time2;
+} samiContext;
+
+static gchar sami_state[MAX_STATE];
+static samiContext samiCtxtStruct = {
+		NULL,
+		NULL,
+		NULL,
+		FALSE,
+		sami_state,
+		0,
+		0,
+		0, 0
+};
+
+static samiContext * samiCtxt = &samiCtxtStruct;
+
+static void
+sami_context_clean(ParserState * state)
+{
+	htmlParserCtxtPtr ctxt = (htmlParserCtxtPtr) state->user_data;
+	if (ctxt) {
+		/* destroy sax context */
+		htmlDocPtr doc = NULL;
+		htmlParseChunk(ctxt, "", 0, 1);
+		doc = ctxt->myDoc;
+		htmlFreeParserCtxt(ctxt);
+		state->user_data = NULL;
+		if (doc) xmlFreeDoc(doc);
+	}
+	if (samiCtxt->buf) {
+		g_free(samiCtxt->buf);
+		samiCtxt->buf = NULL;
+	}
+	if (samiCtxt->resultbuf) {
+		g_free(samiCtxt->resultbuf);
+		samiCtxt->resultbuf = NULL;
+	}
+	samiCtxt->state_len = 0;
+	samiCtxt->in_title = FALSE;
+	samiCtxt->state[0] = '\0';
+	samiCtxt->time1 = samiCtxt->time2 = 0;
+}
+
+static gchar *
+append_string(gchar *dest, const gchar *append)
+{
+	gchar * str;
+	if (dest) {
+		str = g_strconcat(dest, append, NULL);
+		g_free(dest);
+	} else {
+		str = g_strdup(append);
+	}
+	return str;
+}
+
+#define ITALIC_TAG 'i'
+#define COLOR_TAG 'c'
+#define RUBY_TAG 'r'
+#define RT_TAG 't'
+#define CLEAR_TAG '0'
+
+static void
+sami_context_push_state(samiContext *sctx, char state)
+{
+	g_assert(sctx->state_len + 1 < MAX_STATE);
+	sctx->state[sctx->state_len] = state;
+	sctx->state_len++;
+
+	sctx->state[sctx->state_len] = '\0';
+}
+
+static void
+sami_context_pop_state(samiContext *sctx, char state)
+{
+	gchar *str = NULL;
+	int i;
+	for(i = sctx->state_len - 1 ; i >= 0; i--)
+	{
+		switch (sctx->state[i]) {
+			case ITALIC_TAG: /* <i> */
+			{
+				str = append_string(str, "</i>");
+				break;
+			}
+			case COLOR_TAG: /* <span foreground= > */
+			{
+				str = append_string(str, "</span>");
+				break;
+			}
+			case RUBY_TAG: /* <span size= >  -- ruby*/
+			{
+				break;
+			}
+			case RT_TAG: /*  ruby */
+			{
+				/* FIXME */
+				sctx->rubybuf = append_string(sctx->rubybuf, "</span>");
+				char * has_italic = strrchr(sctx->state, ITALIC_TAG);
+				if (has_italic) {
+					sctx->rubybuf = append_string(sctx->rubybuf, "</i>");
+				}
+		
+				break;
+			}
+			default:
+				break;
+		}
+		if (sctx->state[i] == state) {
+			sctx->buf = append_string(sctx->buf, str);
+			g_free(str);
+			sctx->state_len = i;
+			sctx->state[sctx->state_len] = '\0';
+			return;
+		}
+	}
+	if (!str) return;
+	if (state == CLEAR_TAG)
+	{
+		sctx->buf = append_string(sctx->buf, str);
+		sctx->state_len = 0;
+		sctx->state[0] = '\0';
+	}
+	g_free(str);
+}
+
+static void
+handle_start_sync(samiContext * sctx, const xmlChar **atts)
+{
+    int i;
+    sami_context_pop_state(sctx, CLEAR_TAG);
+    if (atts != NULL) {
+        for (i = 0;(atts[i] != NULL);i++) {
+			const xmlChar *key = atts[i++];
+			const xmlChar *value = atts[i];
+			if (!value) continue;
+			if (!xmlStrncmp((const xmlChar*)"start", key, 5)) {
+				sctx->time1 = sctx->time2;
+				sctx->time2 = atoi((const char*)value) * GST_MSECOND;
+				sctx->resultbuf = sctx->buf ? sctx->buf : g_strdup("");
+				sctx->buf = NULL;
+			}
+    	}
+    }
+}
+
+static void
+handle_start_font(samiContext * sctx, const xmlChar **atts)
+{
+    int i;
+    sami_context_pop_state(sctx, COLOR_TAG);
+    if (atts != NULL) {
+        for (i = 0;(atts[i] != NULL);i++) {
+			const xmlChar *key = atts[i++];
+			const xmlChar *value = atts[i];
+			if (!value) continue;
+			if (!xmlStrncmp((const xmlChar*)"color", key, 5)) {
+				/*
+				 * There are invalid color value in many
+				 * sami files.
+				 * It will fix hex color value that start without '#'
+				 */
+				gchar *sharp = "";
+				int len = xmlStrlen(value);
+				if (!(*value == '#' && len == 7)){
+					gchar *r;
+					strtol((const char*)value, &r, 16); /* trying onvert hex */
+					if (((xmlChar *)r == (value+6) && len == 6)){
+						sharp = "#";
+					}
+				}
+				gchar * str = g_strconcat("<span foreground=\"", sharp, value, "\">", NULL);
+				sctx->buf = append_string(sctx->buf, str);
+				g_free(str);
+				sami_context_push_state(sctx, COLOR_TAG);
+			}
+    	}
+    }
+}
+
+static void
+start_sami_element(void *ctx, const xmlChar *name, const xmlChar **atts) {
+	samiContext * sctx = (samiContext *)ctx;
+	if (!xmlStrncmp((const xmlChar*)"title", name, 5)) {
+		sctx->in_title = TRUE;
+	}else if (!xmlStrncmp((const xmlChar*)"sync", name, 4)) {
+		handle_start_sync(sctx, atts);
+	}else if (!xmlStrncmp((const xmlChar*)"font", name, 4)) {
+		handle_start_font(sctx, atts);
+	}else if (!xmlStrncmp((const xmlChar*)"ruby", name, 4)) {
+		sami_context_push_state(sctx, RUBY_TAG);
+	}else if (!xmlStrncmp((const xmlChar*)"br", name, 2)) {
+		sctx->buf = append_string(sctx->buf, "\n");
+		/* FIXME */
+	}else if (!xmlStrncmp((const xmlChar*)"rt", name, 2)) {
+		char * has_italic = strrchr(sctx->state, ITALIC_TAG);
+		if (has_italic) {
+			sctx->rubybuf = append_string(sctx->rubybuf, "<i>");
+		}
+		sctx->rubybuf = append_string(sctx->rubybuf, "<span size='xx-small' rise='-100'>");
+		sami_context_push_state(sctx, RT_TAG);
+	}else if (!xmlStrncmp((const xmlChar*)"p", name, 1)) {
+	}else if (!xmlStrncmp((const xmlChar*)"i", name, 1)) {
+		sctx->buf = append_string(sctx->buf, "<i>");
+		sami_context_push_state(sctx, ITALIC_TAG);
+	}
+}
+
+static void
+end_sami_element(void *ctx , const xmlChar *name)
+{
+	samiContext * sctx = (samiContext *)ctx;
+	if (!xmlStrncmp((const xmlChar*)"title", name, 5)) {
+		sctx->in_title = FALSE;
+	}else if (!xmlStrncmp((const xmlChar*)"font", name, 4)) {
+		sami_context_pop_state(sctx, COLOR_TAG);
+	}else if (!xmlStrncmp((const xmlChar*)"ruby", name, 4)) {
+		sami_context_pop_state(sctx, RUBY_TAG);
+	}else if (!xmlStrncmp((const xmlChar*)"i", name, 1)) {
+		sami_context_pop_state(sctx, ITALIC_TAG);
+	}
+}
+
+static void
+characters_sami(void *ctx , const xmlChar *ch, int len)
+{
+	samiContext * sctx = (samiContext *)ctx;
+	/* skip title */
+	if (sctx->in_title) return;
+	char *has_rt, *has_ruby;
+	has_rt = strrchr(sctx->state, RT_TAG);
+	if (has_rt) {
+		sctx->rubybuf = append_string(sctx->rubybuf, (const gchar*)ch);
+	}else {
+		has_ruby = strrchr(sctx->state, RUBY_TAG);
+		if(sctx->rubybuf) {
+			/* FIXME */
+			int l = len;
+			while(l--) sctx->rubybuf = append_string(sctx->rubybuf, " ");
+		}
+		sctx->buf = append_string(sctx->buf, (const gchar*)ch);
+	}
+}
+
+static xmlSAXHandler samiSAXHandlerStruct = {
+    NULL, /* internalSubset */
+    NULL, /* isStandalone */
+    NULL, /* hasInternalSubset */
+    NULL, /* hasExternalSubset */
+    NULL, /* resolveEntity */
+    NULL, /* getEntity */
+    NULL, /* entityDecl */
+    NULL, /* notationDecl */
+    NULL, /* attributeDecl */
+    NULL, /* elementDecl */
+    NULL, /* unparsedEntityDecl */
+    NULL, /* setDocumentLocator */
+    NULL, /* startDocument */
+    NULL, /* endDocument */
+    start_sami_element, /* startElement */
+    end_sami_element, /* endElement */
+    NULL, /* reference */
+    characters_sami, /* characters */
+    NULL, /* ignorableWhitespace */
+    NULL, /* processingInstruction */
+    NULL, /* comment */
+    NULL, /* xmlParserWarning */
+    NULL, /* xmlParserError */
+    NULL, /* xmlParserError */
+    NULL, /* getParameterEntity */
+    NULL, /* cdataBlock */
+    NULL, /* externalSubset */
+    1,    /* initialized */
+    NULL, /* private */
+    NULL, /* startElementNsSAX2Func */
+    NULL, /* endElementNsSAX2Func */
+    NULL  /* xmlStructuredErrorFunc */
+};
+static xmlSAXHandlerPtr samiSAXHandler = &samiSAXHandlerStruct;
+
+static gchar *
+parse_sami (ParserState * state, const gchar * line)
+{
+	if (!state->user_data){
+		/* initialize sax context */
+		state->user_data = (gpointer)
+			  htmlCreatePushParserCtxt(samiSAXHandler, samiCtxt,
+							 "", 0, NULL, XML_CHAR_ENCODING_UTF8);
+	}
+	htmlParserCtxtPtr ctxt = (htmlParserCtxtPtr) state->user_data;
+	htmlParseChunk(ctxt, line, strlen(line), 0);
+	if (samiCtxt->resultbuf) {
+		gchar *r = NULL;
+		if (samiCtxt->rubybuf) {
+			r = append_string(samiCtxt->rubybuf, "\n");
+			samiCtxt->rubybuf = NULL;
+		}
+		r = append_string(r, samiCtxt->resultbuf);
+		g_free(samiCtxt->resultbuf);
+		samiCtxt->resultbuf = NULL;
+		state->start_time = samiCtxt->time1;
+		state->duration = samiCtxt->time2 - samiCtxt->time1;
+		return r;
+	}
+	return NULL;
+}
+
 static void
 parser_state_init (ParserState * state)
 {
@@ -560,6 +881,7 @@
   state->duration = 0;
   state->state = 0;
   state->segment = NULL;
+  state->user_data = NULL;
 }
 
 static void
@@ -613,7 +935,10 @@
     GST_LOG ("subparse: MPSub (time based) format detected");
     return GST_SUB_PARSE_FORMAT_MPSUB;
   }
-
+  if (!strncmp (match_str, "<SAMI>", 6)) {
+    GST_LOG ("subparse: SAMI (time based) format detected");
+    return GST_SUB_PARSE_FORMAT_SAMI;
+  }
   GST_WARNING ("subparse: subtitle format autodetection failed!");
   return GST_SUB_PARSE_FORMAT_UNKNOWN;
 }
@@ -646,6 +971,9 @@
     case GST_SUB_PARSE_FORMAT_MPSUB:
       self->parse_line = parse_mpsub;
       return gst_caps_new_simple ("text/plain", NULL);
+    case GST_SUB_PARSE_FORMAT_SAMI:
+      self->parse_line = parse_sami;
+      return gst_caps_new_simple ("text/x-pango-markup", NULL);
     case GST_SUB_PARSE_FORMAT_UNKNOWN:
     default:
       GST_DEBUG ("no subtitle format detected");
@@ -662,6 +990,7 @@
     /* flush the parser state */
     parser_state_init (&self->state);
     g_string_truncate (self->textbuf, 0);
+    sami_context_clean(&self->state);
   }
 
   self->textbuf = g_string_append_len (self->textbuf,
@@ -895,6 +1224,9 @@
     case GST_SUB_PARSE_FORMAT_MPSUB:
       GST_DEBUG ("MPSub format detected");
       break;
+    case GST_SUB_PARSE_FORMAT_SAMI:
+      GST_DEBUG ("SAMI format detected");
+      break;
     case GST_SUB_PARSE_FORMAT_UNKNOWN:
       GST_DEBUG ("no subtitle format detected");
       return;
@@ -907,7 +1239,7 @@
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
-  static gchar *sub_exts[] = { "srt", "sub", "mpsub", "mdvd", NULL };
+  static gchar *sub_exts[] = { "srt", "sub", "mpsub", "mdvd", "smi", NULL };
 
   GST_DEBUG_CATEGORY_INIT (sub_parse_debug, "subparse", 0, ".sub parser");
 
Index: gst/subparse/gstsubparse.h
===================================================================
RCS file: /cvs/gstreamer/gst-plugins-base/gst/subparse/gstsubparse.h,v
retrieving revision 1.5
diff -u -r1.5 gstsubparse.h
--- gst/subparse/gstsubparse.h	24 Mar 2006 17:57:39 -0000	1.5
+++ gst/subparse/gstsubparse.h	23 Apr 2006 12:06:05 -0000
@@ -45,7 +45,8 @@
   GST_SUB_PARSE_FORMAT_UNKNOWN = 0,
   GST_SUB_PARSE_FORMAT_MDVDSUB = 1,
   GST_SUB_PARSE_FORMAT_SUBRIP = 2,
-  GST_SUB_PARSE_FORMAT_MPSUB = 3
+  GST_SUB_PARSE_FORMAT_MPSUB = 3,
+  GST_SUB_PARSE_FORMAT_SAMI = 4
 } GstSubParseFormat;
 
 typedef struct {
@@ -54,6 +55,7 @@
   guint64  start_time;
   guint64  duration;
   GstSegment *segment;
+  gpointer *user_data;
 } ParserState;
 
 typedef gchar* (*Parser) (ParserState *state, const gchar *line);
