 create mode 100644 ext/rsvg/gstrsvgoverlay.c
 create mode 100644 ext/rsvg/gstrsvgoverlay.h

diff --git a/ext/rsvg/Makefile.am b/ext/rsvg/Makefile.am
index 3b2f43c..d1b04b9 100644
--- a/ext/rsvg/Makefile.am
+++ b/ext/rsvg/Makefile.am
@@ -1,6 +1,6 @@
 plugin_LTLIBRARIES = libgstrsvg.la
 
-libgstrsvg_la_SOURCES = gstrsvg.c gstrsvgdec.c
+libgstrsvg_la_SOURCES = gstrsvg.c gstrsvgdec.c gstrsvgoverlay.c
 libgstrsvg_la_CFLAGS = \
 	$(GST_PLUGINS_BASE_CFLAGS) \
 	$(GST_CFLAGS) \
@@ -13,4 +13,4 @@ libgstrsvg_la_LIBADD = \
 libgstrsvg_la_LDFLAGS = $(GST_PLUGIN_LDFLAGS) $(LIBM)
 libgstrsvg_la_LIBTOOLFLAGS = --tag=disable-static
 
-noinst_HEADERS = gstrsvgdec.h 
+noinst_HEADERS = gstrsvgdec.h gstrsvgoverlay.h
diff --git a/ext/rsvg/gstrsvg.c b/ext/rsvg/gstrsvg.c
index a7c6006..1318e70 100644
--- a/ext/rsvg/gstrsvg.c
+++ b/ext/rsvg/gstrsvg.c
@@ -1,5 +1,6 @@
 /* GStreamer
  * Copyright (C) <2009> Sebastian Dröge <sebastian.droege@collabora.co.uk>
+ *           (C) 2010 Olivier Aubert <olivier.aubert@liris.cnrs.fr>
  *
  * This library is free software; you can redistribute it and/or
  * modify it under the terms of the GNU Library General Public
@@ -23,17 +24,18 @@
 #include <gst/gst.h>
 
 #include "gstrsvgdec.h"
+#include "gstrsvgoverlay.h"
 
 static gboolean
 plugin_init (GstPlugin * plugin)
 {
   rsvg_init ();
 
-  if (!gst_element_register (plugin, "rsvgdec", GST_RANK_PRIMARY,
-          GST_TYPE_RSVG_DEC))
-    return FALSE;
-
-  return TRUE;
+  return (gst_element_register (plugin, "rsvgoverlay",
+          GST_RANK_NONE, GST_TYPE_RSVG_OVERLAY)
+      &&
+      gst_element_register (plugin, "rsvgdec", GST_RANK_PRIMARY,
+          GST_TYPE_RSVG_DEC));
 }
 
 GST_PLUGIN_DEFINE (GST_VERSION_MAJOR,
diff --git a/ext/rsvg/gstrsvgoverlay.c b/ext/rsvg/gstrsvgoverlay.c
new file mode 100644
index 0000000..297f629
--- /dev/null
+++ b/ext/rsvg/gstrsvgoverlay.c
@@ -0,0 +1,301 @@
+/* GStreamer
+ * Copyright (C) 2010 Olivier Aubert <olivier.aubert@liris.cnrs.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.
+ */
+
+/**
+ * SECTION:element-rsvg_overlay
+ *
+ * Overlay SVG graphics over the video.
+ *
+ * <refsect2>
+ * <title>Example launch line</title>
+ * |[
+ * gst-launch -v videotestsrc ! video/x-raw-rgb,bpp=32,depth=32 ! rsvgoverlay filename=foo.svg ! ximagesink
+ * ]|
+ * </refsect2>
+ */
+
+#ifdef HAVE_CONFIG_H
+#  include "config.h"
+#endif
+
+#include "gstrsvgoverlay.h"
+#include <string.h>
+
+#include <gst/video/video.h>
+
+#include <librsvg/rsvg.h>
+#include <librsvg/rsvg-cairo.h>
+
+enum
+{
+  PROP_0,
+  PROP_DATA,
+  PROP_FILENAME,
+  PROP_FIT_TO_FRAME,
+  PROP_X,
+  PROP_Y,
+};
+
+#define GST_RSVG_LOCK(overlay) G_STMT_START { \
+  GST_LOG_OBJECT (overlay, "Locking rsvgoverlay from thread %p", g_thread_self ()); \
+  g_static_mutex_lock (&overlay->lock); \
+  GST_LOG_OBJECT (overlay, "Locked rsvgoverlay from thread %p", g_thread_self ()); \
+} G_STMT_END
+
+#define GST_RSVG_UNLOCK(overlay) G_STMT_START { \
+  GST_LOG_OBJECT (overlay, "Unlocking rsvgoverlay from thread %p", g_thread_self ()); \
+  g_static_mutex_unlock (&overlay->lock); \
+} G_STMT_END
+
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+#define GST_RSVG_VIDEO_CAPS GST_VIDEO_CAPS_BGRA
+#else
+#define GST_RSVG_VIDEO_CAPS GST_VIDEO_CAPS_ARGB
+#endif
+
+static GstStaticPadTemplate src_template = GST_STATIC_PAD_TEMPLATE ("src",
+    GST_PAD_SRC,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_RSVG_VIDEO_CAPS)
+    );
+
+static GstStaticPadTemplate sink_template = GST_STATIC_PAD_TEMPLATE ("sink",
+    GST_PAD_SINK,
+    GST_PAD_ALWAYS,
+    GST_STATIC_CAPS (GST_RSVG_VIDEO_CAPS)
+    );
+
+GST_BOILERPLATE (GstRsvgOverlay, gst_rsvg_overlay, GstVideoFilter,
+    GST_TYPE_VIDEO_FILTER);
+
+static void
+gst_rsvg_overlay_set_property (GObject * object, guint prop_id,
+    const GValue * value, GParamSpec * pspec)
+{
+  GstRsvgOverlay *overlay = GST_RSVG_OVERLAY (object);
+  GstBaseTransform *btrans = GST_BASE_TRANSFORM (object);
+
+  GST_RSVG_LOCK (overlay);
+
+  switch (prop_id) {
+    case PROP_DATA:
+    case PROP_FILENAME:
+    {
+      const gchar *data = g_value_get_string (value);
+      gsize size;
+      GError *error = NULL;
+
+      if (overlay->handle) {
+        g_object_unref (overlay->handle);
+        overlay->handle = NULL;
+        gst_base_transform_set_passthrough (btrans, TRUE);
+      }
+
+      /* data may be NULL */
+      if (data) {
+        size = strlen (data);
+        if (size) {
+          /* Read data either from string or from file */
+          if (prop_id == PROP_DATA)
+            overlay->handle =
+                rsvg_handle_new_from_data ((guint8 *) data, size, &error);
+          else
+            overlay->handle = rsvg_handle_new_from_file (data, &error);
+          if (error) {
+            GST_ERROR_OBJECT (overlay, "Cannot read SVG data: %s",
+                error->message);
+            g_error_free (error);
+          } else {
+            /* Get SVG dimension. */
+            RsvgDimensionData svg_dimension;
+            rsvg_handle_get_dimensions (overlay->handle, &svg_dimension);
+            overlay->width = svg_dimension.width;
+            overlay->height = svg_dimension.height;
+            gst_base_transform_set_passthrough (btrans, FALSE);
+          }
+        }
+      }
+      break;
+    }
+    case PROP_FIT_TO_FRAME:
+    {
+      overlay->fit_to_frame = g_value_get_boolean (value);
+      break;
+    }
+    case PROP_X:
+    {
+      overlay->x_offset = g_value_get_int (value);
+      break;
+    }
+    case PROP_Y:
+    {
+      overlay->y_offset = g_value_get_int (value);
+      break;
+    }
+
+    default:
+    {
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+    }
+  }
+
+  GST_RSVG_UNLOCK (overlay);
+}
+
+static void
+gst_rsvg_overlay_get_property (GObject * object, guint prop_id, GValue * value,
+    GParamSpec * pspec)
+{
+  GstRsvgOverlay *overlay = GST_RSVG_OVERLAY (object);
+
+  switch (prop_id) {
+    case PROP_X:
+      g_value_set_uint (value, overlay->x_offset);
+      break;
+    case PROP_Y:
+      g_value_set_uint (value, overlay->y_offset);
+      break;
+    case PROP_FIT_TO_FRAME:
+      g_value_set_boolean (value, overlay->fit_to_frame);
+      break;
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+      break;
+  }
+}
+
+static gboolean
+gst_rsvg_overlay_set_caps (GstBaseTransform * btrans, GstCaps * incaps,
+    GstCaps * outcaps)
+{
+  GstRsvgOverlay *overlay = GST_RSVG_OVERLAY (btrans);
+
+  return G_LIKELY (gst_video_format_parse_caps (incaps,
+          &overlay->caps_format, &overlay->caps_width, &overlay->caps_height));
+}
+
+static GstFlowReturn
+gst_rsvg_overlay_transform_ip (GstBaseTransform * btrans, GstBuffer * buf)
+{
+  GstRsvgOverlay *overlay = GST_RSVG_OVERLAY (btrans);
+  cairo_surface_t *surface;
+  cairo_t *cr;
+
+  GST_RSVG_LOCK (overlay);
+  if (!overlay->handle) {
+    GST_RSVG_UNLOCK (overlay);
+    return GST_FLOW_OK;
+  }
+
+  surface =
+      cairo_image_surface_create_for_data (GST_BUFFER_DATA (buf),
+      CAIRO_FORMAT_ARGB32, overlay->caps_width, overlay->caps_height,
+      overlay->caps_width * 4);
+  if (G_UNLIKELY (!surface))
+    return GST_FLOW_ERROR;
+
+  cr = cairo_create (surface);
+  if (G_UNLIKELY (!cr)) {
+    cairo_surface_destroy (surface);
+    return GST_FLOW_ERROR;
+  }
+
+  /* If x or y offset is specified, do not fit-to-frame. */
+  if (overlay->x_offset || overlay->y_offset)
+    cairo_translate (cr, (double) overlay->x_offset,
+        (double) overlay->y_offset);
+  else if (overlay->fit_to_frame && overlay->width && overlay->height)
+    cairo_scale (cr, (float) overlay->caps_width / overlay->width,
+        (float) overlay->caps_height / overlay->height);
+
+  rsvg_handle_render_cairo (overlay->handle, cr);
+  GST_RSVG_UNLOCK (overlay);
+
+  cairo_destroy (cr);
+  cairo_surface_destroy (surface);
+
+  return GST_FLOW_OK;
+}
+
+static gboolean
+gst_rsvg_overlay_stop (GstBaseTransform * btrans)
+{
+  GstRsvgOverlay *overlay = GST_RSVG_OVERLAY (btrans);
+
+  if (overlay->handle) {
+    g_object_unref (overlay->handle);
+    overlay->handle = NULL;
+  }
+
+  return TRUE;
+}
+
+static void
+gst_rsvg_overlay_base_init (gpointer klass)
+{
+  GstElementClass *element_class = GST_ELEMENT_CLASS (klass);
+
+  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));
+
+  gst_element_class_set_details_simple (element_class, "RSVG overlay",
+      "Filter/Editor/Video",
+      "Overlays SVG graphics over a video stream",
+      "Olivier Aubert <olivier.aubert@liris.cnrs.fr>");
+}
+
+static void
+gst_rsvg_overlay_class_init (GstRsvgOverlayClass * klass)
+{
+  GObjectClass *gobject_class = (GObjectClass *) klass;
+  GstBaseTransformClass *basetransform_class = GST_BASE_TRANSFORM_CLASS (klass);
+
+  gobject_class->set_property = gst_rsvg_overlay_set_property;
+  gobject_class->get_property = gst_rsvg_overlay_get_property;
+
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_DATA,
+      g_param_spec_string ("data", "data", "RSVG data.", "",
+          G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FILENAME,
+      g_param_spec_string ("filename", "filename", "SVG filename.", "",
+          G_PARAM_WRITABLE | G_PARAM_STATIC_STRINGS));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_FIT_TO_FRAME,
+      g_param_spec_boolean ("fit-to-frame", "fit to frame",
+          "Fit the SVG to fill the whole frame.", TRUE, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_X,
+      g_param_spec_int ("x", "x offset",
+          "Specify an x offset", 0, G_MAXINT, 0, G_PARAM_READWRITE));
+  g_object_class_install_property (G_OBJECT_CLASS (klass), PROP_Y,
+      g_param_spec_int ("y", "y offset",
+          "Specify a y offset", 0, G_MAXINT, 0, G_PARAM_READWRITE));
+
+  basetransform_class->set_caps = gst_rsvg_overlay_set_caps;
+  basetransform_class->transform_ip = gst_rsvg_overlay_transform_ip;
+  basetransform_class->stop = gst_rsvg_overlay_stop;
+  basetransform_class->passthrough_on_same_caps = FALSE;
+}
+
+static void
+gst_rsvg_overlay_init (GstRsvgOverlay * overlay, GstRsvgOverlayClass * klass)
+{
+  overlay->fit_to_frame = 1;
+}
diff --git a/ext/rsvg/gstrsvgoverlay.h b/ext/rsvg/gstrsvgoverlay.h
new file mode 100644
index 0000000..d38f285
--- /dev/null
+++ b/ext/rsvg/gstrsvgoverlay.h
@@ -0,0 +1,73 @@
+/* GStreamer
+ * Copyright (C) 2010 Olivier Aubert <olivier.aubert@liris.cnrs.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.
+ */
+
+#ifndef __GST_RSVG_OVERLAY_H__
+#define __GST_RSVG_OVERLAY_H__
+
+#include <librsvg/rsvg.h>
+#include <gst/gst.h>
+#include <gst/video/video.h>
+#include <gst/video/gstvideofilter.h>
+
+G_BEGIN_DECLS
+#define GST_TYPE_RSVG_OVERLAY 	     (gst_rsvg_overlay_get_type())
+#define GST_RSVG_OVERLAY(obj) 	     (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_RSVG_OVERLAY,GstRsvgOverlay))
+#define GST_RSVG_OVERLAY_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_RSVG_OVERLAY,GstRsvgOverlayClass))
+#define GST_IS_RSVG_OVERLAY(obj)         (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_RSVG_OVERLAY))
+#define GST_IS_RSVG_OVERLAY_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_RSVG_OVERLAY))
+typedef struct _GstRsvgOverlay GstRsvgOverlay;
+typedef struct _GstRsvgOverlayClass GstRsvgOverlayClass;
+
+/**
+ * GstRsvgOverlay:
+ *
+ * Opaque object data structure.
+ */
+struct _GstRsvgOverlay
+{
+  GstVideoFilter element;
+
+  /* < private > */
+
+  GStaticMutex lock;
+
+  RsvgHandle *handle;
+  /* width and height of the SVG data */
+  int width;
+  int height;
+  /* Should we fit the SVG to the dimension ? */
+  gboolean fit_to_frame;
+
+  int x_offset;
+  int y_offset;
+
+  GstVideoFormat caps_format;
+  int caps_width;
+  int caps_height;
+};
+
+struct _GstRsvgOverlayClass
+{
+  GstVideoFilterClass parent_class;
+};
+
+GType gst_rsvg_overlay_get_type (void);
+
+G_END_DECLS
+#endif /* __GST_RSVG_OVERLAY_H__ */
