commit 97788bdc509f0e74c2549fa0575d21617cdf07cd
Author: Arnaud Vrac <avrac@freebox.fr>
Date:   Thu Jun 30 20:32:51 2011

    htmlparse: add plugin

diff --git a/configure.ac b/configure.ac
index 7268961..80f1635 100644
--- a/configure.ac
+++ b/configure.ac
@@ -321,6 +321,7 @@ AG_GST_CHECK_PLUGIN(geometrictransform)
 AG_GST_CHECK_PLUGIN(h264parse)
 AG_GST_CHECK_PLUGIN(hdvparse)
 AG_GST_CHECK_PLUGIN(hls)
+AG_GST_CHECK_PLUGIN(htmlparse)
 AG_GST_CHECK_PLUGIN(id3tag)
 AG_GST_CHECK_PLUGIN(interlace)
 AG_GST_CHECK_PLUGIN(ivfparse)
@@ -1872,6 +1873,7 @@ gst/geometrictransform/Makefile
 gst/h264parse/Makefile
 gst/hdvparse/Makefile
 gst/hls/Makefile
+gst/htmlparse/Makefile
 gst/id3tag/Makefile
 gst/interlace/Makefile
 gst/ivfparse/Makefile
diff --git a/gst/htmlparse/Makefile.am b/gst/htmlparse/Makefile.am
new file mode 100644
index 0000000..0ea3b17
--- /dev/null
+++ b/gst/htmlparse/Makefile.am
@@ -0,0 +1,9 @@
+plugin_LTLIBRARIES = libgsthtmlparse.la
+
+libgsthtmlparse_la_SOURCES = gsthtmlparse.c
+libgsthtmlparse_la_CFLAGS = $(GST_CFLAGS)
+libgsthtmlparse_la_LIBADD = $(GST_LIBS)
+libgsthtmlparse_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS)
+libgsthtmlparse_la_LIBTOOLFLAGS = --tag=disable-static
+
+noinst_HEADERS = gsthtmlparse.h
diff --git a/gst/htmlparse/gsthtmlparse.c b/gst/htmlparse/gsthtmlparse.c
new file mode 100644
index 0000000..1f290cf
--- /dev/null
+++ b/gst/htmlparse/gsthtmlparse.c
@@ -0,0 +1,287 @@
+/* GStreamer HTML parse plugin
+ * Copyright (c) <2011> Arnaud Vrac <avrac@freebox.fr>
+ *
+ * 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 <string.h>
+#include <ctype.h>
+
+#include "gsthtmlparse.h"
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("text/x-pango-markup")
+    );
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS ("text/plain")
+    );
+
+GST_DEBUG_CATEGORY_STATIC (gst_htmlparse_debug);
+#define GST_CAT_DEFAULT gst_htmlparse_debug
+
+static GstFlowReturn gst_html_parse_chain (GstPad * pad, GstBuffer * buf);
+
+GST_BOILERPLATE (GstHtmlParse, gst_html_parse, GstHtmlParse, GST_TYPE_ELEMENT);
+
+static void
+gst_html_parse_base_init (gpointer g_class)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (g_class);
+
+  gst_element_class_set_details_simple (element_class,
+      "inline html subtitle parser", "Codec/Parser/Subtitle",
+      "Parses plain text subtitles with embedded html tags",
+      "Arnaud Vrac <avrac@freebox.fr>");
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&sink_template));
+
+  gst_element_class_add_pad_template (element_class,
+      gst_static_pad_template_get (&src_template));
+}
+
+static void
+gst_html_parse_class_init (GstHtmlParseClass * klass)
+{
+}
+
+static void
+gst_html_parse_init (GstHtmlParse * parser, GstHtmlParseClass * klass)
+{
+  GstPadTemplate *template;
+
+  template = gst_static_pad_template_get (&sink_template);
+  parser->sinkpad = gst_pad_new_from_template (template, "sink");
+  gst_object_unref (template);
+  gst_pad_set_chain_function (parser->sinkpad,
+      GST_DEBUG_FUNCPTR (gst_html_parse_chain));
+  gst_element_add_pad (GST_ELEMENT (parser), parser->sinkpad);
+
+  template = gst_static_pad_template_get (&src_template);
+  parser->srcpad = gst_pad_new_from_template (template, "src");
+  gst_pad_set_caps (parser->srcpad, GST_PAD_TEMPLATE_CAPS (template));
+  gst_pad_use_fixed_caps (parser->srcpad);
+  gst_object_unref (template);
+  gst_element_add_pad (GST_ELEMENT (parser), parser->srcpad);
+}
+
+typedef struct
+{
+  gchar *str;
+  gint len;
+} token_t;
+
+typedef struct
+{
+  GString *buf;
+} parser_t;
+
+static gint
+tokencmp (token_t * token, const gchar * str)
+{
+  return strncasecmp (token->str, str, token->len);
+}
+
+static gboolean
+parse_tag_attr (gchar ** text, token_t * attr, token_t * value)
+{
+  gchar *p = *text;
+
+  memset (attr, 0, sizeof (*attr));
+  memset (value, 0, sizeof (*value));
+
+  while (*p == ' ' || *p == '\t')
+    p++;
+
+  if (*p == '>' || *p == '\0') {
+    *text = p;
+    return FALSE;
+  }
+
+  attr->str = p;
+  attr->len = strcspn (p, "=>");
+  p += attr->len;
+
+  if (*p == '=') {
+    p++;
+    if (*p == '"') {
+      value->str = ++p;
+      value->len = strcspn (p, "\"");
+      p += value->len + 1;
+    } else {
+      value->str = p;
+      value->len = strcspn (p, " \t>");
+      p += value->len;
+    }
+  }
+
+  *text = p;
+
+  return TRUE;
+}
+
+static void
+parse_tag_name (gchar ** text, token_t * name)
+{
+  name->str = *text;
+  name->len = strcspn (*text, " \t>");
+  *text += name->len;
+}
+
+static void
+parse_tag_end (parser_t * parser, gchar ** text)
+{
+  token_t tag;
+
+  parse_tag_name (text, &tag);
+
+  if (!tokencmp (&tag, "font"))
+    g_string_append (parser->buf, "</span>");
+  else if (!tokencmp (&tag, "b") || !tokencmp (&tag, "strong"))
+    g_string_append (parser->buf, "</b>");
+  else if (!tokencmp (&tag, "i") || !tokencmp (&tag, "em"))
+    g_string_append (parser->buf, "</i>");
+  else if (!tokencmp (&tag, "u"))
+    g_string_append (parser->buf, "</u>");
+  else if (!tokencmp (&tag, "s") || !tokencmp (&tag, "strike"))
+    g_string_append (parser->buf, "</s>");
+}
+
+static void
+parse_tag_start (parser_t * parser, gchar ** text)
+{
+  token_t tag;
+  token_t attr, value;
+
+  parse_tag_name (text, &tag);
+
+  if (!tokencmp (&tag, "font")) {
+    g_string_append (parser->buf, "<span");
+    while (parse_tag_attr (text, &attr, &value)) {
+      if (!tokencmp (&attr, "face") || !tokencmp (&attr, "family")) {
+        g_string_append_printf (parser->buf,
+            " face=\"%.*s\" ", value.len, value.str);
+      } else if (!tokencmp (&attr, "size")) {
+        g_string_append_printf (parser->buf,
+            " size=\"%.*s\" ", value.len, value.str);
+      } else if (!tokencmp (&attr, "color")) {
+        g_string_append_printf (parser->buf,
+            " color=\"%.*s\" ", value.len, value.str);
+      }
+    }
+    g_string_append_c (parser->buf, '>');
+    return;
+  }
+
+  if (!tokencmp (&tag, "br") || !tokencmp (&tag, "br/"))
+    g_string_append_c (parser->buf, '\n');
+  else if (!tokencmp (&tag, "b") || !tokencmp (&tag, "strong"))
+    g_string_append (parser->buf, "<b>");
+  else if (!tokencmp (&tag, "i") || !tokencmp (&tag, "em"))
+    g_string_append (parser->buf, "<i>");
+  else if (!tokencmp (&tag, "u"))
+    g_string_append (parser->buf, "<u>");
+  else if (!tokencmp (&tag, "s") || !tokencmp (&tag, "strike"))
+    g_string_append (parser->buf, "<s>");
+
+  while (parse_tag_attr (text, &attr, &value));
+}
+
+static gchar *
+parse_text (gchar * in)
+{
+  parser_t parser;
+  gchar *ret;
+
+  parser.buf = g_string_new ("");
+
+  GST_LOG ("parsing `%s'", in);
+
+  while (isspace (*in))
+    in++;
+
+  while (*in) {
+    if (*in == '<') {
+      in++;
+      if (*in != '/')
+        parse_tag_start (&parser, &in);
+      else {
+        in++;
+        parse_tag_end (&parser, &in);
+      }
+      if (*in == '>')
+        in++;
+    } else {
+      g_string_append_c (parser.buf, *in++);
+    }
+  }
+
+  ret = g_string_free (parser.buf, FALSE);
+
+  return g_strchomp (ret);
+}
+
+static GstFlowReturn
+gst_html_parse_chain (GstPad * pad, GstBuffer * buf)
+{
+  GstHtmlParse *parser;
+  GstFlowReturn ret;
+  GstBuffer *outbuf;
+  gchar *instr, *outstr;
+
+  parser = GST_HTML_PARSE (gst_pad_get_parent (pad));
+
+  /* parse nul terminated input */
+  instr = g_strndup ((gchar *) GST_BUFFER_DATA (buf), GST_BUFFER_SIZE (buf));
+  outstr = parse_text (instr);
+  g_free (instr);
+
+  outbuf = gst_buffer_new ();
+  gst_buffer_set_data (outbuf, (guchar *) outstr, strlen (outstr));
+  gst_buffer_set_caps (outbuf, GST_PAD_CAPS (parser->srcpad));
+  gst_buffer_copy_metadata (outbuf, buf,
+      GST_BUFFER_COPY_FLAGS | GST_BUFFER_COPY_TIMESTAMPS);
+
+  gst_buffer_unref (buf);
+
+  ret = gst_pad_push (parser->srcpad, outbuf);
+  gst_object_unref (parser);
+
+  return ret;
+}
+
+static gboolean
+plugin_init (GstPlugin * plugin)
+{
+  GST_DEBUG_CATEGORY_INIT (gst_htmlparse_debug, "htmlparse", 0,
+      "HTML subtitle parser");
+
+  return gst_element_register (plugin, "htmlparse", GST_RANK_PRIMARY,
+      GST_TYPE_HTML_PARSE);
+}
+
+GST_PLUGIN_DEFINE (GST_VERSION_MAJOR, GST_VERSION_MINOR,
+    "htmlparse", "Parser for subtitles with HTML tags",
+    plugin_init, VERSION, GST_LICENSE, GST_PACKAGE_NAME, GST_PACKAGE_ORIGIN);
diff --git a/gst/htmlparse/gsthtmlparse.h b/gst/htmlparse/gsthtmlparse.h
new file mode 100644
index 0000000..0a49701
--- /dev/null
+++ b/gst/htmlparse/gsthtmlparse.h
@@ -0,0 +1,37 @@
+#ifndef GSTHTMLPARSE_H_
+# define GSTHTMLPARSE_H_
+
+#include <gst/gst.h>
+
+typedef struct _GstHtmlParseClass GstHtmlParseClass;
+typedef struct _GstHtmlParse GstHtmlParse;
+
+struct _GstHtmlParse
+{
+  GstElement parent;
+
+  GstPad *sinkpad;
+  GstPad *srcpad;
+};
+
+struct _GstHtmlParseClass
+{
+  GstElementClass parent_class;
+};
+
+GType gst_html_parse_get_type (void);
+
+#define GST_TYPE_HTML_PARSE \
+	(gst_html_parse_get_type())
+#define GST_HTML_PARSE(obj) \
+	(G_TYPE_CHECK_INSTANCE_CAST((obj), GST_TYPE_HTML_PARSE, GstHtmlParse))
+#define GST_HTML_PARSE_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_CAST((klass), GST_TYPE_HTML_PARSE, GstHtmlParseClass))
+#define GST_HTML_PARSE_GET_CLASS(obj) \
+	(G_TYPE_INSTANCE_GET_CLASS ((obj), GST_TYPE_HTML_PARSE, GstHtmlParseClass))
+#define GST_IS_HTML_PARSE(obj) \
+	(G_TYPE_CHECK_INSTANCE_TYPE((obj), GST_TYPE_HTML_PARSE))
+#define GST_IS_HTML_PARSE_CLASS(klass) \
+	(G_TYPE_CHECK_CLASS_TYPE((klass), GST_TYPE_HTML_PARSE))
+
+#endif /* !GSTHTMLPARSE_H_ */
